【JavaScript】HTML 要素を追加・削除する方法|createElement・append・remove・insertAdjacentHTML・実務パターンまで解説

【JavaScript】HTML 要素を追加・削除する方法|createElement・append・remove・insertAdjacentHTML・実務パターンまで解説 JavaScript

Web アプリケーションでは、ユーザーの操作に応じてHTML 要素を動的に追加・削除する場面が頻繁にあります。TODO リストへの項目追加、通知バナーの表示と自動消去、フォームフィールドの動的生成など、DOM の操作はフロントエンド開発の基本スキルです。

この記事でわかること
・createElement で要素を作成し appendChild / append で追加する方法
・insertBefore・before / after で任意の位置に挿入する方法
・insertAdjacentHTML で HTML 文字列を直接挿入する方法
・remove・removeChild で要素を削除する方法
・replaceWith・replaceChild で要素を置換する方法
・DocumentFragment で大量要素を効率的に追加する方法
・TODO リスト・通知バナー・無限スクロールの実務パターン
スポンサーリンク

createElement と appendChild で要素を追加する(基本)

DOM に要素を追加する最も基本的な手順は、document.createElement で要素を作成し、appendChild で親要素の末尾に追加するという 2 ステップです。

JavaScript
// 1. 要素を作成
const li = document.createElement("li");

// 2. テキストや属性を設定
li.textContent = "新しい項目";
li.className = "list-item";

// 3. 親要素の末尾に追加
const ul = document.getElementById("myList");
ul.appendChild(li);

appendChild は追加した要素(Node)を戻り値として返します。追加後にさらに操作を続けたい場合に便利です。

複数の属性やイベントを持つ要素を作成する

JavaScript
const btn = document.createElement("button");
btn.textContent = "削除";
btn.className = "btn btn-danger";
btn.setAttribute("data-id", "42");
btn.addEventListener("click", () => {
  btn.closest(".card").remove();
});

document.getElementById("toolbar").appendChild(btn);
createElement で作った要素は、DOM に追加するまで画面には表示されません。作成しただけではメモリ上にのみ存在する状態です。

append と appendChild の違い

モダンブラウザでは append メソッドが使えます。appendChild との違いを正確に把握しておきましょう。

比較項目 appendChild append
引数の型 Node のみ Node と文字列の両方
複数引数 不可(1 つのみ) 可能(カンマ区切り)
戻り値 追加した Node undefined
ブラウザ対応 IE6+ IE 非対応
append の使用例
const container = document.getElementById("container");

// 文字列とノードを同時に追加できる
const strong = document.createElement("strong");
strong.textContent = "重要";

container.append("お知らせ:", strong, "な更新があります");

同様に、先頭に追加する prepend も使えます。

prepend の使用例
const ul = document.getElementById("myList");
const li = document.createElement("li");
li.textContent = "先頭に追加された項目";

// 最初の子要素として追加
ul.prepend(li);
新規開発では append / prepend を使う方がコードが簡潔になります。IE 対応が必要な場合のみ appendChild を使いましょう。

任意の位置に要素を挿入する

insertBefore(従来の方法)

parentNode.insertBefore(newNode, referenceNode) で、参照ノードの直前に要素を挿入します。

JavaScript
const ul = document.getElementById("myList");
const newLi = document.createElement("li");
newLi.textContent = "2番目に挿入";

// 2番目の子要素の前に挿入(=2番目の位置になる)
const secondItem = ul.children[1];
ul.insertBefore(newLi, secondItem);

// referenceNode が null の場合は末尾に追加される
ul.insertBefore(newLi, null); // appendChild と同じ動作

before / after(モダンな方法)

before / after メソッドは、対象の要素を基準にして直前・直後に挿入します。insertBefore より直感的に書けます。

JavaScript
const target = document.getElementById("item3");

// target の直前に挿入
const newBefore = document.createElement("li");
newBefore.textContent = "item3 の前に追加";
target.before(newBefore);

// target の直後に挿入
const newAfter = document.createElement("li");
newAfter.textContent = "item3 の後に追加";
target.after(newAfter);

// 文字列も渡せる(テキストノードとして挿入)
target.before("テキスト: ");
メソッド 挿入位置 特徴
appendChild(node) 親要素の末尾 従来の標準メソッド
append(...nodes) 親要素の末尾 複数・文字列対応
prepend(...nodes) 親要素の先頭 複数・文字列対応
before(...nodes) 対象要素の直前 兄弟として挿入
after(...nodes) 対象要素の直後 兄弟として挿入
insertBefore(new, ref) 参照ノードの直前 IE 対応、戻り値あり

insertAdjacentHTML で HTML 文字列を挿入する

HTML の文字列をそのまま DOM に挿入したい場合は insertAdjacentHTML が便利です。createElement で要素を組み立てる手間を省けます。

構文
element.insertAdjacentHTML(position, htmlString);
// position: 挿入位置を指定する文字列
// htmlString: 挿入する HTML 文字列

position には以下の 4 つの値を指定できます。

HTML(位置関係の図解)
<!-- "beforebegin" → ここ -->
<div id="target">
  <!-- "afterbegin" → ここ -->
  既存のコンテンツ
  <!-- "beforeend" → ここ -->
</div>
<!-- "afterend" → ここ -->
JavaScript
const target = document.getElementById("target");

// 要素の内部の末尾に追加(最もよく使う)
target.insertAdjacentHTML("beforeend", '<p class="note">追加テキスト</p>');

// 要素の内部の先頭に追加
target.insertAdjacentHTML("afterbegin", "<h3>見出し</h3>");

// 要素の直前(兄弟として)に追加
target.insertAdjacentHTML("beforebegin", "<hr>");

// 要素の直後(兄弟として)に追加
target.insertAdjacentHTML("afterend", "<p>後続コンテンツ</p>");
insertAdjacentHTML は文字列をパースして DOM に挿入するため、ユーザー入力をそのまま渡すと XSS(クロスサイトスクリプティング)の危険があります。ユーザー入力を含む場合は textContent を使うか、入力値を必ずエスケープしてください。

innerHTML との違い

比較項目 insertAdjacentHTML innerHTML
動作 既存の子要素を保持して追加 既存の子要素をすべて破棄して置換
イベントリスナー 既存要素のリスナーは維持 既存要素のリスナーは失われる
位置指定 4 種類の位置を選べる 要素内部の置換のみ
パフォーマンス 差分のみパース 全体を再パース

innerHTML += で要素を追加すると、既存の子要素がすべて再生成されるため、イベントリスナーが消えてしまう問題があります。要素の追加には insertAdjacentHTML を使う方が安全です。

NG: innerHTML += でリスナーが消える
const list = document.getElementById("myList");

// 既存の li にクリックイベントを設定
list.querySelector("li").addEventListener("click", handleClick);

// innerHTML += で追加 → 既存の li が再生成されリスナーが消える
list.innerHTML += "<li>新しい項目</li>"; // handleClick が失われる
OK: insertAdjacentHTML でリスナーを維持
const list = document.getElementById("myList");

list.querySelector("li").addEventListener("click", handleClick);

// insertAdjacentHTML なら既存の li のリスナーは維持される
list.insertAdjacentHTML("beforeend", "<li>新しい項目</li>");

要素を削除する

remove(モダンな方法)

要素自身を DOM から削除する最もシンプルな方法です。

JavaScript
const item = document.getElementById("removeTarget");
item.remove(); // 要素を DOM から削除

// 削除した要素は変数に残っているため再利用可能
console.log(item.textContent); // まだアクセスできる
document.body.append(item);    // 別の場所に再追加も可能

removeChild(従来の方法)

親要素を経由して子要素を削除する方法です。IE に対応する必要がある場合はこちらを使います。

JavaScript
const item = document.getElementById("removeTarget");

// 親要素から子要素を削除
item.parentNode.removeChild(item);

// すべての子要素を削除する場合
const parent = document.getElementById("container");
while (parent.firstChild) {
  parent.removeChild(parent.firstChild);
}

すべての子要素を一括削除する方法の比較

方法 コード 特徴
innerHTML el.innerHTML = "" 最も簡潔。リスナーは GC で回収
replaceChildren() el.replaceChildren() モダン。引数なしで全削除
while + removeChild while(el.firstChild) el.removeChild(el.firstChild) 従来の方法。IE 対応
モダンブラウザでは replaceChildren() が最も意図が明確です。引数を渡すと子要素の置換にもなります。el.replaceChildren(newChild1, newChild2) のように使えます。

要素を置換する

replaceWith(モダンな方法)

JavaScript
const oldEl = document.getElementById("oldMessage");
const newEl = document.createElement("div");
newEl.textContent = "更新されたメッセージ";
newEl.className = "message updated";

// oldEl を newEl に置換
oldEl.replaceWith(newEl);

// 文字列でも置換可能
// oldEl.replaceWith("テキストに置換");

replaceChild(従来の方法)

JavaScript
const parent = document.getElementById("container");
const oldChild = parent.children[0];
const newChild = document.createElement("p");
newChild.textContent = "置換後の要素";

// 親要素から子要素を置換
parent.replaceChild(newChild, oldChild);

DocumentFragment で大量の要素を効率的に追加する

ループで大量の要素を追加する場合、1 つずつ DOM に追加すると追加のたびにレイアウト再計算(リフロー)が発生してパフォーマンスが低下します。DocumentFragment を使えば、メモリ上でまとめて組み立ててから一度に追加できます。

NG: ループ内で毎回 DOM に追加
const ul = document.getElementById("myList");
const items = ["りんご", "みかん", "ぶどう", "いちご", "メロン"];

// 5回のリフローが発生
items.forEach(item => {
  const li = document.createElement("li");
  li.textContent = item;
  ul.appendChild(li); // 毎回 DOM が更新される
});
OK: DocumentFragment でまとめて追加
const ul = document.getElementById("myList");
const items = ["りんご", "みかん", "ぶどう", "いちご", "メロン"];

// DocumentFragment にまとめる
const fragment = document.createDocumentFragment();
items.forEach(item => {
  const li = document.createElement("li");
  li.textContent = item;
  fragment.appendChild(li); // メモリ上に追加(リフローなし)
});

// 1回のリフローで一括追加
ul.appendChild(fragment);
数個の要素であれば appendChild を直接使っても体感差はありません。数十〜数百の要素を追加する場合に DocumentFragment の効果が出ます。詳しくは「createDocumentFragment() を使った複数要素の効率的な追加方法」を参照してください。

実務でよく使うパターン

TODO リスト(追加と削除)

HTML
<div id="todo-app">
  <input id="todoInput" type="text" placeholder="タスクを入力">
  <button id="addBtn">追加</button>
  <ul id="todoList"></ul>
</div>
JavaScript
const input = document.getElementById("todoInput");
const addBtn = document.getElementById("addBtn");
const todoList = document.getElementById("todoList");

addBtn.addEventListener("click", () => {
  const text = input.value.trim();
  if (!text) return;

  const li = document.createElement("li");
  li.textContent = text;

  // 削除ボタンを追加
  const deleteBtn = document.createElement("button");
  deleteBtn.textContent = "×";
  deleteBtn.addEventListener("click", () => li.remove());
  li.append(" ", deleteBtn);

  todoList.appendChild(li);
  input.value = "";
  input.focus();
});

自動消去される通知バナー

JavaScript
function showNotification(message, duration = 3000) {
  const notification = document.createElement("div");
  notification.className = "notification";
  notification.textContent = message;

  document.body.insertAdjacentElement("afterbegin", notification);

  // 指定時間後にフェードアウトして削除
  setTimeout(() => {
    notification.style.opacity = "0";
    notification.addEventListener("transitionend", () => {
      notification.remove();
    });
  }, duration);
}

// 使用例
showNotification("保存しました", 2000);

イベント委任で動的に追加した要素にイベントを設定する

動的に追加した要素には、あらかじめ addEventListener を設定できません。親要素にイベントを設定し、event.target で判定するイベント委任パターンが有効です。

JavaScript
const todoList = document.getElementById("todoList");

// 親要素に1つだけイベントリスナーを設定
todoList.addEventListener("click", (e) => {
  // クリックされた要素が削除ボタンかどうか判定
  if (e.target.matches(".delete-btn")) {
    e.target.closest("li").remove();
  }
});

// 後から追加された li 内の .delete-btn もイベントが動作する
function addTodo(text) {
  todoList.insertAdjacentHTML(
    "beforeend",
    `<li>${text} <button class="delete-btn">×</button></li>`
  );
}
イベント委任は、リスナーの数を最小限に抑えてパフォーマンスを向上させます。動的に要素を追加・削除するアプリでは必須のテクニックです。

要素の追加・削除メソッド一覧

操作 モダン API 従来 API
末尾に追加 parent.append(node) parent.appendChild(node)
先頭に追加 parent.prepend(node) parent.insertBefore(node, parent.firstChild)
直前に挿入 ref.before(node) parent.insertBefore(node, ref)
直後に挿入 ref.after(node) parent.insertBefore(node, ref.nextSibling)
要素を削除 node.remove() parent.removeChild(node)
要素を置換 old.replaceWith(new) parent.replaceChild(new, old)
全子要素を削除 parent.replaceChildren() parent.innerHTML = ""

関連記事

よくある質問

QappendChild と append はどちらを使うべきですか?
AIE 対応が不要なら append を推奨します。文字列や複数の要素をまとめて追加でき、コードが簡潔になります。prependbeforeafter も同系統のモダン API です。
QinnerHTML += で要素を追加するのはなぜ非推奨なのですか?
AinnerHTML += は既存の子要素をすべて再パース・再生成するため、設定済みのイベントリスナーが消失します。また、ユーザー入力を含む場合は XSS のリスクもあります。要素の追加には insertAdjacentHTMLappend を使いましょう。
Q動的に追加した要素にイベントリスナーが効かないのはなぜですか?
AaddEventListener は呼び出し時点で存在する要素にしか設定されません。後から追加された要素にも対応するには、親要素にリスナーを設定するイベント委任パターンを使います。parent.addEventListener("click", e => { if (e.target.matches(".btn")) { ... } })
Qremove() で削除した要素は完全に消えますか?
Aremove() は要素を DOM ツリーから切り離すだけです。変数で参照を保持していればメモリに残っており、document.body.append(el) で再追加も可能です。参照がなくなればガベージコレクションで回収されます。
Q大量の要素を追加するとページが重くなります。対策は?
ADocumentFragment を使って一度に追加するか、仮想スクロール(表示領域の要素だけを DOM に保持)の導入を検討してください。数千件以上のデータは、ライブラリ(react-window 等)の活用も有効です。

まとめ

JavaScript で HTML 要素を動的に追加・削除する方法を整理しました。createElement + append が基本で、HTML 文字列を直接挿入したい場合は insertAdjacentHTML、要素の削除は remove() を使うのがモダンな書き方です。

実務ではinnerHTML += を避けること(リスナー消失・XSS リスク)と、動的要素にはイベント委任を使うことの 2 点が特に重要です。これらを押さえておけば、TODO リストや通知、動的フォームなど、DOM 操作を伴う機能を安全かつ効率的に実装できます。