JavaScript | DOM 操作:ノード操作 – cloneNode

JavaScript JavaScript
スポンサーリンク

cloneNode とは何か

cloneNode は、既存の DOM ノード(要素やテキスト)を“複製”するメソッドです。形は node.cloneNode(deep)。ここが重要です:deep が false(省略時は false)なら“その要素だけ”をコピー、true なら“子孫を含めて丸ごと”コピーします。複製したノードはまだ画面に表示されないので、必要な変更を加えてから親へ追加します。


基本の使い方(浅い複製と深い複製の違い)

浅い複製(子は含めない)

<div id="card" class="card">
  <h3>タイトル</h3>
  <p>本文</p>
</div>
<script>
  const card = document.getElementById("card");
  const copy = card.cloneNode(); // = cloneNode(false)
  // copy には class や属性は複製されるが、子要素 (h3, p) はない
  document.body.appendChild(copy);
</script>
HTML

ここが重要です:浅い複製は“枠だけ”欲しいときに使います。内部構造が不要な場合は軽くて安全です。

深い複製(子孫も含める)

const card = document.getElementById("card");
const deepCopy = card.cloneNode(true); // 子孫まで全部コピー
document.body.appendChild(deepCopy);
JavaScript

ここが重要です:UI 部品を“そのままもう1個”作るなら深い複製。イベントリスナーはコピーされないため、必要なら後で付け直します。


何がコピーされ、何がコピーされないか(重要ポイントを深掘り)

コピーされるもの

属性(id, class, data-* など)と、テキスト・子要素の“構造”はコピーされます。style 属性の中身も複製されます。

コピーされないもの

追加したイベントリスナー(addEventListener の登録)は複製されません。input の“現在値”などのプロパティ状態も、基本は初期状態へ戻ることがあります。ここが重要です:動的な状態(イベント・現在値・一時 URL の関連付け)は“あとから付け直す”のが定石です。

const btn = document.getElementById("btn");
btn.addEventListener("click", () => console.log("original"));
const copy = btn.cloneNode(true);
// copy には click リスナーがない → 必要なら付け直す
copy.addEventListener("click", () => console.log("copy"));
JavaScript

id の重複に注意(ユニーク性の維持)

同じ id を持つ要素を複製してそのまま挿入すると、文書内で id が重複してセレクタや参照が壊れます。ここが重要です:clone 後に id を必ず付け替えるか、id をもたないテンプレートを使い、必要箇所だけ一意な識別子を振ります。

const card = document.getElementById("card"); // id="card"
const copy = card.cloneNode(true);
copy.id = "card-copy"; // 一意に変更
document.body.appendChild(copy);
JavaScript

複製後のカスタマイズ(テキスト・属性・画像・イベント)

文言や画像の差し替え

const tpl = document.getElementById("product"); // 子要素に .title, .price, img がある前提
const copy = tpl.cloneNode(true);
copy.querySelector(".title").textContent = "りんご";
copy.querySelector(".price").textContent = "¥120";
const img = copy.querySelector("img");
img.src = "/img/apple.png";
img.alt = "りんご";
document.getElementById("list").appendChild(copy);
JavaScript

ここが重要です:clone → 中身を書き換え → append の型を身につけると、繰り返し UI を安全・高速に生成できます。

イベントの付け直し

const copyBtn = copy.querySelector(".buy");
copyBtn.addEventListener("click", () => {
  copyBtn.disabled = true;
  copyBtn.textContent = "購入済み";
});
JavaScript

ここが重要です:リスナーは複製されないため“必要なイベントは clone 後に付ける”を習慣化します。


テンプレートと cloneNode(最強コンボ)

<template> 要素を雛形に使う

<template id="tpl-card">
  <article class="card">
    <h3 class="title"></h3>
    <p class="desc"></p>
    <button class="buy">購入</button>
  </article>
</template>
<div id="root"></div>
<script>
  function render(item) {
    const node = document.getElementById("tpl-card").content.cloneNode(true); // 深い複製
    node.querySelector(".title").textContent = item.title;
    node.querySelector(".desc").textContent = item.desc;
    const btn = node.querySelector(".buy");
    btn.addEventListener("click", () => btn.textContent = "購入済み");
    document.getElementById("root").appendChild(node);
  }
  render({ title: "りんご", desc: "甘いです" });
</script>
HTML

ここが重要です:template.content は“画面に描画されない雛形”。cloneNode(true) で安全に複製し、中身だけ差し替えると、XSS や構造崩れの心配が減ります。


パフォーマンスのコツ(大量生成・フラグメント)

多数の複製を挿入する場合は DocumentFragment にまとめてから一度で追加します。ここが重要です:1件ずつ挿すより“まとめて”のほうがレイアウト再計算が減り軽くなります。

const frag = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const copy = tpl.content.cloneNode(true);
  copy.querySelector(".title").textContent = `項目 ${i}`;
  frag.appendChild(copy);
}
document.getElementById("root").appendChild(frag);
JavaScript

実務パターン(行追加、差し替え、ドラフト複製)

行の追加(同じ形を増やす)

const row = document.querySelector(".row"); // 雛形行
function addRow(text) {
  const copy = row.cloneNode(true);
  copy.querySelector(".text").textContent = text;
  document.getElementById("list").appendChild(copy);
}
addRow("新しい行");
JavaScript

置換(既存行を丸ごと作り直し)

function replaceRow(old) {
  const fresh = old.cloneNode(true);
  fresh.querySelector(".status").textContent = "更新済み";
  old.replaceWith(fresh);
}
JavaScript

ここが重要です:既存構造を保ちながら“中身だけ差し替え”。レイアウトやスタイルの安定性が高いです。

編集用ドラフトを複製して保管

const form = document.getElementById("edit-form");
const draft = form.cloneNode(true); // 深い複製でドラフト作成
// 編集をキャンセルしたら draft で元に戻す
form.replaceWith(draft);
JavaScript

ここが重要です:clone を“巻き戻し用のスナップショット”として使うと、Undo が簡単に書けます。


よくある落とし穴と回避策

イベントが複製されないため、必ず付け直す。id は一意に変更する。input・video などの“現在状態”は clone で期待通りにならないことがあるので、clone 後に value/checked/currentTime などを明示的に設定する。Object URL を使う画像は差し替え時に URL.revokeObjectURL で解放する。ここが重要です:clone は“構造と属性のコピー”、動的な“状態と振る舞い”は後から整える。


まとめ

cloneNode は「既存ノードの複製」を行う基本メソッドです。浅い複製(構造のみ)と深い複製(子孫含む)を使い分け、イベントは付け直す、id は一意にする、必要なプロパティ(value/checked など)は明示的に再設定。大量生成は DocumentFragment、複雑な UI は template+cloneNode の組み合わせが定石。これらを守れば、初心者でも安全で効率的に“同じ形の UI”を増やし、差し替えやドラフト管理をスマートに実装できます。

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