DOMの構造:「親ノード・子ノード・兄弟ノード」

1) 基本の定義

  • 親ノード (parent node)
    あるノードを直接含んでいる上位のノード。ツリーを遡ると必ず 0 個または 1 個だけ存在します。
    例:<body><main> の親ノード。

  • 子ノード (child node)
    あるノードが直接含んでいる下位のノード群。0 個以上。
    例:<ul> の子ノードには <li> 要素や改行に由来するテキストノードが含まれることがあります。

  • 兄弟ノード (sibling nodes)
    同じ親を共有するノード同士。順序はツリー上の並びに従います。
    例:同じ <ul> 配下の複数の <li> は兄弟。

重要:DOM の「ノード」は要素だけでなく、テキストコメントなども含みます(要素だけに限定したい場合は Element 系 API を使います)。

2) 代表的なプロパティとメソッド

  • 親関係

    • node.parentNode(親が Document/DocumentFragment でも取得)

    • element.parentElement(親が要素のときのみ)

  • 子関係

    • node.childNodes全ノードの子:NodeList)

    • element.children要素ノードのみ:HTMLCollection、ライブコレクション)

    • firstChild / lastChild(全ノード)

    • firstElementChild / lastElementChild(要素のみ)

  • 兄弟関係

    • previousSibling / nextSibling(全ノード)

    • previousElementSibling / nextElementSibling(要素のみ)

  • 走査と操作(要素中心の近年の API)

    • 祖先検索:element.closest(selector)

    • 子孫検索:element.querySelector(All)

    • 追加:parent.append(node | ...nodes), parent.prepend(...)

    • 挿入:ref.before(...), ref.after(...), parent.insertBefore(newNode, refNode)

    • 置換/削除:ref.replaceWith(...), node.remove(), parent.removeChild(node)

  • 判定/比較

    • node.contains(other)(自分が祖先か)

    • node.compareDocumentPosition(other)(文書順の前後関係)

    • node.nodeType(1:要素, 3:テキスト, 8:コメント, 9:Document, 11:DocumentFragment)

3) ミニサンプル(HTML と JS)

HTML

html
<!doctype html> <html> <head><title>DOM Sample</title></head> <body> <main id="app"> <h1>Title</h1> <p>First</p> <p>Second</p> </main> </body> </html>

親・子・兄弟の取得

js
const main = document.getElementById('app'); // 親 console.log(main.parentNode === document.body); // true console.log(main.parentElement.tagName); // "BODY" // 子 console.log(main.childNodes.length); // 改行のテキストも数えることがある console.log(main.children.length); // 要素のみ(<h1>, <p>, <p> で 3) console.log(main.firstElementChild.tagName); // "H1" console.log(main.lastElementChild.tagName); // "P" // 兄弟 const firstP = main.children[1]; // <p>First</p> console.log(firstP.nextElementSibling.textContent); // "Second" console.log(firstP.previousElementSibling.tagName); // "H1"

ノード挿入・削除

js
const secondP = main.lastElementChild; secondP.after(document.createElement('hr')); // 兄弟として <hr> を挿入 const newP = document.createElement('p'); newP.textContent = 'Third'; main.append(newP); // 子の末尾に追加 // 削除 const h1 = main.firstElementChild; h1.remove(); // <h1> をツリーから取り除く

4) よくある落とし穴

  1. 空白や改行によるテキストノード
    整形のための改行・スペースも childNodes に含まれます。要素だけを扱いたいなら childrenfirstElementChild/nextElementSibling を使用します。

  2. ライブコレクション vs スナップショット
    children はライブ(DOM 変更が即時反映)、querySelectorAll の返す NodeList は多くの環境でスナップショット。反復中に DOM を変更するなら配列にコピーしてから操作すると安全です。

  3. 親の型
    parentNodeDocumentDocumentFragment を返すことがあります。常に要素を期待する場合は parentElement を使うか型をチェックします。

  4. 要素とノードの API の混在
    兄弟を要素に限定したいときに previousSibling/nextSibling を使うとテキストやコメントに当たることがあります。Element 系previousElementSibling など)を選びます。

5) 実用パターン

  • 特定の祖先を見つけたい

    js
    const button = event.target.closest('button'); const card = button.closest('.card'); // 最近傍の .card 祖先
  • 子の反復(要素のみ)

    js
    for (const el of parent.children) { // 要素ノードだけ処理 }
  • 兄弟間の並び替え

    js
    const item = list.children[2]; list.insertBefore(item, list.firstElementChild); // 先頭へ移動
  • 包含関係の確認

    js
    if (container.contains(node)) { // container の子孫(= 子またはより深い階層) }

6) まとめ

  • 親・子・兄弟は直接の階層関係であることがポイント。

  • 「要素だけ」扱うか「すべてのノード」を扱うかで Element 系 API と Node 系 API を使い分けます。

  • 改行テキストやライブコレクションなどの挙動差を把握しておくと、走査・挿入・削除・並び替えが安定します。

ChatGPT5 生成日:2025/09/11