JavaScript | DOM 操作:要素の取得 – 要素の存在チェック

JavaScript JavaScript
スポンサーリンク

要素の存在チェックとは何か

「要素の存在チェック」は、操作したい要素が本当に DOM にあるかを確認し、なければ安全にスキップしたり代替処理に切り替えたりすることです。ここが重要です:存在を前提に書くと、null 参照でエラーになりやすく、初期化が不安定になります。毎回“見つからない可能性”を想定して短くガードするのが、堅牢なコードの第一歩です。


基本のチェックパターン(null を正しく扱う)

1件取得のガード(querySelector / getElementById)

<h1 id="title">こんにちは</h1>
<script>
  const el = document.querySelector("#title"); // または document.getElementById("title")
  if (!el) {
    console.warn("#title が見つかりません");
  } else {
    el.textContent = "ようこそ";
  }
</script>
HTML

見つからなければ null が返るため、if (!el) の1行を癖にします。ここが重要です:処理は「早期 return」で分岐を短くし、見通しをよくします。

複数取得のガード(querySelectorAll)

<div class="card"><button class="buy">購入</button></div>
<div class="card"><button class="buy">購入</button></div>
<script>
  const buttons = document.querySelectorAll(".buy"); // NodeList(静的)
  if (buttons.length === 0) {
    console.warn(".buy がありません");
  } else {
    buttons.forEach(btn => btn.classList.add("bound"));
  }
</script>
HTML

NodeList は空でも安全に反復できますが、未存在なら早めに抜けると無駄が減ります。


タイミングのチェック(DOM 構築前は null になりがち)

DOMContentLoaded や defer で“いつ触るか”を合わせる

<script>
  document.addEventListener("DOMContentLoaded", () => {
    const el = document.querySelector("#panel");
    if (!el) return;           // 未存在なら何もしない
    el.textContent = "準備完了";
  });
</script>

<!-- または外部スクリプトを defer で読み込む -->
<script src="app.js" defer></script>
HTML

ここが重要です:要素は「出現前」に触ると必ず失敗します。DOM 構築後に実行するだけで、存在チェックの成功率が一気に上がります。


スコープのチェック(意図した範囲内かを確かめる)

親要素配下だけを対象にする

<section id="product">
  <button class="buy">購入</button>
</section>
<script>
  const product = document.getElementById("product");
  if (!product) return;

  const buy = product.querySelector(".buy"); // product 配下だけ
  if (!buy) return;

  buy.addEventListener("click", () => product.classList.add("purchased"));
});
</script>
HTML

ここが重要です:document 全体から探すより、親から探すほうが混入を防げます。存在チェックは「スコープを限定する」こととセットで考えると、事故が減ります。


クリック発生源のチェック(Element かどうか・範囲内か)

型ガードと範囲ガードの定番

<div class="menu" id="menu">
  <button class="item">A</button>
  <button class="item">B</button>
</div>
<script defer>
  const menu = document.getElementById("menu");
  if (!menu) throw new Error("#menu が必要です");

  menu.addEventListener("click", (e) => {
    const t = e.target;
    if (!(t instanceof Element)) return;      // テキストノード等は除外
    if (!menu.contains(t)) return;            // メニュー外は無視

    const isItem = t.matches("button.item");
    if (!isItem) return;                      // 対象でなければ終わり

    t.classList.toggle("is-active");
  });
</script>
HTML

ここが重要です:イベント処理は「Element かどうか」「対象コンテナ内かどうか」「セレクタ適合かどうか」の3段ガードで安定します。


代替取得・フォールバック(柔軟に生き延びる)

安全な OR 合体で別フックに切り替える

const el = document.getElementById("panel")
        || document.querySelector(".panel")
        || document.querySelector('[data-role="panel"]');

if (!el) {
  console.warn("パネルが見つからないため、機能をスキップします");
} else {
  el.textContent = "OK";
}
JavaScript

ここが重要です:本番では“ないこと”が普通に起こります。複数フックのフォールバックを用意すると、壊れにくくなります。


一度だけ初期化するチェック(重複実行を防ぐ)

フラグや属性で「初期化済み」を刻む

const widget = document.querySelector("#widget");
if (!widget || widget.dataset.inited === "true") return;

widget.dataset.inited = "true";  // 初期化済み印
// イベント登録や構築処理…
JavaScript

ここが重要です:存在チェックに「一回性チェック」を重ねると、再実行による二重バインド・二重描画を防げます。


例:柔らかい初期化(なければ静かにスキップ)

<header id="site-header">
  <button id="toggle">開閉</button>
</header>
<aside id="drawer" class="hidden">メニュー</aside>
<script defer>
  const header = document.getElementById("site-header");
  const toggle = document.getElementById("toggle");
  const drawer = document.getElementById("drawer");

  if (!header || !toggle || !drawer) {
    // 不足要素があれば何もしない(静かにスキップ)
    console.info("ドロワー初期化をスキップ:要素不足");
    return;
  }

  toggle.addEventListener("click", () => {
    const hidden = drawer.classList.toggle("hidden");
    toggle.textContent = hidden ? "開く" : "閉じる";
  });
</script>
HTML

ここが重要です:不足時に失敗させず、静かに諦める“柔らかい初期化”は、ページの部分的な変更に強い設計です。


一歩深掘り:非同期に現れる要素のチェック

MutationObserver で“後から出てくる要素”を待つ

function waitFor(selector, root = document) {
  const found = root.querySelector(selector);
  if (found) return Promise.resolve(found);

  return new Promise(resolve => {
    const obs = new MutationObserver(() => {
      const el = root.querySelector(selector);
      if (el) { obs.disconnect(); resolve(el); }
    });
    obs.observe(root, { childList: true, subtree: true });
  });
}

// 使い方
waitFor("#toast").then(el => el.classList.add("show"));
JavaScript

ここが重要です:SPA などで“後から挿入される”要素は、存在チェックに待機の仕組みを足すと安定します。


まとめ

要素の存在チェックは「null ガード」を癖にし、タイミング(defer/DOMContentLoaded)を合わせ、スコープ(親から検索)を限定するのが基本です。イベントでは Element/範囲/セレクタの3段ガードで誤判定を防ぎ、フォールバックや“一度だけ初期化”の工夫で本番耐性を高めます。非同期登場には待機を導入——この型を身につけると、DOM が“ないときもある”現実に揺らがない、堅牢な初期化が書けるようになります。

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