JavaScript:DOM操作の基礎
1学習の目的
- HTMLの要素を取得し、文字・見た目・構造を書き換えられるようになる。console.log の中だけだった処理が、実際の画面に反映されるようになる。
- 要素の作成と追加を身につけ、データから画面を組み立てられるようになる。ここからJavaScriptが「Webページを動かす言語」になる。
2基礎解説
| やりたいこと | 書き方 | 備考 |
|---|---|---|
| 1つ取得する | document.querySelector("#id") | 無ければ null |
| 全部取得する | document.querySelectorAll(".cls") | 複数の要素が返る |
| 文字を書き換える | el.textContent = "新しい文字" | 安全。基本はこちら |
| 要素を作って足す | createElement / appendChild | 画面に組み立てる |
- DOMは「HTMLをJavaScriptから触れるようにしたもの」。document がページ全体の入り口になる。
- 取得は querySelector(1つ)と querySelectorAll(全部)。CSSと同じ書き方で、#id .class tag が使える。
- 文字を入れるなら textContent。innerHTML はHTMLとして解釈されるため、ユーザーの入力をそのまま入れると不正なスクリプトを埋め込まれる危険がある。
- 見た目は classList.add / remove / toggle で切り替える。style を直接いじるより、CSSにクラスを定義しておくほうが管理しやすい。
- 要素が見つからないと null が返り、そのまま操作すると TypeError で止まる。IDのつづりミスが原因のことが多い。
現場使用例:ボタンを押したら文字を変える、入力内容を画面に反映、APIで取得した一覧をカードとして並べる、エラー時に赤いクラスを付ける。
<!-- HTML側 -->
<h1 id="title">こんにちは</h1>
<ul id="list"></ul>
// JavaScript側
const title = document.querySelector("#title");
title.textContent = "変更しました"; // 文字を書き換え
title.classList.add("highlight"); // クラスを追加
// 要素を作って画面に追加する
const list = document.querySelector("#list");
const items = ["りんご", "みかん"];
for (const item of items) {
const li = document.createElement("li");
li.textContent = item;
list.appendChild(li);
}
// 全部まとめて操作する
document.querySelectorAll("li").forEach(li => {
li.classList.add("item");
});
3基本ドリル(10問)
#title の textContent が 変更しました
document.querySelector("#title") で取得し、.textContent に代入する。
const title = document.querySelector("#title");
title.textContent = "変更しました";
取得 → 操作がDOM操作の基本の2手。querySelector はCSSと同じセレクタが使えるので、覚えることが少なくて済む。
A. undefined B. null C. 空文字 D. エラーになる 選択★☆☆無料
解答 == B
「無い」を明示的に表す値。
B
null のまま .textContent を書くと TypeError で止まる。IDのつづりミスや、スクリプトがHTMLより先に読まれた場合に起きる。
出力 == 3
document.querySelectorAll(".item") で全部取れる。件数は .length。
const items = document.querySelectorAll(".item");
console.log(items.length);
querySelector は1つ、querySelectorAll は全部。All のほうは要素が0個でも null にはならず、空のリストが返る。
const el = document.querySelector("#msg");
el.____ = "こんにちは";
#msg の textContent が こんにちは
文字(テキスト)の中身を表すプロパティ。HTMLとして解釈されない安全なほう。
textContent
innerHTML との使い分けが重要。ユーザーが入力した文字をそのまま innerHTML に入れると、悪意あるスクリプトが動く危険がある。
A. el.class.add("on") B. el.classList.add("on") C. el.addClass("on") D. el.class = "on" 選択★☆☆無料
解答 == B
クラスの「一覧」を操作するプロパティ。
B
classList には add / remove / toggle / contains がある。className = "on" と書くと既存のクラスを全部消して置き換えるので注意。
active が付き hidden が消えている
classList.add() と classList.remove() を続けて呼ぶ。
const box = document.querySelector("#box");
box.classList.add("active");
box.classList.remove("hidden");
見た目の切り替えはクラスで行うのが基本。色や大きさはCSS側に書いておき、JavaScriptはクラスを付け外しするだけにすると役割が分かれて管理しやすい。
const list = document.querySelector("#list");
const li = document.____("li");
li.textContent = "新しい項目";
list.appendChild(li);
#list に li が1つ追加され textContent が 新しい項目
「要素を作る」という意味のメソッド。document から呼ぶ。
createElement
作る → 中身を入れる → 追加するの3手が要素追加の定型。createElement しただけでは画面に出ず、appendChild で初めて表示される。
A. innerHTML B. textContent C. どちらも同じ D. どちらも危険 選択★★☆無料
解答 == B
HTMLとして解釈されるかどうか。
B
innerHTML は文字列をHTMLとして解釈するため、<script> などを埋め込まれる危険がある(XSS)。入力された値を表示するときは必ず textContent。
#list の子要素が3つで textContent が りんご/みかん/ぶどう
for...of で回し、毎回 createElement → textContent → appendChild を行う。
const list = document.querySelector("#list");
const items = ["りんご", "みかん", "ぶどう"];
for (const item of items) {
const li = document.createElement("li");
li.textContent = item;
list.appendChild(li);
}
データから画面を組み立てる——これがDOM操作の本質。配列の中身が変わればそのまま画面が変わるので、HTMLを手で書く必要がなくなる。
すべての .item に done が付いている
querySelectorAll の結果は forEach で回せる。
document.querySelectorAll(".item").forEach((el) => {
el.classList.add("done");
});
querySelectorAll の結果は forEach で回せるが、map や filter は使えない場合がある。配列として扱いたいときは [...document.querySelectorAll(…)] と展開する。
4実践シナリオ(5問)
#list に li が2つ、textContent が ノートPC:128,000円 / マウス:3,200円
for...of で回し、テンプレートリテラルで文字を組み立ててから textContent に入れる。
const products = [
{ name: "ノートPC", price: 128000 },
{ name: "マウス", price: 3200 },
];
const list = document.querySelector("#list");
for (const p of products) {
const li = document.createElement("li");
li.textContent = `${p.name}:${p.price.toLocaleString()}円`;
list.appendChild(li);
}
JS07で学んだオブジェクトの配列が、そのまま画面の一覧になった。データと表示が分離しているので、products を差し替えるだけで表示が変わる。
3つの li に pass/fail/pass が付いている
createElement のあと classList.add() で条件に応じたクラスを付ける。三項演算子が使える。
const scores = [82, 45, 91];
const list = document.querySelector("#list");
for (const s of scores) {
const li = document.createElement("li");
li.textContent = `${s}点`;
li.classList.add(s >= 60 ? "pass" : "fail");
list.appendChild(li);
}
データの中身によって見た目を変える典型パターン。色の指定はCSS側に .pass { color: green; } と書いておけば、JavaScriptは判定に専念できる。
出力2行。要素が見つかりません / 更新しました
取得結果が null かどうかを if で判定する。関数にすると2回書かずに済む。
const update = (selector, text) => {
const el = document.querySelector(selector);
if (el === null) {
console.log("要素が見つかりません");
return;
}
el.textContent = text;
console.log("更新しました");
};
update("#missing", "新しい文字");
update("#title", "新しい文字");
取得できたか確認してから操作するのが安全な書き方。null チェックを省くと Cannot set properties of null というエラーでページ全体が止まる。
#tbody に tr が2つ、各 tr に td が2つ
tr を作り、その中に td を2つ appendChild してから、tr を tbody に appendChild する。入れ子の構造をそのままコードにする。
const users = [
{ name: "田中", dept: "営業" },
{ name: "鈴木", dept: "開発" },
];
const tbody = document.querySelector("#tbody");
for (const u of users) {
const tr = document.createElement("tr");
const tdName = document.createElement("td");
tdName.textContent = u.name;
tr.appendChild(tdName);
const tdDept = document.createElement("td");
tdDept.textContent = u.dept;
tr.appendChild(tdDept);
tbody.appendChild(tr);
}
HTMLの入れ子構造がそのままコードの入れ子になる。tr に td を入れてから tbody に入れる——この順序を守れば、どんな複雑な構造でも組み立てられる。
#list の子要素が2つになり、出力 == 2件を表示しました
中身を空にするには list.innerHTML = "" と書く。そのあと新しい要素を追加していく。
const list = document.querySelector("#list");
const newItems = ["A", "B"];
list.innerHTML = "";
for (const item of newItems) {
const li = document.createElement("li");
li.textContent = item;
list.appendChild(li);
}
console.log(`${newItems.length}件を表示しました`);
再描画は「全部消してから作り直す」のが最も簡単で確実。innerHTML = "" は空文字を入れるだけなので、この用途ならXSSの心配もない。差分だけ更新する方法もあるが、複雑になるので規模が大きくなってから考えればよい。
5仕上げ課題
const products = [
{ name: "ボールペン", stock: 120 },
{ name: "ノート", stock: 8 },
{ name: "消しゴム", stock: 45 },
{ name: "ファイル", stock: 3 },
];
const threshold = 10;
HTMLには #list(ul)と #summary(p)があるものとする。
仕様:
① #list の中身をいったん空にする
② 各商品について li を作り、文字は「ボールペン:120個」の形式
③ 在庫が threshold 未満の商品には "low" クラスを付け、文字の末尾に「 ⚠発注」を足す
④ すべての li に "item" クラスを付ける
⑤ #summary に「全4品目 / 要発注2品目 / 総在庫176個」と表示する
⑥ 最後に「描画完了:4件」と出力する
期待される結果:li が4つ作られ、うち2つに low クラスが付く。summary に集計が入る。
li 4つ・low 2つ・summary の文言・出力1行
合計は reduce、要発注の件数は filter で数えられる(JS09)。li の文字は三項演算子でマークを足す。クラスは classList.add("item") を全件に、条件付きで "low" を追加する。
const products = [
{ name: "ボールペン", stock: 120 },
{ name: "ノート", stock: 8 },
{ name: "消しゴム", stock: 45 },
{ name: "ファイル", stock: 3 },
];
const threshold = 10;
const list = document.querySelector("#list");
const summary = document.querySelector("#summary");
list.innerHTML = "";
for (const p of products) {
const li = document.createElement("li");
const isLow = p.stock < threshold;
li.textContent = `${p.name}:${p.stock}個${isLow ? " ⚠発注" : ""}`;
li.classList.add("item");
if (isLow) {
li.classList.add("low");
}
list.appendChild(li);
}
const lowCount = products.filter((p) => p.stock < threshold).length;
const totalStock = products.reduce((sum, p) => sum + p.stock, 0);
summary.textContent = `全${products.length}品目 / 要発注${lowCount}品目 / 総在庫${totalStock}個`;
console.log(`描画完了:${products.length}件`);
ここまで console.log に出していた在庫レポートが、ついに画面になった。JS06・JS07・JS09で何度も書いた同じデータが、今度はHTMLとして組み上がっている。
重要なのはデータと表示が分離していることだ。products の中身を変えるだけで画面が変わり、色やレイアウトを変えたければCSSの .low を直すだけでよい。JavaScriptは「何を表示するか」だけを担当し、「どう見せるか」はCSSに任せる——この分担が、大きなWebアプリでも破綻しない設計の基本になる。
ただし今の画面は一度描いたら終わりで、ユーザーが何をしても反応しない。次章のイベント処理を学ぶと、ボタンを押す・文字を入力するといった操作に応じて画面が動くようになる。