JavaScriptでWebページを動かすの導入スライド
JavaScriptでWebページを動かす - DOM とは何か
ブラウザは HTML を読んで木を作る
ブラウザは HTML ファイルを上から読み、書いてある入れ子のとおりに部品を並べた木を組み立てます。この木を DOM と呼びます。JavaScript がページの文字や見た目を変えられるのは、この木を触れるからです。
文字を取り出せるということは、入れ替えることもできるということです。この回はまず、取り出す側から見ていきます。
document と書くと、ブラウザが組み立てた木そのものを指せます。
const title = document.querySelector("h1");
console.log(title.textContent); // 山田はな1 / 4
HTML の入れ子が、そのまま木の段になる
プロフィールサイトの作品ギャラリーを例にします。左の HTML を読み込むと、ブラウザの中には右のような親子関係を持つ木ができます。深く書いた行ほど、木の下の段に入ります。
<body>木のいちばん上<ul id="works">作品一覧の親ブラウザの中にできた木
| 段 | 部品 | 持っている文字 |
|---|---|---|
| 1 | body | — |
| 2 | h1 | 山田はな |
| 2 | ul#works | 自分では持っていない |
| 3 | li | ポスター |
| 3 | li | ロゴ |
囲った ul そのものは文字を書かれていません。文字を書かれているのは、下の段にぶら下がった li 2 つです。ul の textContent を読むと、その li の文字がつながって返ってきます。
作品を 1 つ増やすというのは、この木の 3 段目に li をもう 1 つぶら下げることです。
2 / 4
考えてみよう外しても進めます
JavaScript で h1 の文字を「山田はな(イラストレーター)」に書き換えたあと、パソコンに保存してある HTML ファイルを開くとどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
JavaScriptでWebページを動かす - DOM とは何か
この章で出てくる言葉
DOM の話には似た言葉が並びます。どれも「木のどこを指しているか」の違いだけなので、先に対応を押さえておくと、あとの回が全部読めるようになります。
JavaScript が触れるのは上から 4 つまでです。いちばん下のファイルには届きません。
| 言葉 | 指しているもの |
|---|---|
| DOM | ブラウザが HTML から作った部品の木 |
| 要素 | 木の中の部品 1 つ。h1 や li など |
| document | 木そのもの。ここから要素を探す |
| 親と子 | 外側に書いた要素と、その中に書いた要素 |
| HTML ファイル | 木のもとになる設計図。書き換えても変わらない |
4 / 4
JavaScriptでWebページを動かす - 要素を取得する
木の中から目的の要素を探す
書き換えたい部品を先に見つけないと、何も始まりません。探すのに使うのが querySelector と querySelectorAll です。探しかたの指定には、CSS で使うセレクタの文字列をそのまま渡します。
同じ .work という 1 つの文字列で、当たる相手の数が変わります。次の図解で、その数を自分で数えます。
1 つだけ欲しいときが querySelector、全部欲しいときが querySelectorAll です。
const name = document.querySelector("#profile-name");
console.log(name.textContent); // 山田はな
const works = document.querySelectorAll(".work");
console.log(works.length); // 31 / 4
JavaScriptでWebページを動かす - 要素を取得する
セレクタで当たる相手が変わる
セレクタを 3 つとも押して、当たった行が何行になるか数えてください。
- タグ名だけの p は 5 行、点で始まる .note は 2 行、シャープで始まる #lead は 1 行に当たる
- この当たった行の数が、そのまま querySelectorAll で返ってくる個数になる
- querySelector は同じ当たりかたをしたうえで、いちばん上の 1 行だけを返す
2 / 4
考えてみよう外しても進めます
ページに .work が 1 つも無い状態で document.querySelector(".work") を書くと、変数には何が入りますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
JavaScriptでWebページを動かす - 要素を取得する
探しかたの早見
セレクタの書き分けと、返ってくるものの違いをまとめます。この 2 つの表を行き来できれば、探す作業で迷うことはありません。
querySelectorAll が返すのは配列によく似た並びです。1 つずつ触るときは forEach で回します。
| やりたいこと | 書き方 |
|---|---|
| id で 1 つ探す | querySelector("#profile-name") |
| class で全部探す | querySelectorAll(".work") |
| タグ名で探す | querySelectorAll("li") |
| 親の中だけを探す | querySelectorAll("#works li") |
| 見つからなかったとき | querySelector は null |
| 1 つも当たらなかったとき | querySelectorAll は長さ 0 |
4 / 4
JavaScriptでWebページを動かす - テキストを書き換える
textContent に代入すると画面の文字が変わる
見つけた要素の textContent は、読むだけでなく代入もできます。代入した瞬間に、その部品が画面に出している文字が入れ替わります。
では、書き換わったのはどこまででしょうか。次の図解で、画面と保存してあるファイルを見比べます。
= の右に書いた文字が、そのまま画面の文字になります。
const name = document.querySelector("#profile-name");
console.log(name.textContent); // 山田はな
name.textContent = "山田はな(イラストレーター)";
console.log(name.textContent); // 山田はな(イラストレーター)1 / 4
JavaScriptでWebページを動かす - テキストを書き換える
書き換わるのは画面だけ
手順を最後まで送って、上の画面と下のファイルのどちらが変わるか見てください。
- 画面の文字は こんにちは から ようこそ に変わる
- 下の index.html の行はどの手順でも変わらない。書き換えはファイルまで届かない
- だから読み込み直すと、ファイルに書いてある文字に戻る
2 / 4
考えてみよう外しても進めます
利用者が入力した文字をそのまま innerHTML に代入すると、何が起きうると思いますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
textContent と innerHTML の違い
どちらも中身を差し替えるものですが、受け取った文字の扱いが違います。同じ 1 つの文字列を入れて、画面に何が出るかを見比べます。
box.textContent = input文字として出すbox.innerHTML = inputHTML として読む画面に出るもの
| 書いた先 | 画面に出るもの | タグの扱い |
|---|---|---|
| textContent | <b>新作</b> | ただの文字 |
| innerHTML | 新作 (太字) | 本物の部品になる |
囲った行が、外から来た文字を入れてはいけないほうです。
迷ったら textContent を使います。innerHTML が要るのは、自分で書いた HTML を組み立てて入れるときだけです。
4 / 4
JavaScriptでWebページを動かす - 要素を作って追加する
createElement で新しい部品を作る
作品が 1 つ増えたら、一覧に行を足したくなります。まずは足す部品そのものを createElement で作ります。作った直後の部品は、中身が空の li が 1 つ手元にある状態です。
ここまで書いても、画面には何も出てきません。作った部品はまだ木の外にあるからです。
作った部品にも、いつもどおり textContent で文字を入れられます。
const li = document.createElement("li");
li.textContent = "ロゴデザイン";
console.log(li.textContent); // ロゴデザイン1 / 4
append で木にぶら下げて、はじめて画面に出る
画面に出すには、どの親の下に入れるかを決めて append します。木にぶら下がった瞬間に、その部品は画面の一部になります。
document.createElement("li")部品を作るlist.append(li)木にぶら下げる作品一覧
| 作品名 | いまどこにいるか |
|---|---|
| ポスター | 木の中 (HTML に書いてあった) |
| 名刺 | 木の中 (HTML に書いてあった) |
| ロゴデザイン | 木の中 (append で足した) |
最後の append を消すと、囲った行だけが木の外に残り、画面には出ません。
append は親の子どもの、いちばん最後に足します。先頭に入れたいときは prepend を使います。
2 / 4
考えてみよう外しても進めます
createElement と textContent までは書いて、append を書き忘れました。画面はどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
JavaScriptでWebページを動かす - 要素を作って追加する
部品を足したり外したりする書き方
作る、文字を入れる、木に足すの 3 つが 1 組です。外す側もあわせて覚えておくと、一覧の作り直しまでこの表だけで書けます。
外した部品も、変数に入れておけば同じものをもう一度 append できます。作り直す必要はありません。
| やりたいこと | 書き方 |
|---|---|
| 新しい部品を作る | document.createElement("li") |
| 中に文字を入れる | li.textContent = "ロゴデザイン" |
| 親の最後に足す | list.append(li) |
| 親の先頭に足す | list.prepend(li) |
| 部品を外す | li.remove() |
| 中身を全部空にする | list.textContent = "" |
4 / 4
JavaScriptでWebページを動かす - 配列から一覧を描画する
作品のデータは、オブジェクトの配列で持つ
作品を 3 つ並べるのに、HTML へ li を 3 行書くこともできます。ただ、それだと作品が増えるたびに HTML を書き足すことになります。データは配列で持ち、画面はそこから作ります。
この配列の要素 1 つが、一覧の 1 行になります。要素が 3 つなら 3 行です。
第 7 章で作ったオブジェクトを、第 6 章で使った配列に並べただけです。
const works = [
{ title: "ポスター", year: 2024 },
{ title: "名刺", year: 2025 },
{ title: "ロゴ", year: 2025 },
];
console.log(works.length); // 3
console.log(works[0].title); // ポスター1 / 4
forEach で行を必要なだけ量産する
配列を forEach で 1 周し、その中で部品を作って append します。回った回数だけ行が増えるので、何行作るかを自分で数える必要はありません。
works.forEach要素の数だけ回るdocument.createElement("li")1 周ごとに部品を作るlist.append(li)1 周ごとに木へ足す画面にできる一覧
| 何周目か | work の中身 | 画面に増える行 |
|---|---|---|
| 1 周目 | ポスター / 2024 | ポスター(2024) |
| 2 周目 | 名刺 / 2025 | 名刺(2025) |
| 3 周目 | ロゴ / 2025 | ロゴ(2025) |
囲った 3 周目で配列が終わるので、行も 3 つで止まります。
作る部品は毎周あたらしく作ります。1 つの li を使い回すと、最後の 1 行しか残りません。
2 / 4
考えてみよう外しても進めます
この書き方のまま works に 4 つ目の作品を足すと、画面はどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
描き直すときの手順
並べ替えや絞り込みをすると、一覧を描き直すことになります。空にしてから描くところまでを 1 組で覚えます。
描く先を見つける
document.querySelector("#works")
先に中身を空にする
list.textContent = ""
1 行ぶんを作る
createElement("li") に textContent を入れる
木に足す
list.append(li) を 1 周ごとに
空にする行を忘れると、描き直すたびに古い行の下に新しい行が積み上がっていきます。
4 / 4
JavaScriptでWebページを動かす - クリックに反応する
押されたときにだけ動かす
プロフィールサイトのダークモード切替ボタンは、押されるまで何もしません。こういう処理は、上から順に走らせるのではなく「押されたら呼んでほしい」とブラウザに預けておきます。預ける道具が addEventListener です。
呼ぶ時刻を決めるのはブラウザです。こちらは「これを呼んで」と渡すだけになります。
押されたときに呼んでほしい関数を、名前で預けています。
const button = document.querySelector("#theme-button");
button.addEventListener("click", switchTheme);1 / 4
渡すのは関数そのもの
ここでいちばん多い間違いが、関数名のうしろに () を付けてしまうことです。() を付けると、その行を読んだ時点で関数が呼ばれ、返ってきた値のほうが預けられます。
handleClick);押されたときに呼ばれるhandleClick());その場で呼ばれてしまう2 つの書き方の違い
| 2 番目に書いたもの | ページを開いた直後 | ボタンを押したとき |
|---|---|---|
| handleClick | 何も起きない | 処理が動く |
| handleClick() | すぐに動いてしまう | 何も起きない |
下の行は預けたものが関数ではないので、押しても呼ぶ相手がいません。
第6章で関数を引数として渡したのと同じ形です。呼ぶ側が自分ではない、という点だけが新しくなります。
2 / 4
考えてみよう外しても進めます
ページを開いた瞬間にダークモードへ切り替わってしまいました。addEventListener の 2 番目に何を書いたと考えられますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
JavaScriptでWebページを動かす - クリックに反応する
登録するときに決めること
addEventListener に伝えるのは「どの要素で」「どんな出来事が起きたら」「何を呼ぶか」の 3 つです。書く場所も決まっているので、形で覚えてしまうのが早道です。
この「あとで呼んでもらう関数を預ける」形をコールバックと呼びます。第6章で書いたものが、ここでそのまま効いてきます。
| 決めること | どこにどう書くか |
|---|---|
| どの要素か | button.addEventListener の button |
| どんな出来事か | "click" や "input" などの文字列 |
| 何を呼ぶか | 関数の名前。() は付けない |
| 同じ要素に 2 つ登録 | できる。登録した順に呼ばれる |
| 登録をやめる | removeEventListener に同じ関数を渡す |
4 / 4
JavaScriptでWebページを動かす - event オブジェクト
呼ばれるときに情報が渡ってくる
預けた関数は、ブラウザから呼ばれるときに引数を 1 つ受け取ります。中には、何が起きたのか、どこで起きたのかがまとめて入っています。この引数を event オブジェクトと呼びます。
受け取る側は書かなくてもエラーになりません。必要になったときだけ引数を用意します。
引数の名前は自由ですが、event と書くのが慣習です。
button.addEventListener("click", function (event) {
console.log(event.type); // "click"
console.log(event.target.id); // "theme-button"
});1 / 4
押された要素と、登録した要素
作品一覧のように同じボタンが何個も並ぶ場所では、親の ul に 1 つだけ登録しておく書き方をします。このとき event から読み取れる要素が 2 種類あり、指しているものが違います。
event.target実際に押された要素event.currentTarget登録を書いた要素同じ 1 回のクリックで読み取れるもの
| 読み取るもの | 返る要素 | 何を指しているか |
|---|---|---|
| event.target | button | 押された、いちばん内側の要素 |
| event.currentTarget | ul | addEventListener を書いた要素 |
押す場所を変えると target だけが変わります。currentTarget はいつも同じです。
親に 1 つ登録しておけば、あとから作品が増えても登録し直さずに済みます。
2 / 4
考えてみよう外しても進めます
作品一覧の ul に登録した処理の中で、押された作品の削除ボタンを取り出したいとき、見るのはどちらですか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
JavaScriptでWebページを動かす - event オブジェクト
event からよく読み取るもの
event には多くの情報が入っていますが、入門のうちに使うのはごく一部です。この 5 つを知っていれば、たいていの処理は書けます。
最後の preventDefault が、次のレッスンで問い合わせフォームを扱うときの主役になります。
| 読み取るもの | 入っているもの |
|---|---|
| event.type | 起きた出来事の名前。click や input |
| event.target | 出来事が起きた、いちばん内側の要素 |
| event.currentTarget | addEventListener を書いた要素 |
| event.target.value | 入力欄なら、そのとき入っている文字 |
| event.preventDefault() | ブラウザの決まった動きを止める |
4 / 4
JavaScriptでWebページを動かす - フォーム送信を制御する
送信ボタンは何もしなくても動く
問い合わせフォームの送信ボタンは、JavaScript を 1 行も書かなくても動きます。押すとブラウザが入力内容を送り、ページを読み込み直すからです。自分で受け取って処理したいときは、この決まった動きを先に止めます。
止めた分だけ、送る処理も画面の書き換えも自分で書くことになります。
最初の 1 行で、ブラウザ側の送信を取り消しています。
const form = document.querySelector("#contact-form");
form.addEventListener("submit", function (event) {
event.preventDefault();
console.log("送信を受け取りました");
});1 / 4
止めないと結果が消える
preventDefault を書き忘れても、エラーは出ません。書いた処理もきちんと動きます。そのあとブラウザがページを読み込み直すので、画面に出したはずのものが一瞬で消えるだけです。
// event.preventDefault();ここを止めたままだとmessageArea.textContent書いた結果が残らない送信ボタンを押したあと
| preventDefault | 画面の動き | 書いた処理の結果 |
|---|---|---|
| 書かない | ページが読み込み直される | 一瞬出て消える |
| 書く | ページはそのまま | 画面に残る |
上の行も処理は動いています。動いた直後にページごと作り直されているだけです。
「押しても何も起きない」と感じたときは、まずこの 1 行があるかどうかを疑います。
2 / 4
考えてみよう外しても進めます
preventDefault を書き忘れたフォームで送信ボタンを押すと、自分で書いた処理はどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
ブラウザが勝手にやってくれる動き
決まった動きを持っているのは submit だけではありません。JavaScript で受け持ちたいときは、どれも同じ preventDefault で止めます。
form の submit
入力内容を送ってページを読み込み直す
a のクリック
href に書いた先へ移動する
チェックボックスのクリック
チェックが入る、または外れる
止め方は、受け取った event で preventDefault() を呼ぶだけです。そのまま移動してほしいときは書きません。何でも止めればよいわけではありません。止めた動きは、代わりに自分で用意することになります。
4 / 4
JavaScriptでWebページを動かす - setTimeout と setInterval
あとで動かす予約をする
クリックのように、きっかけを待つのではなく、時間で動かしたい処理もあります。ヘッダーの時計がその例です。setTimeout は 1 回だけ、setInterval は同じ間隔でくり返し呼んでくれます。
預けかたは addEventListener と同じです。違うのは、待つのが操作ではなく時間だという点だけになります。
どちらも渡すのは関数の名前です。ここでも () は付けません。
setTimeout(showGreeting, 3000); // 3 秒後に 1 回だけ
setInterval(updateClock, 1000); // 1 秒ごとにくり返し1 / 4
1000 は「ぴったり 1 秒後」ではない
指定した時間は「最短でこれだけ待つ」という意味です。その時刻にほかの処理が走っていれば、順番を待ってから呼ばれます。ずれは少しずつ積もっていきます。
new Date()呼ばれるたびに今の時刻を取り直すupdateClock, 10001000 ミリ秒ごとに呼ぶconst timerId止めるための番号1000 と書いたときの実際
| 呼ばれた回 | 予定 | 実際 |
|---|---|---|
| 1 回目 | 1.000 秒 | 1.002 秒 |
| 2 回目 | 2.000 秒 | 2.007 秒 |
| 3 回目 | 3.000 秒 | 3.015 秒 |
呼ばれる時刻は少しずつ遅れます。数えた回数から時刻を計算すると、その分ずれていきます。
この時計がずれないのは、呼ばれるたびに new Date() で今の時刻を取り直しているからです。
2 / 4
考えてみよう外しても進めます
setInterval で動かしている時計を、そのまま止めずに放っておくとどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
JavaScriptでWebページを動かす - setTimeout と setInterval
予約と取り消しは対になっている
予約すると番号が返ってきます。その番号が、あとで取り消すときの手がかりになります。番号を変数に入れておかないと、止める手立てが無くなります。
最後の行が、時間指定が「最短」でしかないことを表しています。0 と書いても、今動いている処理が終わるまでは呼ばれません。
| やりたいこと | 書き方 |
|---|---|
| 3 秒後に 1 回だけ | setTimeout(fn, 3000) |
| 1 秒ごとにくり返す | setInterval(fn, 1000) |
| 1 回の予約を取り消す | clearTimeout(id) |
| くり返しを止める | clearInterval(id) |
| できるだけ早く | setTimeout(fn, 0)。それでも今すぐではない |
4 / 4
JavaScriptでWebページを動かす - localStorage で保存する
変数はページを閉じると消える
ダークモードに切り替えても、ページを開き直すと元に戻ってしまいます。変数はページが読み込み直された時点で作り直されるからです。次に来たときも同じ設定で始めたいなら、ブラウザ側に預けておきます。その預かり所が localStorage です。
預けられるのは文字列だけです。ここが最初のつまずきどころになります。
名前を付けて預け、同じ名前で取り出す。これだけの仕組みです。
localStorage.setItem("theme", "dark");
const theme = localStorage.getItem("theme"); // "dark"1 / 4
文字列にしてから預ける
設定が増えると、テーマと文字サイズをまとめて 1 つのオブジェクトで持ちたくなります。オブジェクトはそのまま預けられないので、JSON.stringify で文字列に直してから渡し、取り出したら JSON.parse で戻します。
JSON.stringify(settings)オブジェクトを文字列にJSON.parse文字列をオブジェクトに戻すsaved.fontSize + 2戻したので計算できる同じ設定が形を変えていく
| 段階 | 中身 | 型 |
|---|---|---|
| 元のオブジェクト | { theme: "dark", fontSize: 16 } | object |
| stringify した文字列 | {"theme":"dark","fontSize":16} | string |
| parse で戻したもの | { theme: "dark", fontSize: 16 } | object |
預かってもらえるのは真ん中の形だけです。行きと帰りで 1 回ずつ変換を挟みます。
stringify を忘れてオブジェクトを渡すと、"[object Object]" という文字列に変換されて預かられます。エラーは出ません。
2 / 4
考えてみよう外しても進めます
JSON.stringify を忘れて、オブジェクトをそのまま setItem に渡すとどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
3 / 4
JavaScriptでWebページを動かす - localStorage で保存する
できること、してはいけないこと
名前と値の組を預かってくれる、それだけの仕組みです。境目をはっきりさせておくと、あとで悩まずに済みます。
この教材のプレビュー枠は保存が許されていない環境なので、localStorage を呼ぶとその場でエラーになります。仕組みだけ持ち帰って、自分のブラウザで試してください。
| やりたいこと | 書き方と結果 |
|---|---|
| 文字列を預ける | localStorage.setItem("theme", "dark") |
| 取り出す | localStorage.getItem("theme")。無ければ null |
| 1 つ消す | localStorage.removeItem("theme") |
| オブジェクトを預ける | JSON.stringify と JSON.parse を挟む |
| 数値を取り出す | 文字列で返るので Number() で数値に戻す |
| パスワードを預ける | してはいけない。誰でも読み出せる |
4 / 4
