入力欄とテーブル行をボタンで増減する
ボタン操作で入力欄やテーブル行を増やしたり消したりする方法を、4段階で解説します。各デモと同じ動きを再現できるHTML・CSS・JavaScriptを掲載しています。
HTML・CSS・JSコードプレビューツールでも動作を確認できます。
このページで作るもの
① 基本の追加 — 要素を作って末尾へ足す
<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>
.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;
}
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 で親の末尾へ足します。label に input を入れてから親へ追加する順序がポイントです。appendChild するまで画面には出ません。
② 番号付きラベル — 追加のたびに番号を振る
<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>
.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;
}
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);
});
クリック時点の入力欄の個数を調べ、+1 した値をラベル番号に使います。欄が2つあれば次は「(3)」になります。消したあとに番号を振り直す処理は別途必要です。
③ 追加+削除 — ×ボタンで行ごと消す
<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="この行を消す">×</button>
</div>
</div>
.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;
}
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 |
<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="この行を消す">×</button>
</td>
</tr>
</tbody>
</table>
.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;
}
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());
});
行を足すときは thead を含む table 全体ではなく tbody に appendChild します。ヘッダーの下へ正しく並びます。セルは td を順に作って tr へ入れ、関数化しておくと行の形を揃えやすいです。
appendChild はどこへ追加されますか?
appendChild は親の末尾へ足します。途中の位置へ入れたい場合もありますが、入力欄の増減では末尾追加で十分なことが多いです。
番号付きラベルを消したあと番号が飛びます
length + 1 方式は「いま何個あるか」だけを見ます。途中の行を消すと番号は飛びます。連番を保ちたい場合は、削除後に全ラベルを付け直す処理を追加してください。
テーブルと入力フォーム、どちらを使えばいいですか?
単純な連絡欄の増減は①〜③のフォーム向きです。列が複数ある一覧データの編集は④のテーブル向きです。テーブル自体のマークアップはテーブル解説も参照してください。
当サイトで公開しているWebデザインやUIの実装例は、一覧として以下記事に纏めています。

