jQueryとjQuery Mobileでcloneしてみた

## はじめに
この記事の筆者はひよっこWebエンジニア（２年目）で、jQueryとJavaScriptはほぼ未経験です。
温かい目で見ていただけると幸いですm(_ _)m
コード例はPHPで動的に書いたものを説明しやすくするためにHTMLに書き直し、なおかつ読みやすいように調整したものですので、間違っているかもしれません。

## 背景
自社サービスにおいて、cloneを使う機会があったので、
また使う事になったときのためにまとめておくことにしました。

## 使った技術
- PHP
- JS
    - jQuery
    - jQuery Mobile
- CSS
    - jQuery
    - jQuery Mobile


## jQuery編
### コード例

```php5
<table>
    <tr id="like_fruits_1">
        <th>
            <label>好きなフルーツ</label>
            <input type="hidden" id="like_fruits_count" value="1">
        </th>
        <td>
            <select name="fruits[1]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </select>
            <button type="button" id="add_like_fruits_button" class="button button-pill button-primary" style="width: 100px;"><i class="fa fa-plus-square"></i> 追加</button>
            <button type="button" id="delete_like_fruits_button" class="button button-pill button-caution" style="width: 100px;"><i class="fa fa-trash"></i> 削除</button>
        </td>
    </tr>

    <tr id="clone_like_fruits_template" style="display: none">
        <th></th>
        <td>
            <select name="fruits[xxx]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </select>
        </td>
    </tr>
</table>

<script type=text/javascript>
    $(document).ready(function() {
        // 削除ボタンを無効化する
        $('#delete_like_fruits_button').prop('disabled', true);
    });

    // 追加ボタンをクリックしたときの処理
    $(document).on('click', '#add_like_fruits_button', function() {
        // 追加ボタンが押下されたら削除ボタンを有効化する
        $('#delete_like_fruits_button').prop('disabled', false);

        // 今ある要素数を取得
        let creation_times = +$('#like_fruits_count').val();

        // 追加したあとの要素数を保存
        $('#like_fruits_count').val(creation_times + 1);

        // clone_like_fruits_templateのクローンを作成
        let copy = $('#clone_like_fruits_template').clone(true);

        // xxxを置換
        copy.html(function(i, oldHTML) {
            return oldHTML.replace(/xxx/g, creation_times+1)
        });

        // styleを外す。
        copy.prop('style', false);

        // 属性を付加する
        copy.attr('id', 'like_fruits_'+(creation_times+1));

        // 現要素の一番最後に格納
        copy.insertAfter('#like_fruits_'+creation_times);
    });

    // 削除ボタンをクリックしたときの処理
    $(document).on('click', '#delete_like_fruits_button', function() {
        // 今ある要素数を取得
        let creation_times = +$('#like_fruits_count').val();

        // 削除した後の個数を保存する
        $('#like_fruits_count').val(creation_times - 1);

        // 項目の一番最後の行を削除する
        $('#like_fruits_'+creation_times).remove();

        // 一番最初の要素は消せないように削除ボタンを無効化する
        if (creation_times <= 2) {
            $('#delete_like_fruits_button').prop('disabled', true);
        }
    });
</script>
```

で定義して、「追加」を押すと、

```php5
<table>
    <tr id="like_fruits_1">
        <th>
            <label>好きなフルーツ</label>
            <input type="hidden" id="like_fruits_count" value="2">
        </th>
        <td>
            <select name="fruits[1]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </select>
            <button type="button" id="add_like_fruits_button" class="button button-pill button-primary" style="width: 100px;"><i class="fa fa-plus-square"></i> 追加</button>
            <button type="button" id="delete_like_fruits_button" class="button button-pill button-caution" style="width: 100px;"><i class="fa fa-trash"></i> 削除</button>
        </td>
    </tr>

    <tr id="like_fruits_2">
        <th></th>
        <td>
            <select name="fruits[2]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </select>
        </td>
    </tr>

    <tr id="clone_like_fruits_template" style="display: none">
        <th></th>
        <td>
            <select name="fruits[xxx]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </select>
        </td>
    </tr>
</table>

<script type=text/javascript>
    $(document).ready(function() {
        // 削除ボタンを無効化する
        $('#delete_like_fruits_button').prop('disabled', true);
    });

    // 追加ボタンをクリックしたときの処理
    $(document).on('click', '#add_like_fruits_button', function() {
        // 追加ボタンが押下されたら削除ボタンを有効化する
        $('#delete_like_fruits_button').prop('disabled', false);

        // 今ある要素数を取得
        let creation_times = +$('#like_fruits_count').val();

        // 追加したあとの要素数を保存
        $('#like_fruits_count').val(creation_times + 1);

        // clone_like_fruits_templateのクローンを作成
        let copy = $('#clone_like_fruits_template').clone(true);

        // xxxを置換
        copy.html(function(i, oldHTML) {
            return oldHTML.replace(/xxx/g, creation_times+1)
        });

        // styleを外す。
        copy.prop('style', false);

        // 属性を付加する
        copy.attr('id', 'like_fruits_'+(creation_times+1));

        // 一番最後に格納
        copy.insertAfter('#like_fruits_'+creation_times);
    });

    // 削除ボタンをクリックしたときの処理
    $(document).on('click', '#delete_like_fruits_button', function() {
        // 今ある要素数を取得
        let creation_times = +$('#like_fruits_count').val();

        // 削除した後の個数を保存する
        $('#like_fruits_count').val(creation_times - 1);

        // 一番最後の行を削除する
        $('#like_fruits_'+creation_times).remove();

        // 一番最初は消せないように削除ボタンを無効化する
        if (creation_times <= 2) {
            $('#delete_like_fruits_button').prop('disabled', true);
        }
    });
</script>
```


みたいな感じに展開されるように作った。
jQueryは初心者でも結構あっさり実装できた。


## jQuery Mobile編
### コード例
```php5
<table>
    <tr id="like_fruits_1">
        <th>
            <label>好きなフルーツ</label>
            <input type="hidden" id="like_fruits_count" value="1">
        </th>
        <td>
            <select name="fruits[1]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </select>
            <button type="button" id="add_like_fruits_button" class="button button-pill button-primary" style="width: 100px;"><i class="fa fa-plus-square"></i> 追加</button>
            <button type="button" id="delete_like_fruits_button" class="button button-pill button-caution" style="width: 100px;"><i class="fa fa-trash"></i> 削除</button>
        </td>
    </tr>

    <tr id="clone_like_fruits_template" style="display: none">
        <th></th>
        <td>
            <my-select name="fruits[xxx]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </my-select>
        </td>
    </tr>
</table>

<script type=text/javascript>
    $(document).ready(function() {
        // 削除ボタンを無効化する
        $('#delete_like_fruits_button').attr('class', 'button button-pill button-caution button-large ui-btn-inline ui-shadow ui-link disabled');
    });

    // 追加ボタンをクリックしたときの処理
    $(document).on('click', '#add_like_fruits_button', function() {
        // 追加ボタンが押下されたら削除ボタンを有効化する
        $('#delete_like_fruits_button').attr('class', 'button button-pill button-caution button-large ui-btn-inline ui-shadow ui-link');

        // 今ある要素数を取得
        let creation_times = +$('#like_fruits_count').val();

        // 追加したあとの要素数を保存
        $('#like_fruits_count').val(creation_times + 1);

        // clone_like_fruits_templateのクローンを作成
        let copy = $('#clone_like_fruits_template').clone(true);

        // xxxを置換。my-selectを置換
        copy.html(function(i, oldHTML) {
            return oldHTML.replace(/xxx/g, creation_times+1).replace(/my-select/g, 'select')
        });

        // styleを外す。
        copy.prop('style', false);

        // 属性を付加する
        copy.attr('id', 'like_fruits_'+(creation_times+1));

        // 一番最後に格納
        copy.insertAfter('#like_fruits_'+creation_times).trigger('create');
    });

    // 削除ボタンをクリックしたときの処理
    $(document).on('click', '#delete_like_fruits_button', function() {
        // 今ある要素数を取得
        let creation_times = +$('#like_fruits_count').val();

        // 一番最初の要素しか無いときは何も処理をさせない
        if (creation_times === 1) {
            return;
        }

        // 削除した後の個数を保存する
        $('#like_fruits_count').val(creation_times - 1);

        // 一番最後の行を削除する
        $('#like_fruits_'+creation_times).remove();

        // 一番最初は消せないように削除ボタンを無効化する
        if (creation_times <= 2) {
            $('#delete_like_fruits_button').attr('class', 'button button-pill button-caution button-large ui-btn-inline ui-shadow ui-link disabled');
            return;
        }
    });
</script>
```

で定義して、「追加」を押すと、

```php5
<table>
    <tr id="like_fruits_1">
        <th>
            <label>好きなフルーツ</label>
            <input type="hidden" id="like_fruits_count" value="2">
        </th>
        <td>
            <select name="fruits[1]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </select>
            <button type="button" id="add_like_fruits_button" class="button button-pill button-primary" style="width: 100px;"><i class="fa fa-plus-square"></i> 追加</button>
            <button type="button" id="delete_like_fruits_button" class="button button-pill button-caution" style="width: 100px;"><i class="fa fa-trash"></i> 削除</button>
        </td>
    </tr>

    <tr id="like_fruits_2">
        <th></th>
        <td>
            <select name="fruits[2]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </select>
        </td>
    </tr>

    <tr id="clone_like_fruits_template" style="display: none">
        <th></th>
        <td>
            <my-select name="fruits[xxx]" class="select">
                <option value="">--</option>
                <option value="apple">りんご</option>
                <option value="orange">オレンジ</option>
                <option value="water_melon">スイカ</option>
                <option value="melon">メロン</option>
            </my-select>
        </td>
    </tr>
</table>

<script type=text/javascript>
    $(document).ready(function() {
        // 削除ボタンを無効化する
        $('#delete_like_fruits_button').attr('class', 'button button-pill button-caution button-large ui-btn-inline ui-shadow ui-link disabled');
    });

    // 追加ボタンをクリックしたときの処理
    $(document).on('click', '#add_like_fruits_button', function() {
        // 追加ボタンが押下されたら削除ボタンを有効化する
        $('#delete_like_fruits_button').attr('class', 'button button-pill button-caution button-large ui-btn-inline ui-shadow ui-link');

        // 今ある要素数を取得
        let creation_times = +$('#like_fruits_count').val();

        // 追加したあとの要素数を保存
        $('#like_fruits_count').val(creation_times + 1);

        // clone_like_fruits_templateのクローンを作成
        let copy = $('#clone_like_fruits_template').clone(true);

        // xxxを置換。my-selectを置換
        copy.html(function(i, oldHTML) {
            return oldHTML.replace(/xxx/g, creation_times+1).replace(/my-select/g, 'select')
        });

        // styleを外す。
        copy.prop('style', false);

        // 属性を付加する
        copy.attr('id', 'like_fruits_'+(creation_times+1));

        // 一番最後に格納
        copy.insertAfter('#like_fruits_'+creation_times).trigger('create');
    });

    // 削除ボタンをクリックしたときの処理
    $(document).on('click', '#delete_like_fruits_button', function() {
        // 今ある要素数を取得
        let creation_times = +$('#like_fruits_count').val();

        // 一番最初の要素しか無いときは何も処理をさせない
        if (creation_times === 1) {
            return;
        }

        // 削除した後の個数を保存する
        $('#like_fruits_count').val(creation_times - 1);

        // 一番最後の行を削除する
        $('#like_fruits_'+creation_times).remove();

        // 一番最初は消せないように削除ボタンを無効化する
        if (creation_times <= 2) {
            $('#delete_like_fruits_button').attr('class', 'button button-pill button-caution button-large ui-btn-inline ui-shadow ui-link disabled');
            return;
        }
    });
</script>
```

これでほぼjQueryと同じ感じに展開された。


## 名前は似ているが、実は結構違った「jQuery」と「jQuery Mobile」
「jQuery Mobile」は「Mobile」とつくだけだから、「jQuery」で書いたコードがそのまま動くでしょ。なんて軽く思っていたら違う部分が結構あった。
jQuery Mobileでは、cloneで追加されたセレクトボックスを選択しても、画面上に反映されなかった。
これの解消に２日くらい時間を要した。

理由は、DOMとしてレンダリングされたとき、すでに「jQuery Mobile」によって```select```が```ui-select```に変換されていたからだった。
今回は対処法としてDOMの段階では```my-select```みたいな「jQuery Mobile」に勝手に変換されないようにして解決させた。

さすがに「Java」と「JavaScript」のように全く違う言語とまではいかないけど、それでも結構違った。


## 最後に、今回作成した成果物をご紹介
### jQuery
追加ボタンを押す前
![スクリーンショット 2020-12-12 20.38.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/877917/ec18f0d9-2a44-f812-12c8-be3fd5dd36b3.png)


追加ボタンを押した後
![スクリーンショット 2020-12-12 20.38.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/877917/937a5166-a4b1-ef79-dde1-b97802e57c68.png)

### jQuery Mobile
追加ボタンを押す前
![スクリーンショット 2020-12-12 20.39.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/877917/f9892594-181b-85d3-f9c7-3a1ad1c24b3f.png)

追加ボタンを押した後
![スクリーンショット 2020-12-12 20.39.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/877917/75013b97-9ab5-f9e5-68e7-ada770335079.png)