【JavaScript】要素の追加・削除(createElement, appendChild等)

javascript-icon JavaScript

こんにちは、かつコーチです。

前回は querySelector を使って、DOMツリーの中から目的の要素を取得する方法を解説しました。

今回は一歩進んで、DOMツリーそのものに新しい要素を追加したり、不要な要素を削除したりする方法を扱います。

「Todoリストにアイテムを追加する」「通知を消す」といった、実際のWebアプリでよく見る動きの基礎になる部分です。

要素を新しく作る:createElement

基本の書き方

新しい要素をDOMに追加するには、まず document.createElement() で要素そのものを作ります。

const p = document.createElement("p");
p.textContent = "新しく追加した段落です";

console.log(p);
// <p>新しく追加した段落です</p>

ここで注意したいのは、createElement で作った時点では、この要素はまだ画面には表示されていないということです。

作った要素は、いわば「まだどこにも置かれていない部品」のような状態です。

この部品をDOMツリーのどこかに「取り付ける」ことで、初めて画面に表示されます。

appendChildで子要素として追加する

作った要素をDOMツリーに追加するには、appendChild() を使います。

<ul id="fruit-list">
  <li>りんご</li>
</ul>
const list = document.querySelector("#fruit-list");

const newItem = document.createElement("li");
newItem.textContent = "みかん";

list.appendChild(newItem);

list.appendChild(newItem) は、「list の子要素の一番最後に newItem を追加する」という意味です。

このコードを実行すると、画面には「りんご」の下に「みかん」が追加された状態で表示されます。

insertBeforeで挿入位置を指定する

appendChild は必ず一番最後に追加されますが、途中に挿入したい場合は insertBefore() を使います。

const list = document.querySelector("#fruit-list");
const referenceItem = document.querySelector("#fruit-list li"); // 最初の<li>

const newItem = document.createElement("li");
newItem.textContent = "ぶどう";

list.insertBefore(newItem, referenceItem);
// 「りんご」の前に「ぶどう」が挿入される

insertBefore(挿入する要素, 基準となる要素) という順番で指定する点に注意してください。

要素を削除する

removeChildによる削除

要素を削除するときは、removeChild() を使う方法が昔からよく使われてきました。

const list = document.querySelector("#fruit-list");
const target = document.querySelector("#fruit-list li"); // 最初の<li>

list.removeChild(target);

removeChild() は「親要素」に対して呼び出し、「どの子要素を削除するか」を引数で指定する、という少し回りくどい書き方になっています。

remove()でシンプルに削除する

現在は、削除したい要素自身に対して remove() を呼び出すだけの、よりシンプルな方法が使えます。

const target = document.querySelector("#fruit-list li"); // 最初の<li>

target.remove();

親要素を経由せず、削除したい要素だけを直接指定できるので、コードの意図も読みやすくなります。

新しくコードを書くのであれば、基本的には remove() を使えば十分です。

テキストの挿入方法によるつまずきポイント

innerHTMLとtextContentの違い

要素の中身を追加する方法には、createElement を使う以外に、innerHTML に直接文字列を代入する方法もあります。

私が実務で実際にヒヤッとした経験が、この innerHTML の使い方に関するものでした。

❌ Before:ユーザー入力をそのままinnerHTMLに差し込んでしまう

const list = document.querySelector("#comment-list");
const userInput = "<img src=x onerror=alert('乗っ取り成功')>"; // ユーザーが入力した文字列という想定

const li = document.createElement("li");
li.innerHTML = userInput; // ユーザー入力をそのままHTMLとして解釈させてしまう
list.appendChild(li);
// userInputの中にタグが含まれていると、そのままHTMLとして実行されてしまう

innerHTML は、代入した文字列を「HTMLのコード」としてそのまま解釈します。

もしユーザーが入力した文字列の中に <script> タグや <img> タグのようなものが含まれていた場合、意図しないコードが実行されるXSS(クロスサイトスクリプティング)という脆弱性につながる恐れがあります。

私は個人開発のフォームで、この仕組みを知らずに innerHTML を使ってコメント欄を実装してしまい、後からセキュリティのレビューで指摘されて青ざめた経験があります。

✅ After:textContentを使って文字列として安全に挿入する

const list = document.querySelector("#comment-list");
const userInput = "<img src=x onerror=alert('乗っ取り成功')>";

const li = document.createElement("li");
li.textContent = userInput; // 文字列としてそのまま表示され、HTMLとしては解釈されない
list.appendChild(li);
// 画面には「<img src=x onerror=alert('乗っ取り成功')>」という文字列がそのまま表示される

textContent は、代入した文字列をHTMLとしてではなく、ただの文字列として扱います。

ユーザーが入力した内容を画面に表示するときは、innerHTML ではなく textContent を使うのが基本方針です。

innerHTML を使ってよいのは、自分自身が用意した固定のHTML文字列を挿入する場合など、外部からの入力が混ざらないケースに限定しましょう。

複数要素をまとめて追加するときの工夫

DocumentFragmentで再描画のコストを抑える

余談として、リストのアイテムを何十件もまとめて追加するようなケースでは、appendChild をループの中で何度も呼び出すと、そのたびに画面の再描画が発生してパフォーマンスが落ちることがあります。

const list = document.querySelector("#fruit-list");
const fruits = ["いちご", "メロン", "バナナ"];

const fragment = document.createDocumentFragment();

fruits.forEach(function (fruit) {
  const li = document.createElement("li");
  li.textContent = fruit;
  fragment.appendChild(li);
});

list.appendChild(fragment); // まとめて1回だけDOMに追加される

DocumentFragment は「画面には表示されない仮のコンテナ」で、この中に要素を溜めてから最後に一度だけ本物のDOMに追加すると、再描画の回数を1回に抑えられます。

件数が少ないうちは気にしなくても問題ありませんが、こういう考え方があることだけ知っておくと、後々のパフォーマンス改善で役立ちます。

まとめ

この記事のポイント

  • createElement で新しい要素を作り、appendChildinsertBefore でDOMツリーに追加する
  • 要素の削除は removeChild(親経由)よりも、remove()(要素自身に呼び出す)の方がシンプル
  • ユーザー入力を画面に表示するときは、innerHTML ではなく textContent を使いXSSを防ぐ
  • 大量の要素を追加するときは DocumentFragment を使うと再描画のコストを抑えられる

次に読むべき記事

要素の追加・削除ができるようになったら、次は見た目に直結する「クラスの操作」を見ていきましょう。

→ 次の記事:classListでクラスを操作する方法

タイトルとURLをコピーしました