入力フォームを増減する実装解説|Dynamic Form Fields JS Guide

入力フォームを増減 デザイン見本

ボタンクリックで入力フォームを動的に追加・削除する実装解説。createElement() + appendChild() / removeChild() を使ったコピペ用コードを掲載。

① 基本の追加 — createElement() と appendChild()

HTML
<button id="add-btn">入力欄を増やす</button>
<div id="form-area">
  <label>連絡事項:<input type="text"></label>
</div>
CSS
#form-area {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}
label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
}
input[type="text"] {
  padding: 6px 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
JS
var addBtn = document.getElementById('add-btn');
var formArea = document.getElementById('form-area');

addBtn.addEventListener('click', function() {
  var newLbl = document.createElement('label');
  newLbl.textContent = '連絡事項:';

  var newInp = document.createElement('input');
  newInp.type = 'text';

  newLbl.appendChild(newInp);
  formArea.appendChild(newLbl);
});
createElement() + appendChild()

document.createElement() で新しいHTML要素をメモリ上に生成し、appendChild() で親要素の末尾に追加します。label 要素に input を入れてから div に追加する順序が重要です。appendChild() を呼ぶまで要素はページに表示されません。

② 番号付きラベル — 追加するたびに番号が増える

HTML
<button id="add-btn">入力欄を増やす</button>
<div id="form-area">
  <label>連絡事項(1):<input type="text"></label>
</div>
CSS
#form-area {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}
label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
}
input[type="text"] {
  flex: 1;
  padding: 6px 10px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
JS
var addBtn = document.getElementById('add-btn');
var formArea = document.getElementById('form-area');

addBtn.addEventListener('click', function() {
  var formNum = formArea.querySelectorAll('input').length + 1;

  var newInp = document.createElement('input');
  newInp.type = 'text';

  var newLbl = document.createElement('label');
  newLbl.textContent = '連絡事項(' + formNum + '):';

  newLbl.appendChild(newInp);
  formArea.appendChild(newLbl);
});
querySelectorAll().length で番号を取得

クリック時点の input 要素数を querySelectorAll(‘input’).length で取得し、+1した値をラベルの番号に使います。変数 formNum には入力欄の現在数 + 1 の値が入るため、既存の入力欄が2つあれば「(3)」が付いた新しい欄が生成されます。

③ 追加+削除 — ×ボタンで個別に削除

×
×
HTML
<button id="add-btn">入力欄を増やす</button>
<div id="form-area">
  <div class="form-row">
    <label>連絡事項:<input type="text"></label>
    <span class="close-btn">&times;</span>
  </div>
</div>
CSS
.form-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.close-btn {
  cursor: pointer;
  font-size: 18px;
  color: #999;
  padding: 2px 6px;
  border-radius: 3px;
  transition: color 0.2s, background 0.2s;
}
.close-btn:hover {
  color: #e53e3e;
  background: #fff5f5;
}
JS
var addBtn = document.getElementById('add-btn');
var formArea = document.getElementById('form-area');

function makeRow() {
  var wrap = document.createElement('div');
  wrap.className = 'form-row';

  var lbl = document.createElement('label');
  lbl.textContent = '連絡事項:';

  var inp = document.createElement('input');
  inp.type = 'text';
  lbl.appendChild(inp);

  var closeBtn = document.createElement('span');
  closeBtn.className = 'close-btn';
  closeBtn.textContent = '\u00D7';
  closeBtn.addEventListener('click', function() {
    wrap.parentNode.removeChild(wrap);
  });

  wrap.appendChild(lbl);
  wrap.appendChild(closeBtn);
  return wrap;
}

var existingRows = formArea.querySelectorAll('.form-row');
for (var i = 0; i < existingRows.length; i++) {
  (function(row) {
    var closeBtn = row.querySelector('.close-btn');
    if (closeBtn) {
      closeBtn.addEventListener('click', function() {
        row.parentNode.removeChild(row);
      });
    }
  })(existingRows[i]);
}

addBtn.addEventListener('click', function() {
  formArea.appendChild(makeRow());
});
parentNode.removeChild() による行削除

要素を削除するには element.parentNode.removeChild(element) を使います。makeRow() 関数内でクロージャを活用して生成した wrap 変数を参照し、そのまま削除します。既存の行には初期化ループで後からイベントを設定します。即時実行関数(IIFE)で変数 row をスコープに閉じ込めることで、ループ内のクロージャ問題を回避しています。

④ テーブル行の追加+削除 — tbody への行操作

名前 備考
山田 太郎 サンプルデータ A ×
鈴木 花子 サンプルデータ B ×
HTML
<button id="add-row-btn">行を増やす</button>
<table>
  <thead>
    <tr>
      <th>名前</th>
      <th>備考</th>
      <th class="close-col"></th>
    </tr>
  </thead>
  <tbody id="my-tbody">
    <tr>
      <td>山田 太郎</td>
      <td>サンプルデータ A</td>
      <td class="close-col"><span class="close-btn">&times;</span></td>
    </tr>
  </tbody>
</table>
CSS
table {
  border-collapse: collapse;
  width: 100%;
  margin-top: 12px;
}
th, td {
  border: 1px solid #e2e8f0;
  padding: 8px 12px;
  text-align: left;
  font-size: 14px;
}
thead {
  background: #4f46e5;
  color: #fff;
}
tbody tr:nth-child(even) {
  background: #f8f9fa;
}
.close-col {
  width: 40px;
  text-align: center;
  border: none;
}
.close-btn {
  cursor: pointer;
  font-size: 16px;
  color: #999;
  padding: 2px 6px;
  border-radius: 3px;
  transition: color 0.2s, background 0.2s;
}
.close-btn:hover {
  color: #e53e3e;
  background: #fff5f5;
}
JS
var addRowBtn = document.getElementById('add-row-btn');
var tbody = document.getElementById('my-tbody');

function makeTblRow() {
  var newTr = document.createElement('tr');

  var td1 = document.createElement('td');
  td1.textContent = '新規データ';

  var td2 = document.createElement('td');
  td2.textContent = '備考欄';

  var tdClose = document.createElement('td');
  tdClose.className = 'close-col';

  var closeBtn = document.createElement('span');
  closeBtn.className = 'close-btn';
  closeBtn.textContent = '\u00D7';
  closeBtn.addEventListener('click', function() {
    newTr.parentNode.removeChild(newTr);
  });

  tdClose.appendChild(closeBtn);
  newTr.appendChild(td1);
  newTr.appendChild(td2);
  newTr.appendChild(tdClose);
  return newTr;
}

var existingTrs = tbody.querySelectorAll('tr');
for (var i = 0; i < existingTrs.length; i++) {
  (function(tr) {
    var closeBtn = tr.querySelector('.close-btn');
    if (closeBtn) {
      closeBtn.addEventListener('click', function() {
        tr.parentNode.removeChild(tr);
      });
    }
  })(existingTrs[i]);
}

addRowBtn.addEventListener('click', function() {
  tbody.appendChild(makeTblRow());
});
tbody への行追加

テーブルに行を追加する際は thead を含む table 要素ではなく、tbody 要素に対して appendChild() します。こうするとヘッダー行の後ろに正しく追加されます。各セルは createElement(‘td’) で生成し tr に順番に追加します。makeTblRow() 関数でまとめて処理すると、行の構造を統一できます。


当サイトで公開しているWebデザインやUIの実装例は、一覧として以下記事に纏めています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次