入力フォームを増減する作り方|追加と削除

入力欄とテーブル行をボタンで増減する

ボタン操作で入力欄やテーブル行を増やしたり消したりする方法を、4段階で解説します。各デモと同じ動きを再現できるHTML・CSS・JavaScriptを掲載しています。
HTML・CSS・JSコードプレビューツールでも動作を確認できます。

① 基本の追加 — 要素を作って末尾へ足す

HTML
<button class="dyn-basic-button" type="button">入力欄を増やす</button>
<div class="dyn-basic-area">
  <label class="dyn-lbl">連絡事項:<input type="text" class="dyn-inp"></label>
</div>
CSS
.dyn-basic-button {
  padding: 8px 20px;
  border: none;
  border-radius: 8px;
  background: #4f46e5;
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.dyn-basic-area {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}

.dyn-lbl {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #334155;
}

.dyn-inp {
  flex: 1;
  min-width: 0;
  padding: 6px 10px;
  border: 1px solid #cbd5e1;
  border-radius: 6px;
  font-size: 14px;
}
JS
var basicButton = document.querySelector('.dyn-basic-button');
var basicArea = document.querySelector('.dyn-basic-area');

basicButton.addEventListener('click', function() {
  var newLbl = document.createElement('label');
  newLbl.className = 'dyn-lbl';
  newLbl.textContent = '連絡事項:';

  var newInp = document.createElement('input');
  newInp.type = 'text';
  newInp.className = 'dyn-inp';

  newLbl.appendChild(newInp);
  basicArea.appendChild(newLbl);
});
createElement() → appendChild()

createElement で要素をメモリ上に作り、appendChild で親の末尾へ足します。label に input を入れてから親へ追加する順序がポイントです。appendChild するまで画面には出ません。

② 番号付きラベル — 追加のたびに番号を振る

HTML
<button class="dyn-num-button" type="button">入力欄を増やす</button>
<div class="dyn-num-area">
  <label class="dyn-lbl">連絡事項(1):<input type="text" class="dyn-inp"></label>
</div>
CSS
.dyn-num-button {
  padding: 8px 20px;
  border: none;
  border-radius: 8px;
  background: #4f46e5;
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.dyn-num-area {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}

.dyn-lbl {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #334155;
}

.dyn-inp {
  flex: 1;
  min-width: 0;
  padding: 6px 10px;
  border: 1px solid #cbd5e1;
  border-radius: 6px;
  font-size: 14px;
}
JS
var numButton = document.querySelector('.dyn-num-button');
var numArea = document.querySelector('.dyn-num-area');

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

  var newInp = document.createElement('input');
  newInp.type = 'text';
  newInp.className = 'dyn-inp';

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

  newLbl.appendChild(newInp);
  numArea.appendChild(newLbl);
});
querySelectorAll(‘input’).length + 1

クリック時点の入力欄の個数を調べ、+1 した値をラベル番号に使います。欄が2つあれば次は「(3)」になります。消したあとに番号を振り直す処理は別途必要です。

③ 追加+削除 — ×ボタンで行ごと消す

HTML
<button class="dyn-row-button" type="button">入力欄を増やす</button>
<div class="dyn-row-area">
  <div class="dyn-row">
    <label class="dyn-lbl">連絡事項:<input type="text" class="dyn-inp"></label>
    <button class="dyn-close" type="button" aria-label="この行を消す">&times;</button>
  </div>
</div>
CSS
.dyn-row-button {
  padding: 8px 20px;
  border: none;
  border-radius: 8px;
  background: #4f46e5;
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.dyn-row-area {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}

.dyn-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.dyn-lbl {
  display: flex;
  flex: 1;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: #334155;
}

.dyn-inp {
  flex: 1;
  min-width: 0;
  padding: 6px 10px;
  border: 1px solid #cbd5e1;
  border-radius: 6px;
  font-size: 14px;
}

.dyn-close {
  padding: 2px 8px;
  border: 1px solid #e2e8f0;
  border-radius: 6px;
  background: #fff;
  color: #94a3b8;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

.dyn-close:hover {
  color: #e53e3e;
  background: #fff5f5;
  border-color: #fecaca;
}
JS
var rowButton = document.querySelector('.dyn-row-button');
var rowArea = document.querySelector('.dyn-row-area');

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

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

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

  var closeBtn = document.createElement('button');
  closeBtn.type = 'button';
  closeBtn.className = 'dyn-close';
  closeBtn.setAttribute('aria-label', 'この行を消す');
  closeBtn.textContent = '\u00D7';

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

rowArea.addEventListener('click', function(ev) {
  var closeBtn = ev.target.closest('.dyn-close');
  if (!closeBtn) return;
  var wrap = closeBtn.closest('.dyn-row');
  if (wrap && wrap.parentNode) {
    wrap.parentNode.removeChild(wrap);
  }
});

rowButton.addEventListener('click', function() {
  rowArea.appendChild(makeFormRow());
});
親でまとめてクリックを受け取る

消すボタンごとに個別登録する代わりに、親要素で click を受け取り closest で行を特定します。後から追加した行にも同じ処理が効きます。行の削除は parentNode.removeChild(行) で行います。

④ テーブル行の増減 — tbody に追加する

名前 備考
山田 太郎 サンプルデータ A
鈴木 花子 サンプルデータ B
HTML
<button class="dyn-tbl-button" type="button">行を増やす</button>
<table class="dyn-tbl">
  <thead>
    <tr>
      <th>名前</th>
      <th>備考</th>
      <th class="dyn-close-col"></th>
    </tr>
  </thead>
  <tbody class="dyn-tbl-body">
    <tr>
      <td>山田 太郎</td>
      <td>サンプルデータ A</td>
      <td class="dyn-close-col">
        <button class="dyn-tbl-close" type="button" aria-label="この行を消す">&times;</button>
      </td>
    </tr>
  </tbody>
</table>
CSS
.dyn-tbl-button {
  padding: 8px 20px;
  border: none;
  border-radius: 8px;
  background: #4f46e5;
  color: #fff;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}

.dyn-tbl {
  width: 100%;
  margin-top: 12px;
  border-collapse: collapse;
  font-size: 14px;
}

.dyn-tbl th,
.dyn-tbl td {
  border: 1px solid #e2e8f0;
  padding: 8px 12px;
  text-align: left;
}

.dyn-tbl thead {
  background: #4f46e5;
  color: #fff;
}

.dyn-tbl tbody tr:nth-child(even) {
  background: #f8fafc;
}

.dyn-close-col {
  width: 44px;
  text-align: center;
}

.dyn-tbl-close {
  padding: 2px 8px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: #94a3b8;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}

.dyn-tbl-close:hover {
  color: #e53e3e;
  background: #fff5f5;
  border-color: #fecaca;
}
JS
var tblButton = document.querySelector('.dyn-tbl-button');
var tblBody = document.querySelector('.dyn-tbl-body');

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

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

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

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

  var closeBtn = document.createElement('button');
  closeBtn.type = 'button';
  closeBtn.className = 'dyn-tbl-close';
  closeBtn.setAttribute('aria-label', 'この行を消す');
  closeBtn.textContent = '\u00D7';

  tdClose.appendChild(closeBtn);
  newTr.appendChild(tdName);
  newTr.appendChild(tdNote);
  newTr.appendChild(tdClose);
  return newTr;
}

tblBody.addEventListener('click', function(ev) {
  var closeBtn = ev.target.closest('.dyn-tbl-close');
  if (!closeBtn) return;
  var row = closeBtn.closest('tr');
  if (row && row.parentNode) {
    row.parentNode.removeChild(row);
  }
});

tblButton.addEventListener('click', function() {
  tblBody.appendChild(makeTableRow());
});
追加先は table ではなく tbody

行を足すときは thead を含む table 全体ではなく tbody に appendChild します。ヘッダーの下へ正しく並びます。セルは td を順に作って tr へ入れ、関数化しておくと行の形を揃えやすいです。

注意点:動的に増やした入力欄は、そのままでは「何件目か」「必須か」といった検証ルールが自動では付きません。送信前に querySelectorAll で欄を集めてチェックする処理を別途用意してください。また、消した行の番号を詰め直したい場合は、削除後にラベルを付け直す処理が必要です。

appendChild はどこへ追加されますか?

appendChild は親の末尾へ足します。途中の位置へ入れたい場合もありますが、入力欄の増減では末尾追加で十分なことが多いです。

消すボタンを後から足しても動きますか?

親にイベントを任せる書き方(イベント委譲)なら動きます。はその方式です。追加のたびに個別登録する必要がありません。

番号付きラベルを消したあと番号が飛びます

length + 1 方式は「いま何個あるか」だけを見ます。途中の行を消すと番号は飛びます。連番を保ちたい場合は、削除後に全ラベルを付け直す処理を追加してください。

テーブルと入力フォーム、どちらを使えばいいですか?

単純な連絡欄の増減は①〜③のフォーム向きです。列が複数ある一覧データの編集は④のテーブル向きです。テーブル自体のマークアップはテーブル解説も参照してください。


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

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