JavaScript | DOM 操作:DOM 基礎 – HTML と JavaScript の紐づけ

JavaScript JavaScript
スポンサーリンク

HTML と JavaScript の紐づけとは何か

HTML は「見た目と構造」、JavaScript は「動きと振る舞い」を担当します。紐づけとは、HTML の要素を JavaScript から選び、読み取り・変更・イベント登録を行うことです。ここが重要です:紐づけは「どの要素に」「どんなタイミングで」「どういう方法で」行うかが鍵。安定したフック(目印)を用意し、適切な読み込み順で、責務を分離して結びつけると、壊れにくい UI になります。


スクリプトの読み込み方法とタイミング

インラインと外部ファイルの違い

インラインは <script>...</script> に直接書く方法、外部は src でファイルを読み込みます。規模が大きくなるほど外部化が適しています。ここが重要です:外部化するとキャッシュが効き、再利用と保守が楽になります。インラインは最小限のブートストラップだけに留めるのが賢い選択です。

<!-- インライン(最小限に) -->
<script>
  document.addEventListener("DOMContentLoaded", () => {
    // 初期化
  });
</script>

<!-- 外部化(推奨) -->
<script src="app.js" defer></script>
HTML

defer と async、そして module

defer は HTML の解析を止めず、解析完了直前に記述順で実行されます。DOM 操作中心なら最適です。async はダウンロードが終わり次第ただちに実行され、順序保証や DOM 構築との整合が崩れやすいので、トラッキングなど DOM 非依存に使います。type="module" は ES モジュールとして読み込み、デフォルトで defer と同等のタイミングで実行され、import/export が使えます。

<!-- DOM 操作中心の正攻法 -->
<script src="main.js" defer></script>

<!-- モジュールで構造化 -->
<script type="module">
  import { init } from "./main.js";
  init();
</script>
HTML

ここが重要です:DOM を触るコードは基本 defer(または module)。タイミングが整い、余計なイベント待ちのボイラープレートが減ります。


要素を見つける安定したフックの作り方

id・class・data-* の使い分け

要素を JS から選ぶには、CSS セレクタを使います。id は一意の要素に、class は複数要素のグループに、data-* は「JS が読むためのメタ情報」の格納に向いています。ここが重要です:見た目のクラス(スタイル用)と、振る舞いのフック(JS 用)を分けると、変更に強くなります。

<button id="buy" class="btn btn-primary" data-sku="SKU-123">購入</button>
HTML
const btn = document.querySelector("#buy");
console.log(btn.dataset.sku); // "SKU-123"
JavaScript

セレクタの例と注意点

querySelector は最初の一致を一つ、querySelectorAll はすべてを返します。HTML 構造に依存しすぎないセレクタを選び、過剰にネストしたセレクタを避けると保守性が上がります。

const cards = document.querySelectorAll(".product-card");
const first = document.querySelector(".product-card .buy-button");
JavaScript

ここが重要です:フックは「変わりにくい属性」を選ぶ。data-* は仕様の一部として活用すると、将来のスタイル変更に強い紐づけができます。


イベントで紐づける(振る舞いは JS に集約)

addEventListener とイベント委譲

HTML の onclick などの属性に書く方法は簡単ですが、HTML と JS が混ざって保守が難しくなります。addEventListener を使い、必要なら親要素にまとめて登録(委譲)するとスケールします。

<div class="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>
HTML
document.addEventListener("DOMContentLoaded", () => {
  const tabs = document.querySelector(".tabs");
  const panelA = document.querySelector("#panel-a");
  const panelB = document.querySelector("#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");
  });
});
JavaScript

ここが重要です:HTML は「意味と構造」、JS は「動き」。イベントを JS に集約し、クラス切り替えで見た目を制御すると、役割分担が明確になります。


データの受け渡し(HTML→JS と JS→HTML)

HTML から JS へ渡す

data-*、フォーム値、meta などで渡せます。data-* は要素単位、フォームはユーザー入力、meta はページ全体の設定に向いています。

<div id="price" data-currency="JPY" data-value="1200"></div>
HTML
const el = document.querySelector("#price");
const value = Number(el.dataset.value);
const currency = el.dataset.currency;
JavaScript

JS から HTML へ反映する

テキストは textContent、属性は setAttribute、状態や見た目はクラスで切り替えます。ここが重要です:ユーザー入力や外部データは必ず textContent で入れ、innerHTML は信頼できる限定的な場面だけにします(安全性と保守性のため)。

const priceEl = document.querySelector("#price");
priceEl.textContent = `${value.toLocaleString()} ${currency}`;
priceEl.classList.add("is-updated");
JavaScript

紐づけのベストプラクティスと落とし穴

ベストプラクティス

  • HTML と JS を分離し、外部スクリプト+defer(または type="module")で読み込む。
  • フックは安定した属性(id、data-*)を使い、JS 用クラスとスタイル用クラスを意識的に分ける。
  • イベントは addEventListener に集約し、表示制御はクラス切り替えで行う。
  • 初期化は DOMContentLoaded(または defer 実行時)に一度だけ行う。

ここが重要です:この4点を守るだけで、紐づけは“壊れない・読みやすい・拡張しやすい”状態になります。

よくある落とし穴

  • 早すぎる実行で document.querySelector(...)null。DOM 構築後に触る。
  • innerHTML の乱用で XSS とイベント消失。基本は textContent+要素生成。
  • HTML の構造に強く依存した深
タイトルとURLをコピーしました