HTML と JavaScript の紐づけとは何か
HTML は「見た目と構造」、JavaScript は「動きと振る舞い」を担当します。紐づけとは、HTML の要素を JavaScript から選び、読み取り・変更・イベント登録を行うことです。ここが重要です:紐づけは「どの要素に」「どんなタイミングで」「どういう方法で」行うかが鍵。安定したフック(目印)を用意し、適切な読み込み順で、責務を分離して結びつけると、壊れにくい UI になります。
スクリプトの読み込み方法とタイミング
インラインと外部ファイルの違い
インラインは <script>...</script> に直接書く方法、外部は src でファイルを読み込みます。規模が大きくなるほど外部化が適しています。ここが重要です:外部化するとキャッシュが効き、再利用と保守が楽になります。インラインは最小限のブートストラップだけに留めるのが賢い選択です。
<!-- インライン(最小限に) -->
<script>
document.addEventListener("DOMContentLoaded", () => {
// 初期化
});
</script>
<!-- 外部化(推奨) -->
<script src="app.js" defer></script>
HTMLdefer と 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>
HTMLconst 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>
HTMLdocument.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>
HTMLconst el = document.querySelector("#price");
const value = Number(el.dataset.value);
const currency = el.dataset.currency;
JavaScriptJS から 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 の構造に強く依存した深
