1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2024-05-04

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

add_delete01.gif
こういうUI

add_delete02.gif
input要素が1つしかない場合は削除できないようにします。

HTML

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

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

の3点です。

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から削除

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

//イベントリスナーで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();をコメントアウトしてフォームを実行してみると、イベントが伝播して送信ボタンも押されてしまうのが分かります。

コードの末尾にある、

document.addEventListener("keypress", function (e) {
    if (e.key === "Enter") {
        e.preventDefault();
    }
});

も同様に、エンターキーが押された時にイベントが伝播して、勝手にinput要素が増えないようにするために必要です。

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

1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?