JavaScript | DOM 操作:要素の取得 – 親要素・子要素の取得

JavaScript JavaScript
スポンサーリンク

親要素・子要素とは何か

DOM は木構造で、要素どうしが「親子」の関係を持ちます。ある要素を内側に包む要素が親、内側に含まれている要素が子です。ここが重要です:DOM 操作では「どの範囲(親配下)を対象にするか」と「要素だけ扱うのか(空白テキストは除外)」を意識して API を選ぶのがコツです。


親要素の取得(parentElement・closest の使い分け)

直接の親を取る

<div class="card">
  <h2 class="title">タイトル</h2>
</div>
<script>
  const title = document.querySelector(".title");
  const parent = title.parentElement; // 直近の親(要素)
  console.log(parent.className); // "card"
</script>
HTML

parentElement は「直近の親要素」を返します。親が欲しいだけなら最短で確実です。ここが重要です:parentNode だとテキストやドキュメントノードも含むため、UI ロジックでは parentElement を基本にします。

条件に合う祖先を探す(closest)

<article class="card">
  <div class="content">
    <button class="buy">購入</button>
  </div>
</article>
<script>
  const btn = document.querySelector(".buy");
  const card = btn.closest(".card"); // もっとも近い祖先の .card
  console.log(card.tagName); // "ARTICLE"
</script>
HTML

closest は「現在地から上に辿って、セレクタに合う最初の祖先」を返します。ここが重要です:HTML 構造の入れ子が変わっても、安定して目的の親(コンポーネント)を特定できます。


子要素の取得(children・querySelector の使い分け)

要素だけの子を列挙する

<ul id="list">
  <li>A</li>
  <li>B</li>
</ul>
<script>
  const list = document.getElementById("list");
  const kids = list.children;            // 要素だけ(ライブ)
  console.log(kids.length);              // 2
  console.log(kids[0].tagName);          // "LI"
</script>
HTML

children は「要素ノードだけ」を返すため、空白テキストに惑わされません。ここが重要です:見た目や UI 操作は要素中心なので、children/firstElementChild/lastElementChild を使うと安全です。

子孫の中から条件で絞る

<div class="card">
  <h2 class="title">T</h2>
  <button data-role="buy">購入</button>
</div>
<script>
  const card = document.querySelector(".card");
  const title = card.querySelector(".title");                 // 1件
  const buy   = card.querySelector('[data-role="buy"]');      // 属性条件
</script>
HTML

親要素から querySelector(または querySelectorAll)を呼ぶと、「その親配下だけ」を対象に柔軟な検索ができます。ここが重要です:スコープを絞ると、同名クラスが混ざる事故を防げます。


子ノード全般を扱う場合(childNodes・テキストの罠)

テキストやコメントも含めた子

<p id="msg">
  Hello
  <!-- note -->
  <b>World</b>
</p>
<script>
  const p = document.getElementById("msg");
  const nodes = p.childNodes; // テキスト/コメント/要素(ライブ)
  console.log(nodes[0].nodeType); // 3(Text)
</script>
HTML

childNodes はテキストやコメントも含みます。ここが重要です:空白や改行がテキストノードとして数えられるため、UI 操作では childNodes ではなく children を優先します。解析目的(文字列処理など)でのみ childNodes を使いましょう。


兄弟もあわせて辿る(正確な移動のコツ)

要素だけの兄弟を辿る

<ul>
  <li class="a">A</li>
  <li class="b">B</li>
</ul>
<script>
  const a = document.querySelector(".a");
  console.log(a.nextElementSibling.className); // "b"
</script>
HTML

nextElementSibling/previousElementSibling は要素だけを辿ります。ここが重要です:要素中心の移動を統一すると、空白テキストによるズレを防げます。


スコープ設計(親単位で区切ると保守が楽)

コンポーネント内で完結させる

<article class="card" id="product">
  <h2 data-role="title">商品名</h2>
  <p data-role="desc">説明</p>
  <button data-role="buy">購入</button>
</article>
<script defer>
  const card  = document.querySelector("#product");
  const title = card.querySelector('[data-role="title"]');
  const desc  = card.querySelector('[data-role="desc"]');
  const buy   = card.querySelector('[data-role="buy"]');
</script>
HTML

親要素(カード)から見つけることで、画面に同種のカードが複数あっても混ざりません。ここが重要です:data-* に役割を明示し、親単位の検索にすると、HTML 構造が変わっても壊れにくくなります。


実践例1:親から子を取得してイベントを委譲

<div class="tabs" id="tabs">
  <button data-tab="a" class="is-active">A</button>
  <button data-tab="b">B</button>
</div>
<div id="panel-a">Aの内容</div>
<div id="panel-b" class="is-hidden">Bの内容</div>
<script defer>
  const tabs = document.getElementById("tabs");
  const panelA = document.getElementById("panel-a");
  const panelB = document.getElementById("panel-b");

  tabs.addEventListener("click", (e) => {
    if (!(e.target instanceof HTMLButtonElement)) return;
    const key = e.target.dataset.tab;

    tabs.querySelectorAll("button").forEach(b =>
      b.classList.toggle("is-active", b === e.target)
    );

    panelA.classList.toggle("is-hidden", key !== "a");
    panelB.classList.toggle("is-hidden", key !== "b");
  });
</script>
HTML

親(.tabs)にだけイベントを登録し、クリックされた子(button)を判定しています。ここが重要です:親で受ける「イベント委譲」は、子の追加・削除に強く、登録数も減らせます。


実践例2:子を増やす・減らす安全な書き方

<ul id="list"></ul>
<script defer>
  const list = document.getElementById("list");

  function addItem(text) {
    const li = document.createElement("li");
    li.textContent = text;
    list.append(li); // 子に追加
  }

  function clearAll() {
    while (list.firstElementChild) {
      list.firstElementChild.remove(); // 要素だけを安全に削除
    }
  }

  addItem("Apple");
  addItem("Banana");
  clearAll();
</script>
HTML

firstElementChild を使うことで、空白テキストに引っかからずに安全に削除できます。ここが重要です:子の操作は「要素限定」の API で統一すると、予期せぬ挙動を防げます。


よくある落とし穴(空白テキスト・タイミング・ライブ性)

DOM 未構築のタイミングで親や子を取得すると null になります。defer で読み込むか、DOMContentLoaded を待ってから触ってください。また、childNodes はライブで変動し、空白テキストも含むため、UI ロジックでは避けます。ここが重要です:要素限定(children/closest/querySelector)と安全なタイミング(defer/DOMContentLoaded)をセットで守ると、親子操作は驚くほど安定します。


まとめ

親要素の取得は parentElement と closest、子要素の取得は children と親起点の querySelector が基本です。空白テキストに惑わされない「要素限定」の API を選び、スコープは親単位で区切る。イベントは親で受けて子を判定(委譲)し、追加・削除は firstElementChild など要素中心で安全に扱う。タイミングは defer/DOMContentLoaded に合わせる——この型を身につければ、親子関係の操作は直感的で壊れません。

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