テキストと要素の違いとは何か
DOM は「要素ノード」と「テキストノード」でできています。要素ノードはタグ(div、p、span など)そのもので、属性やクラス、イベントを持てます。テキストノードは純粋な文字だけで、タグや属性はありません。ここが重要です:見た目や振る舞い(CSS・イベント)は要素が担当し、表示する文言はテキストが担当します。役割を分けることで、安全で壊れにくい UI が作れます。
要素ノードの特徴(構造・属性・イベントを持てる)
要素は「箱」そのもの
<div id="card" class="info" data-type="product">Apple</div>
<script>
const card = document.getElementById("card");
console.log(card.tagName); // "DIV"(要素名)
console.log(card.className); // "info"(クラス)
console.log(card.dataset.type); // "product"(data-* 属性)
card.addEventListener("click", () => console.log("clicked")); // イベント登録
</script>
HTML要素はタグ名、属性、クラス、スタイル、イベントリスナを持てます。ここが重要です:見た目(CSS)や動き(JS)は要素に紐づけ、テキストはその中身として扱います。
テキストノードの特徴(文字だけ、属性やイベントはない)
テキストは「中身の文字」
<p id="msg"></p>
<script>
const msg = document.getElementById("msg");
const text = document.createTextNode("こんにちは"); // テキストノードを生成
msg.appendChild(text); // p 要素の中身として追加
console.log(text.nodeType); // 3(Text)
// text にクラスやイベントは付けられない
</script>
HTMLテキストノードは文字を保持するだけで、クラスやスタイル、イベントは持てません。ここが重要です:装飾や操作は必ず親の要素に対して行い、文字は textContent や createTextNode で入れます。
取得・走査での違い(children と childNodes、最初の子の扱い)
要素だけを扱うか、全ノードを扱うか
<ul id="list">
<!-- コメント -->
<li>A</li>
<li>B</li>
テキスト
</ul>
<script>
const list = document.getElementById("list");
console.log(list.children.length); // 2(LI 要素だけ)
console.log(list.childNodes.length); // 4(コメント・要素・テキストを含む)
console.log(list.firstElementChild.tagName); // "LI"(要素限定の先頭)
console.log(list.firstChild.nodeType); // 8 or 3(コメント/テキストの可能性)
</script>
HTMLここが重要です:UI ロジックは「要素限定」の API を使うのが安全です。children/firstElementChild/nextElementSibling を優先すると、空白やコメントでズレません。
テキストの入れ替えと HTML の展開(安全性と意図の違い)
テキストだけを安全に入れる
<h2 id="title"></h2>
<script>
const title = document.getElementById("title");
title.textContent = "ようこそ"; // タグを解釈せず、文字として表示(安全)
</script>
HTMLHTML を構造として挿入する
<div id="box"></div>
<script>
const box = document.getElementById("box");
box.innerHTML = "<b>強調</b> と <i>斜体</i>"; // タグが展開される(要注意)
</script>
HTMLここが重要です:外部データやユーザー入力は常に textContent(または createTextNode)。innerHTML は固定テンプレートに限り、動的部分は必ずエスケープして混ぜます。安全性を最優先に。
イベントとスタイルは要素に付ける(テキストには付けない)
動き・見た目は親要素へ
<p id="line"><span id="name"></span> さん、こんにちは</p>
<script>
const line = document.getElementById("line");
const name = document.getElementById("name");
name.textContent = "田中"; // 文字は安全に差し込む
line.classList.add("greeting"); // 見た目は要素にクラスで
line.addEventListener("click", () => { // 動きも要素にイベントで
line.classList.toggle("active");
});
</script>
HTMLここが重要です:テキストノードにイベントやクラスは付けられません。常に「要素に付ける」を徹底すると、設計がシンプルで壊れにくくなります。
部分更新のコツ(構造は要素で、内容はテキストで)
子要素を保ったまま文字だけ差し替える
<article class="card">
<h3><span id="title-text"></span></h3>
<p id="desc-text"></p>
</article>
<script>
document.getElementById("title-text").textContent = "お知らせ";
document.getElementById("desc-text").textContent = "明日メンテナンスがあります。";
</script>
HTMLここが重要です:構造は要素(固定)、可変部分はテキスト。この分離で、イベントが消えず、XSS の心配もなく、変更に強い UI になります。
解析・判定の違い(matches・closest は要素で使う)
要素に対するセレクタ判定・祖先探索
<div class="card"><button class="buy">購入</button></div>
<script>
document.addEventListener("click", (e) => {
const t = e.target;
if (!(t instanceof Element)) return; // テキストノード対策
if (t.matches("button.buy")) {
const card = t.closest(".card");
card.classList.add("purchased");
}
});
</script>
HTMLここが重要です:matches/closest は「要素」に対して使います。イベントの target がテキストノードになることがあるため、必ず Element かどうかをチェックします。
実践例:メモ行の追加(構造は要素、文字はテキスト)
<ul id="notes"></ul>
<input id="input" placeholder="メモを入力">
<button id="add">追加</button>
<script>
const notes = document.getElementById("notes");
const input = document.getElementById("input");
const add = document.getElementById("add");
add.addEventListener("click", () => {
const text = input.value.trim();
if (!text) return;
const li = document.createElement("li"); // 要素(構造)
li.appendChild(document.createTextNode(text)); // テキスト(内容)
notes.appendChild(li);
input.value = "";
});
</script>
HTMLここが重要です:createElement + createTextNode の組み合わせなら、外部入力でも“文字として”安全に表示できます。構造と内容を分けるのが定石です。
まとめ
要素は「箱」で、属性・クラス・イベント・スタイルを持てる。テキストは「中身の文字」で、装飾やイベントは持てない。取得や走査は要素限定の API(children/firstElementChild/matches/closest)を使い、内容の更新は textContent や createTextNode で安全に行う。HTML を展開する必要があるときだけ innerHTML を限定的に使い、動的部分は必ず文字として扱う。構造と内容の役割を明確に分離すると、セキュアで保守しやすい DOM 操作が身につきます。
