JavaScript | DOM 操作:要素の取得 – closest

JavaScript JavaScript
スポンサーリンク

closest とは何か

closest は、ある要素から「自分自身を含めて、上方向(祖先方向)にセレクタに一致する最初の要素」を返すメソッドです。見つかればその要素、見つからなければ null。ここが重要です:HTML の入れ子構造が多少変わっても、「役割(セレクタ)」で祖先を特定できるため、DOM 構造変更に強いコードを書けます。


基本の使い方(自分から上に遡って探す)

シンプルな例

<article class="card">
  <header class="card-header">
    <button class="buy">購入</button>
  </header>
</article>
<script>
  const btn = document.querySelector(".buy");
  const card = btn.closest(".card");
  console.log(card.tagName); // "ARTICLE"
</script>
HTML

この例では、button から見て上方向に「.card」を探し、最初に見つかった article を返します。自分自身がセレクタに一致する場合は、自分が返ります。

自身が一致する場合

<div class="panel">
  <div class="panel inner">
    <button class="panel">OK</button>
  </div>
</div>
<script>
  const btn = document.querySelector("button.panel");
  console.log(btn.closest(".panel") === btn); // true(自分が一致)
</script>
HTML

ここが重要です:closest は「自分を含めて」判定します。まず自分、次に親、さらにその親…と上へ辿ります。


parentElement と querySelector との違い(どれを使うかの軸)

parentElement は「直近の親だけ」

const el = document.querySelector(".buy");
const parent = el.parentElement; // 直近の親
JavaScript

直近の親が欲しいだけならこれで十分ですが、条件に合う祖先を探すなら closest が適切です。

querySelector は「下方向の検索」

const card = document.querySelector(".card");
// 下方向(子孫)にあるボタンを探す
const buy = card.querySelector(".buy");
JavaScript

querySelector は子孫方向の探索。closest は祖先方向の探索。ここが重要です:「上へ辿るなら closest、下へ探すなら querySelector」という役割分担で迷いが消えます。


イベント委譲での実践(最小登録で確実に特定)

クリックした要素が“あるコンポーネント内”かを判定

<ul class="menu">
  <li class="item"><a class="link" href="#">A</a></li>
  <li class="item"><a class="link" href="#">B</a></li>
</ul>
<script defer>
  const menu = document.querySelector(".menu");

  menu.addEventListener("click", (e) => {
    const item = (e.target as Element).closest(".item"); // 祖先の .item を特定
    if (!item || !menu.contains(item)) return; // メニュー外のクリックは無視
    item.classList.toggle("is-active");
  });
</script>
HTML

ここが重要です:イベントは親(.menu)に1つだけ登録し、発生源から closest で目的の祖先を特定する。子の追加・削除に強く、登録数も増えないためスケールします。


セレクタ設計の勘所(安定したフックを選ぶ)

data-* を使うと構造変更に強い

<article class="card" data-role="product">
  <h2 data-role="title">Title</h2>
  <button class="buy">購入</button>
</article>
<script>
  const btn = document.querySelector(".buy");
  const product = btn.closest('[data-role="product"]');
  // 入れ子が変わっても、役割で祖先を特定
</script>
HTML

ここが重要です:見た目用クラス(レイアウト変更で変わりやすい)より、id や data-* の「役割フック」をセレクタに使うと、closest の信頼性が上がります。


よくある落とし穴と回避策(null・境界・shadow DOM)

見つからないときは null

const el = document.querySelector(".buy");
const modal = el.closest(".modal");
if (!modal) {
  // モーダル外ならスキップなど
}
JavaScript

ここが重要です:必ず null ガードを入れる。祖先に期待のコンテナが存在しないケースは普通に起こります。

範囲外のクリックを除外

親の中だけで動かしたい場合は、contains を併用すると安全です。

const root = document.querySelector(".root");
root.addEventListener("click", (e) => {
  const target = e.target as Element;
  const inside = target.closest(".root");
  if (!inside || inside !== root) return; // root 外は無視
});
JavaScript

shadow DOM 内ではスコープが分かれる

Web コンポーネントの shadowRoot 内でも closest は使えますが、影の外側は辿れません(閉じたスコープ)。コンポーネント間を跨ぐ設計は「外からイベントを受け取る仕組み」を用意します。


まとめ

closest は「自分から上へ辿って、セレクタに一致する最初の祖先(自分を含む)」を返す、祖先探索の決定版です。直近の親なら parentElement、子孫探索なら querySelector、と役割を分ける。イベント委譲との相性が抜群で、クリック発生源から目的のコンテナを即特定でき、登録数を増やさずに堅牢な UI を作れます。data-* など安定したフックを使い、常に null ガードを入れる——これだけで closest は初心者の強い味方になります。

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