目次
DOMとは何か
HTML → DOMツリー
ブラウザは:
- HTMLを読み込む
- パースして「木構造(ツリー)」にする
- そのツリーを DOM(Document Object Model) として JavaScript から操作できるようにする
例として、HTML:
<body>
<h1 id="title">こんにちは</h1>
<p class="message">メッセージ</p>
</body>DOMツリーのイメージ:
document
└─ html
├─ head
└─ body
├─ h1#title
└─ p.messageJavaScript からは document を起点にこのツリーにアクセスします。
Node / Element という概念
DOMの世界にはいろいろな「ノード」があります。
- Node:DOMツリーの全ての要素の共通インターフェース
- 要素ノード(element)
- テキストノード(text)
- コメントノード(comment)など
- Element:HTMLタグに対応するノード(
<div>,<p>,<span>など)
普段よく触るのは「要素ノード(Element)」です。
要素の取得:DOMクエリAPI
古いAPI
document.getElementById(id)document.getElementsByClassName(className)document.getElementsByTagName(tagName)
例:
const title = document.getElementById("title");
const messages = document.getElementsByClassName("message"); // HTMLCollectionこれらは歴史的に存在していて、今も使われますが、
新しく覚えるなら querySelector / querySelectorAll を基本にすると楽です。
モダンなAPI:querySelector / querySelectorAll
// CSSセレクタで1つだけ取得(最初の1件)
const title = document.querySelector("#title");
const firstMessage = document.querySelector(".message");
// CSSセレクタで複数取得(すべて)
const items = document.querySelectorAll(".item"); // NodeList- 引数は CSSセレクタ(
#id,.class,tag, 組み合わせなど) querySelector:マッチする 最初の1件(なければnull)querySelectorAll:マッチする すべて(NodeList)
例:
const firstItem = document.querySelector("ul.list > li");
const buttons = document.querySelectorAll("button.primary");NodeList と HTMLCollection と Array
querySelectorAll→ NodeList(静的)getElementsByClassName→ HTMLCollection(ライブ)
ざっくり:
- NodeList(静的):取得時点のスナップショット。後でDOMが変わっても勝手には更新されない。
- HTMLCollection(ライブ):DOMが書き換わると中身も自動で変わることがある。
そしてどちらも Array ではない ので、map や filter が直接は使えません。
const items = document.querySelectorAll(".item");
items.forEach(el => {
console.log(el.textContent);
});
// Arrayメソッドを使いたければ明示的に変換
const arr = Array.from(items);
const texts = arr.map(el => el.textContent);テキスト・HTMLの読み書き
textContent(基本はこちらを使用)
const title = document.querySelector("#title");
console.log(title.textContent); // 現在のテキスト
title.textContent = "新しいタイトル"; // テキストを置き換え- 子要素も含めた「テキストノードだけ」を扱うプロパティ
- HTMLタグは解釈されず、ただの文字列として扱われる(安全)
innerHTML(HTMLごと書き換え)
const box = document.querySelector("#box");
box.innerHTML = "<strong>太字</strong>と<span>タグ</span>を含む内容";- 文字列として渡したHTMLをパースして、要素として差し込む
- 強力だが、XSS(スクリプト混入)リスクがあるので、生のユーザー入力には使わない
ざっくり使い分け:
- テキストだけ変えたい →
textContent - 手元で安全に生成したHTML断片を入れたい →
innerHTMLもアリ
属性・値・データ属性
属性アクセス:id, href, src, value など
多くのHTML属性は、要素オブジェクトのプロパティとして直接操作できます。
<a id="link" href="https://example.com">Example</a>
<input id="nameInput" value="Taro" />const link = document.querySelector("#link");
console.log(link.href); // 絶対URLとして取得
link.href = "https://google.com";
const input = document.querySelector("#nameInput");
console.log(input.value); // 入力値
input.value = "Hanako"; // 入力値を書き換えgetAttribute / setAttribute
属性名そのままを扱いたい場合は、こちら。
const link = document.querySelector("#link");
console.log(link.getAttribute("href")); // HTML上の値(相対パスなど)
link.setAttribute("href", "/home");プロパティ vs attribute の違いは細かく言うと色々ありますが、
まずは:
- 一般的な操作 → プロパティ(
element.href,element.value) - 「属性そのもの」を気にする特殊な場合 →
getAttribute/setAttribute
くらいの認識でOKです。
データ属性と dataset
<div id="item" data-id="123" data-role="admin"></div>const item = document.querySelector("#item");
console.log(item.dataset.id); // "123"
console.log(item.dataset.role); // "admin"
// 書き換え
item.dataset.role = "user";data-foo-bar→dataset.fooBarというキャメルケースでアクセス- 簡単なメタ情報・設定値を DOM に埋め込むのに便利
クラスとスタイルの操作
クラス操作:classList
<div id="box" class="box active"></div>const box = document.querySelector("#box");
// クラスを追加
box.classList.add("highlight");
// クラスを削除
box.classList.remove("active");
// トグル(あれば消す・なければ追加)
box.classList.toggle("hidden");
// 所持確認
if (box.classList.contains("highlight")) {
console.log("ハイライト中");
}昔は element.className を文字列として扱っていましたが、
今は基本 classList を覚える方が圧倒的に楽&安全です。
インラインスタイル:style プロパティ
<div id="box"></div>const box = document.querySelector("#box");
box.style.width = "200px";
box.style.backgroundColor = "red"; // CSSのbackground-color → JSではbackgroundColor
// まとめて指定するならstyle.cssTextもある
box.style.cssText = "width: 200px; height: 100px; background: blue;";- CSSの
background-color→ JSではbackgroundColorのようにキャメルケース - ただし レイアウト系はなるべくCSSで管理し、JSのstyle操作は必要最小限 がベター
要素の生成・挿入・削除
要素を新しく作る:document.createElement
const li = document.createElement("li");
li.textContent = "新しい項目";
li.classList.add("item");挿入:append, prepend, before, after
<ul id="list">
<li class="item">A</li>
</ul>const list = document.querySelector("#list");
const li = document.createElement("li");
li.textContent = "B";
// 子要素の末尾に追加
list.append(li);
// 子要素の先頭に追加
// list.prepend(li);
// 兄弟として前後に追加
// list.before(li);
// list.after(li);append/prependは 子リスト に追加before/afterは 兄弟として 挿入- IE対応などが不要なら、この4つで大体足りることが多いです
従来の appendChild もまだよく使われますが、
モダンなブラウザ前提なら append の方が柔軟(文字列も渡せるなど)。
削除:remove
<div id="box"></div>const box = document.querySelector("#box");
box.remove(); // 親から自分を削除従来は parent.removeChild(child) でしたが、
今は child.remove() がシンプル。
innerHTML vs 明示的な構築
同じことをやる別のパターン:
list.innerHTML += '<li class="item">B</li>';- コードは短くなるが、
- 中身が毎回全部パースされる(パフォーマンス面)
- 既存のイベントリスナなどが消える場合もある
- ユーザー入力をそのまま突っ込むと危険
なので、基本スタイルとしては:
- 構造を組む →
createElement+appendなど innerHTMLは「安全なテンプレート文字列からまとめて描画したいとき」に限定
くらいで考えておくとバランスがいいです。
親子・兄弟ノードの参照
DOM を辿りながら処理したい場面もあるので、代表的なプロパティを。
const item = document.querySelector(".item");
const parent = item.parentElement; // 親要素
const children = parent.children; // 子要素コレクション(HTMLCollection)
const first = parent.firstElementChild; // 最初の子要素
const last = parent.lastElementChild; // 最後の子要素
const prev = item.previousElementSibling; // 前の兄弟要素
const next = item.nextElementSibling; // 次の兄弟要素parentNode/childNodesなど「Nodeレベル」のプロパティもありますが、
最初は Element系 (parentElement,children,ElementChild) を使う方が分かりやすいです。
コメント