なぜ動的フォームが便利なのか?
アンケートやチェックリストのように、ユーザーが入力する項目数が決まっていないケースでは、フォーム項目を自由に追加・削除できると非常に便利です。
例えば、好きな食べ物を自由にいくつでも入力させたいときや、メールアドレスを家族分登録してもらいたいときなど、固定されたフォームでは対応しきれません。
今回ご紹介するのは、純粋なJavaScript(ライブラリ不要)で、フォーム項目を動的に追加・削除できるシンプルな実装例です。
HTML構造と初期表示の仕組み
基本となる HTML は以下のとおりです。
フォームを <form> タグで囲み、その外に「追加」ボタンを設置します。
このあと解説するJavaScriptによって、window.onload イベントで自動的に1つ目の入力欄が作成されます。
<div class="form-wrapper">
<form id="form">
</form>
<div class="add-btn-area">
<button class="add-btn" onclick="add_form_element();">追加</button>
</div>
</div>
ページ読み込み時にフォーム項目を自動生成
window.onload イベントで add_form_element() を呼び出すことで、ページロード時に最初の入力欄が表示されます。
add_form_element() の処理詳細は次で説明を行います。
window.onload = function () {
add_form_element();
};
add_form_element() の処理詳細
「追加」ボタンが押されると次の流れで要素が生成されます:
- 「削除ボタン」「追加ボタン」の有効制御を更新
- 現在のフォーム項目数を取得し、番号を決定
- ラベル(例: No 1, No 2…)を生成
- テキスト入力欄(
<input type="text">)を生成 - 削除ボタンを生成
これらをまとめて <div> 要素に挿入し、フォーム本体へ追加し、削除ボタン・追加ボタンの有効/無効の設定を行います。
/**
* フォームの追加
*/
function add_form_element() {
// フォーム内の要素の数
var formarea = document.querySelector('#form');
var num = 0;
if (formarea !== null) {
num = formarea.childElementCount;
}
num++;
// ラベルの作成
var label = create_label(num);
// 入力欄の作成
var text_form = create_text_from(num);
// 削除ボタンの作成
var del_btn = create_delete_btn(num);
// ラベル・入力欄・削除ボタンをdiv要素に追加
var form_area = document.createElement('div');
form_area.setAttribute('id', 'form_area_' + num);
form_area.appendChild(label);
form_area.appendChild(text_form);
form_area.appendChild(del_btn);
// フォームに要素を追加
var form = document.getElementById('form');
form.appendChild(form_area);
// 削除ボタンの有効無効
set_delete_btn_disabled();
// 追加ボタンの有効無効
set_add_btn_disabled();
};
ラベルの生成
今回のサンプルでは、「No1」「No2」など番号付きのラベルを付けていますが、フォームを追加する際、ユーザーに分かりやすくするために、入力欄だけではなくラベル等、他の要素も一緒に追加をすると思います。
createElementで、labelタグを追加できます。
/**
* ラベル
*/
function create_label(num) {
var label = document.createElement('label');
var label_txt = document.createTextNode('No' + num);
label.appendChild(label_txt);
return label;
}
入力欄の生成:識別可能な id と name
入力欄には id と name 属性を設定します。
これにより、JavaScriptから値を取得しやすく、フォーム送信時にもサーバー側で正しく処理できます。
createElementで、inputタグを追加できます。setAttributeで、type属性とid属性とname属性の追加ができます。
/**
* 入力欄
*/
function create_text_from(num) {
var text_form = document.createElement('input');
text_form.setAttribute('type', 'text');
text_form.setAttribute('id', 'form_' + num);
text_form.setAttribute('name', 'form_' + num);
return text_form;
}
削除ボタンの生成:タイプ指定と動的バインド
type="button" を指定しないと、クリックでフォームが送信されてしまうことがあります。
さらに、onclick 属性で行ごとに削除対象を指定し、動的バインドしています。
createElementで、buttonタグを追加できます。setAttributeで、class属性とtype属性とonclick属性の追加ができます。
/**
* 削除ボタン
*/
function create_delete_btn(num) {
var btn = document.createElement('button');
var btn_txt = document.createTextNode('削除');
btn.appendChild(btn_txt);
btn.setAttribute('class', 'del_btn');
btn.setAttribute('type', 'button');
btn.setAttribute('onclick', 'delete_form_element("form_area_' + num + '");');
return btn;
}
delete_form_element() :削除&番号リセット
削除ボタンを押すと実行されるのが delete_form_element() です。
削除処理では以下を実行します:
- 該当フォーム行の削除
- 残ったフォーム行の id/ラベル/入力欄 ID・name/削除ボタンの再生成
- ボタン有効制御を再設定
/**
* フォームの削除
*/
function delete_form_element(name) {
// 対象フォームの削除
var elem = document.getElementById(name);
elem.remove();
// 残っているフォームのラベル・IDの番号の振りなおしと削除ボタンの作り直し
var forms = document.querySelector('#form').children;
// 連番の振りなおし
for (i = 0; i < forms.length; i++) {
// フォームのIDの番号の付け直し
forms[i].id = 'form_area_' + (i + 1);
// ラベルの番号の付け直し
forms[i].children[0].innerText = "No" + (i + 1);
// 入力欄のIDの番号の付け直し
forms[i].children[1].id = 'form_' + (i + 1);
forms[i].children[1].name = 'form_' + (i + 1);
// 削除ボタンは作り直し
forms[i].children[2].remove();
var btn = create_delete_btn(i + 1);
forms[i].appendChild(btn);
}
// 削除ボタンの有効無効
set_delete_btn_disabled();
// 追加ボタンの有効無効
set_add_btn_disabled();
};
「追加」「削除」ボタンのON/OFF制御
今回のサンプルでは、最大5件までフォームを追加できるように制限をかけています。
- 追加ボタン:最大 5 件までとし、それ以上は無効化
- 削除ボタン:項目が 1 件のみなら無効化、それ以外は有効化
/**
* 追加ボタンの有効無効の設定
*/
function set_add_btn_disabled() {
var form = document.getElementById('form');
var buttons = form.getElementsByTagName('button');
if (buttons.length < 5) {
document.getElementsByClassName('add-btn')[0].disabled = false;
}
else {
document.getElementsByClassName('add-btn')[0].disabled = true;
}
}
/**
* 削除ボタンの有効無効の設定
*/
function set_delete_btn_disabled() {
var form = document.getElementById('form');
var buttons = form.getElementsByTagName('button');
if (buttons.length == 1) {
buttons[0].disabled = true;
}
else {
for (i = 0; i < buttons.length; i++) {
buttons[i].disabled = false;
}
}
}
実装の注意ポイント
削除ボタンに type="button" を指定しないと、押した瞬間にフォームがsubmitされてしまいます。これを防ぐためにも、button要素には明示的に type 属性を付けることが大切です。
また、querySelector, childElementCount, appendChild など、基本的なDOM操作が多く使われています。JavaScriptの練習素材としても最適です。
CodePenで試せるデモ
以下のCodePenで、今回紹介したコードを実際に動かして試すことができます。
ぜひカスタマイズして使ってみてください!
参考資料(DOM操作のMDNリファレンス)
document.createElement() – 要素を生成
Element.setAttribute() – 属性を設定/更新
Node.appendChild() – 要素を子要素として挿入
Element.remove() – 要素をDOMから削除
Element.childElementCount – 子要素の数を取得
Element.querySelector() – CSSセレクタで要素取得(最初の一致)


