【JavaScript】input要素を追加・削除するUI

WordPressのCustom Fieldのような、ユーザーが自分でinput要素を追加したり削除したりできるフォームのUIを、Vanilla JSで作る方法です。

![add_delete01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781928/babd5088-87af-e448-9d0c-e312b9080471.gif)
こういうUI

![add_delete02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781928/45f6ea40-37cf-e417-e20e-9c33c3bcad01.gif)
input要素が1つしかない場合は削除できないようにします。

## HTML
HTMLには一般的なフォームを設置。ポイントは、

- input要素はユーザーが追加・削除するので、全て`name=content[]`のように同じname属性を設定しておいて、POSTした先で配列として受け取り処理します。
- input要素とそれを削除するボタンをセットにして、`<tr class="inputField">`でマークアップ。
- JavaScript側でこのtr要素を追加・削除できるように、親要素のtable要素に`inputFields`クラスを付与しておく。

の3点です。

```html:index.html
<form method="POST" action="/">
    <table class="inputFields">
        <tr class="inputField">
            <th>
                <input type="text" name="content[]" placeholder="バリュー"></th>
            <td>
                <!--input要素を削除するボタン-->
                <button class="deleteFieldBtn">入力欄を削除</button></td>
        </tr>
    </table>
    <!--input要素を追加するボタン-->
    <button class="addFieldBtn">入力欄を追加</button>
    <input type="submit" name="submit" value="送信">
</form>
```

## JavaScript
イベントリスナーで、form上で起こる全てのclickイベントを監視し、
1. クリックされた要素に`addFieldBtn`クラスが含まれていた場合は、input要素をDOMに追加
2. クリックされた要素に`deleteFieldBtn`クラスが含まれていた場合、親要素ごとDOMから削除

という形で条件分岐します。

```JavaScript
//イベントリスナーでform上の全てのclickイベントを監視する
document.forms[0].addEventListener("click", function (e) {
    var inputFielsContainer = e.target.parentNode.querySelector(".inputFields");
    // クリックされた要素に、addFieldBtnが含まれていたら
    if (e.target.classList.contains("addFieldBtn")) {
        e.preventDefault();
        var field = document.createElement("tr");
        field.classList.add("inputField");
        field.innerHTML = `
        <th><input type="text" name="content[]" placeholder="バリュー"></th>
        <td><button class="deleteFieldBtn">入力欄を削除</button></td>
        `;
        var clone = field.cloneNode(true);
        inputFielsContainer.appendChild(clone);
    }

    // クリックされた要素に、deleteFieldBtnクラスが含まれていたら
    else if (e.target.classList.contains("deleteFieldBtn")) {
        e.preventDefault();
        var deleteFieldBtns = document.querySelectorAll(".deleteFieldBtn");
        //deleteFieldBtクラスを持つ要素がDOM上に1つしかない場合は削除できないようにする
        if(deleteFieldBtns.length == 1){
            alert("入力欄は最低1つ必要です");
            return;
        }else{
            var field = e.target.parentNode.parentNode;
            field.remove();
        }
    }
});

//エンターキー押下でinput要素が追加されないようにする
document.addEventListener("keypress", function (e) {
    if (e.key === "Enter") {
        e.preventDefault();
    }
});
```

`e.target.classList.contains("hoge")`で、クリックイベントが発生した要素が、`hoge`というクラスを持っているか否かを判別できます。要素を追加する場合は`innerHTML`で、削除する場合は`remove()`で親要素ごと削除します。

ここで重要なのが、各条件分岐の中の`e.preventDefault();`で、これは、クリックイベントが伝播して、入力欄の追加／削除ボタンが押された際に、フォームの送信ボタンも一緒にクリックされないようにするために必要です。
試しに条件分岐の中の2つの`e.preventDefault();`をコメントアウトしてフォームを実行してみると、イベントが伝播して送信ボタンも押されてしまうのが分かります。

コードの末尾にある、
```JavaScript
document.addEventListener("keypress", function (e) {
    if (e.key === "Enter") {
        e.preventDefault();
    }
});
```
も同様に、エンターキーが押された時にイベントが伝播して、勝手にinput要素が増えないようにするために必要です。

完成形のコードはこちら
https://codepen.io/outsider-kithy/details/OJGeKve