目次を開く(全 17 章)
01学習レベル:基礎
JavaScript の置き方
HTML から読み込むだけです。defer を付けて <head> に置くのが今の定番。これで「HTML を全部読み終えてから実行」されるので、要素が見つからないエラーを防げます。
<head>
<script src="assets/javascripts/main.js" defer></script>
</head>
書いた結果を確かめるには console.log() を使います。ブラウザで F12(Mac は ⌘ + Option + I)を押すと開発者ツールが開き、「コンソール」タブに出力されます。これがいちばん手軽なデバッグの道具です。
console.log('こんにちは、JavaScript');
console.log(1 + 2);
console.log('今日の日付:', new Date().toLocaleDateString('ja-JP'));
このページでは、コンソールの代わりに、コードの下の「実行結果」の枠へ出力しています。
main.js を defer で読み込む じゃんけんの defer と即時関数 <script> console.log()
02学習レベル:基礎
変数 — const と let
変数は、値に名前を付けて使う仕組みです。最初は名前の付いた箱のようなものと考えて構いません。
宣言には、まず const を使います。あとで値を入れ替える必要があるときだけ let を使います。
const applePrice = 100; // この買い物では、りんご 1 個 100 円
let appleCount = 2; // 買う個数はあとで変わる
appleCount = appleCount + 1; // もう 1 個買う
console.log(applePrice * appleCount); // 300
appleCount += 1; // 「appleCount = appleCount + 1」の短い書き方
console.log(applePrice * appleCount); // 400
この例では、買う個数は 2 個、3 個、4 個と変わります。一方、りんご 1 個の値段は、この買い物の計算中ずっと 100 円です。そこで、変わる個数は let、変えない値段は const で宣言します。
const にすると、後のコードでうっかり applePrice を書き換えようとしてもエラーで気付けます。コードが長くなっても、合計を計算するときには「りんご 1 個 100 円」という前提を信頼できます。単にエラーを避けるために let に変えると、その前提を守れなくなります。変える必要のない値を変えられないようにするのは「不変性」を大切にする書き方の一例です。
後で学ぶ配列やオブジェクトでは、const で宣言しても項目を追加・変更できます。const が禁止するのは、その変数名に別の配列やオブジェクトを代入し直すことです。今は「再代入しないなら const」を押さえておけば十分です。
var はブロック { } を無視して外まで漏れ出し、同じ名前で何度でも宣言できてしまいます。
const / let はブロックの中だけで有効なので、
名前の有効範囲が分かりやすくなります。名前は applePrice のように、何を表すか分かる英単語で付けます。
03学習レベル:基礎
読みやすいコード — 名前と書式
コードは、あとで読む自分にも伝わるように書きます。最初は名前を具体的にする・計算を分ける・書式を揃えるの 3 つを意識しましょう。
- 名前を具体的にする —
xやnumberより、bookPriceやbookCountなら何の値か分かります。 - 途中の値にも名前を付ける — 合計を一度に計算する代わりに、本の代金を
booksPriceに入れると、計算の順序を追えます。 - 空白と改行を揃える —
bookPrice * bookCountのように演算子の前後を空け、計算のまとまりごとに行を分けます。
const bookPrice = 500;
const bookCount = 3;
const deliveryFee = 200;
const booksPrice = bookPrice * bookCount;
const totalPrice = booksPrice + deliveryFee;
console.log(totalPrice); // 1700
booksPrice は本だけの代金、totalPrice は送料も含めた代金です。名前を付けて分けると、あとから送料の計算を変えるときも、どこを直すか分かります。
コメントは、式を読めば分かる計算の説明より、「なぜこの決まりにしたか」を残すと役立ちます。関数や条件が長くなったときの書き方は、JS 実践編でも扱います。
04学習レベル:基礎
データ型
データ型は、値の種類を表します。たとえば、商品の値段は数値、商品名は文字列です。まずは、よく使う次の種類を見てみましょう。
| 種類 | 例 | 何を表すか |
|---|---|---|
数値(number) | 1280、3.5 | 値段や個数などの数字。JavaScript では整数も小数も同じ数値型です。 |
文字列(string) | 'りんご' | 名前や文章。数字に見える '1280' も、引用符で囲めば文字列です。 |
真偽値(boolean) | true、false | 「はい/いいえ」のような二択。たとえば、お店が営業中かどうか。 |
null | null | 「値がない」と意図して示す値。 |
undefined | undefined | まだ値を入れていないときなどに現れる値。 |
配列(array) | ['赤', '青'] | 複数の値を順番に並べたもの。 |
オブジェクト(object) | { name: '山田' } | 名前を付けた情報をまとめたもの。 |
数値と文字列は別の種類です。たとえば 1280 + 1 は 1281 ですが、'1280' + 1 は文字をつなげた '12801' になります。値の種類を調べるには typeof を使います。配列はオブジェクトの一種なので、typeof の結果は 'object' です。
const text = 'あいうえお'; // 文字列 string
const price = 1280; // 数値 number(整数も小数も同じ)
const isOpen = true; // 真偽値 boolean
const nothing = null; // 「空」だと明示した状態
let notSet; // undefined(まだ入れていない)
const list = ['赤', '青', '緑']; // 配列 array
const user = { name: '山田', age: 30 }; // オブジェクト object
console.log(typeof text, typeof price, typeof isOpen);
console.log(typeof nothing, typeof notSet);
console.log(list.length, list[0]);
console.log(user.name, user['age']);
typeof null が 'object' になるのは JavaScript の古い不具合で、直せないまま残っています。
05学習レベル:基礎
演算子の種類と優先順位
演算子は、計算・比較・代入などに使う記号です。種類ごとに見ていきましょう。
算術演算子 — 数値を計算する
この表は、上ほど計算の優先順位が高い順です。同じ優先順位の * と / などは、通常は左から計算します。
| 記号 | 意味 | 例 |
|---|---|---|
( ) | 囲んだ部分を先に計算 | (2 + 3) * 4 → 20 |
** | 冪乗(べきじょう) | 2 ** 3 → 8 |
* / % | かける・割る・割った余り | 10 % 3 → 1 |
+ - | 足す・引く | 2 + 3 * 4 → 14 |
** を続けた場合は右からまとまります(2 ** 3 ** 2 は 2 ** (3 ** 2))。計算の順序をはっきり示したいときは ( ) を使いましょう。
インクリメント・デクリメント演算子 — 1 ずつ増減する
++ は値を 1 増やし、-- は 1 減らします。値を書き換えるので、let で宣言した変数などに使います。
| 書き方 | 変数の変化 | 式として使ったときの値 |
|---|---|---|
count++ | 1 増える | 増える前の値(後置) |
count-- | 1 減る | 減る前の値(後置) |
++count | 1 増える | 増えた後の値(前置) |
--count | 1 減る | 減った後の値(前置) |
単独で count++; と書くなら、count += 1; と同じように 1 増えます。console.log(count++); のように式の中で使うと、後置は増える前、前置は増えた後の値を渡す点に注意してください。優先順位は後置が前置より高く、どちらも ** より高いです。
比較演算子 — 値を比べる
比較の結果は true または false です。この表も優先順位の高い順です。
| 記号 | 意味 | 例 |
|---|---|---|
< > <= >= | より小さい・より大きい・以下・以上 | 5 >= 5 → true |
=== !== | 型と値が等しい・どちらかが違う | 1 === '1' → false |
等しいかどうかを調べるときは === と !== を使います。他の言語で == に慣れている人も注意してください。JavaScript の == は型を自動で変換するため、1 == '1' も true になります。この教材では == を使わず、=== を使います。
論理演算子 — 条件を組み合わせる
優先順位は !、&&、|| の順です。分かりにくいときは ( ) で条件をまとめます。
| 記号 | 意味 | 例 |
|---|---|---|
! | 否定。「はい」と「いいえ」を反転する | !true → false |
&& | かつ。両方の条件を満たす | true && false → false |
|| | または。どちらかの条件を満たす | true || false → true |
代入演算子と複合代入演算子
= は右の値を左の変数に代入します。+= などの複合代入演算子(自己代入演算子)は、今の値を使って計算し、その結果を同じ変数に代入します。どちらも let で宣言した変数など、再代入できる場所に使います。
| 記号 | 意味 | 例 |
|---|---|---|
= | 代入 | count = 3 |
**= | 冪乗して代入 | count **= 2 は count = count ** 2 |
*= /= %= | かける・割る・余りを求めて代入 | count *= 2 は count = count * 2 |
+= -= | 足す・引いて代入 | count += 1 は count = count + 1 |
**= や += の行は、対応する計算の順に並べています。代入演算子どうしの優先順位は同じです。
// 算術演算子と優先順位
console.log(3 + 4, 10 - 4, 6 * 7); // 7 6 42
console.log(10 / 3); // 3.333…(整数どうしでも小数になる)
console.log(10 % 3); // 1(余り)
console.log(2 ** 10); // 1024(冪乗)
console.log(2 + 3 * 4); // 14(かけ算が先)
console.log((2 + 3) * 4); // 20(かっこ内が先)
// + は文字列の連結にもなる。数値と混ぜると文字列になってしまう
console.log('合計' + 100, '1' + 1, '1' - 1); // 合計100 / 11 / 0
// 大小の比較
console.log(2 < 5, 5 > 5, 5 >= 5, 5 <= 4); // true false true false
// 等しい/等しくない。型まで見るこの 2 つを使う
console.log(1 === 1, 1 === '1'); // true false
console.log(1 !== 2, 1 !== '1'); // true true
console.log(1 == '1', 1 != '1'); // true false ← 型を無視するので危険
// 組み合わせる
const age = 20;
console.log(age >= 18 && age < 65); // かつ
console.log(age < 18 || age >= 65); // または
console.log(!(age >= 18)); // 否定
// 代入と複合代入(値を変えるので let を使う)
let count = 2;
count = 3;
count += 1; // count = count + 1 と同じ
console.log(count); // 4
// 前置と後置で、表示される値が違う
console.log(count++); // 4(表示してから 5 に増える)
console.log(count); // 5
console.log(--count); // 4(4 に減ってから表示する)
// 偽とみなされる値(falsy)の代表例
for (const value of [false, 0, '', null, undefined, NaN]) {
console.log(value, '→', Boolean(value));
}
// ?? は「null か undefined のときだけ」右を使う
const input = 0;
console.log(input || 10); // 10 ← 0 も偽なので置き換わってしまう
console.log(input ?? 10); // 0 ← こちらが意図どおり
// ?. は途中が無ければ undefined を返す(エラーで止まらない)
const data = { user: { name: '山田' } };
console.log(data.user?.name);
console.log(data.company?.name);
文字列に値を入れる — テンプレートリテラル
文字列を `(バッククォート)で囲むと、${...} の中に変数や計算式を書けます。
const maxCount = 5;
// + で文字列をつなぐ書き方
const messageWithPlus = maxCount + ' 件まで表示できます';
// バッククォートなら、文章の中に値を入れられる
const message = `${maxCount} 件まで表示できます`;
console.log(message); // 5 件まで表示できます
console.log(messageWithPlus === message); // true(どちらも同じ文字列)
const applePrice = 100;
const appleCount = 3;
const totalMessage = `合計は ${applePrice * appleCount} 円です`;
console.log(totalMessage); // 合計は 300 円です
messageWithPlus と message は同じ文字列になります。+ で文章を分けてつなぐより、${...} で値を差し込むほうが読みやすく、値が増えても書きやすいです。
06学習レベル:基礎
選択 — 条件によって処理を分ける
プログラムは上から順に処理を進めます。これを逐次といいます。条件によって進む道を変えるのが選択、同じ処理を繰り返すのが反復です。この 3 つは、構造化プログラミングの基本です。まずは選択を見てみましょう。
if / else if / else
if は条件が true のときに処理を実行します。else if で別の条件を調べ、どれにも当てはまらなければ else に進みます。else if と else は必要なときだけ書きます。
// 条件を順に調べ、成績を決める
const score = 78;
let grade;
if (score >= 90) {
grade = '優';
} else if (score >= 70) {
grade = '良';
} else {
grade = '可';
}
console.log(grade); // 良
switch
switch は、曜日のように一つの値で処理を分けるときに使います。case に一致したところから実行し、break で抜けます。一致するものがなければ default に進みます。
const day = 'sat';
let dayLabel;
switch (day) {
case 'sat':
case 'sun':
dayLabel = '休み';
break; // break がないと次の処理にも進む
default:
dayLabel = '平日';
}
console.log(dayLabel); // 休み
発展:値の対応表で選ぶ
選んだ飲み物から値段を取り出すだけなら、switch の代わりにオブジェクトを対応表として使えます。キー(apple など)と値段の組を並べ、drinkPrices[selectedDrink] で選んだ値を取り出します。
// 飲み物の名前と値段を対応させる
const drinkPrices = {
apple: 100,
orange: 150,
coffee: 200
};
const selectedDrink = 'orange';
// 対応する値が undefined なら 0 を使う
const price = drinkPrices[selectedDrink] ?? 0;
console.log(`${selectedDrink} の値段は ${price} 円です`); // orange の値段は 150 円です
?? は左側が undefined または null のときだけ、右側の 0 を使います。たとえば値段が 0 の商品は、そのまま 0 として扱えます。値を選ぶだけなら対応表が簡潔です。フォームなどから選択値を受け取る場合は、用意したキーの一つかも確認します。選択肢ごとに異なる処理を実行する場合は if や switch を使います。
07学習レベル:基礎
反復 — 処理を繰り返す
同じ処理を繰り返すことを反復といいます。回数が決まっているか、条件で続けるか、配列の要素を順に取り出すかで書き方を選びます。
| 構文 | 使いどころ | 条件を調べるタイミング |
|---|---|---|
while | 条件を満たす間、繰り返す | 処理の前。最初から条件が偽なら 0 回 |
do...while | 少なくとも 1 回は実行したい | 処理の後。最初の 1 回は必ず実行 |
for | 回数を数えながら繰り返す | 各回の処理の前 |
for...of | 配列などの値を一つずつ取り出す | 次の値があるかを各回の前に調べる |
while と do...while では、繰り返すうちに条件が変わるようにします。変わらなければ処理が終わらないことがあります。for は「最初の値・続ける条件・毎回の更新」をまとめて書きます。
// while: 条件を先に調べる
let remaining = 3;
while (remaining > 0) {
console.log(`残り ${remaining} 回`);
remaining -= 1;
}
// do...while: 処理をしてから条件を調べる
let attemptCount = 0;
do {
attemptCount += 1;
console.log(`${attemptCount} 回目の試行`);
} while (attemptCount < 2);
// for: 回数を数える
for (let number = 1; number <= 3; number += 1) {
if (number === 2) continue; // この回だけ飛ばす
console.log(`${number} 回目`);
}
// for...of: 配列の値を順に取り出す
for (const fruit of ['りんご', 'みかん']) {
console.log(fruit);
}
break は繰り返しを終え、continue はその回の残りを飛ばして次へ進みます。配列の番号も必要な場合は、後の配列の節で entries() を紹介します。
08学習レベル:基礎
配列とよく使うメソッド
配列は「順番に並んだ値の入れ物」です。要素を順に処理するときは、前の節で学んだ for...of を基本にします。ここでは番号が必要な場合と、既存のコードで見かける forEach も確認します。
const fruits = ['りんご', 'みかん', 'ぶどう'];
// 1. for 文:番号(添字)を自分で進める。いちばん基本の形
for (let i = 0; i < fruits.length; i++) {
console.log(i, fruits[i]);
}
// 2. for...of:値を一つずつ取り出す
for (const fruit of fruits) {
console.log(fruit);
}
// 番号も必要なら entries() と組み合わせる
for (const [index, fruit] of fruits.entries()) {
console.log(`${index + 1} 番目:${fruit}`);
}
// 3. forEach:配列の各要素に関数を実行する書き方
fruits.forEach((fruit, index) => {
console.log(`${index + 1} 番目:${fruit}`);
});
// for と for...of は途中で抜けられる
for (const fruit of fruits) {
if (fruit === 'みかん') {
break; // ここで終わり
}
console.log('見つける前:', fruit);
}
| 書き方 | 向いている場面 | 途中で抜けられる |
|---|---|---|
for | 番号を使う。逆順や 2 つ飛ばしなど、進み方を細かく決めたいとき | できる(break) |
for...of | 中身を順に見たいとき。番号は entries() で得られる。ふだんはこれ | できる(break) |
forEach | 配列の各要素に関数を実行する。番号は第 2 引数で受け取れる | できない |
forEach は配列のメソッドで、渡した関数を要素ごとに呼び出します。break や continue は使えません。関数内の return はその回の関数を終えるだけで、次の要素の処理は続きます。また、forEach 自体の返り値は undefined なので、map のように結果をつなげて使うことはできません。まずは for...of を使い、forEach は既存のコードを読めるようにしておけば十分です。
名前の付いた処理に置き換える
回すことに慣れたら、次はメソッドです。「変換するなら map」「絞り込むなら filter」のように
やりたいことが名前で分かるので、コメントが要らなくなります。まずは map / filter / find / reduce の 4 つ。
const items = [
{ name: 'えんぴつ', price: 120, stock: 5 },
{ name: 'ノート', price: 280, stock: 0 },
{ name: '消しゴム', price: 90, stock: 12 }
];
// map:全部を別の形に変える(個数は変わらない)
console.log(items.map((item) => item.name));
// filter:条件に合うものだけ残す
console.log(items.filter((item) => item.stock > 0).map((i) => i.name));
// find:最初の 1 つを探す(無ければ undefined)
console.log(items.find((item) => item.price < 100));
// reduce:ひとつの値にまとめる(合計・最大など)
const total = items.reduce((sum, item) => sum + item.price, 0);
console.log('合計', total);
// some / every:ひとつでも / 全部が
console.log(items.some((i) => i.stock === 0), items.every((i) => i.price > 50));
// 元の配列を壊さない並べ替え(toSorted)
const byPrice = items.toSorted((a, b) => a.price - b.price);
console.log(byPrice.map((i) => i.price), items.map((i) => i.price));
// at(-1) で最後の要素
console.log(['春', '夏', '秋', '冬'].at(-1));
// includes / join / スプレッド
console.log(['赤', '青'].includes('青'), ['赤', '青'].join(' と '));
console.log([...[1, 2], ...[3, 4]]);
sort() は元の配列を並べ替えてしまいます。toSorted() は新しい配列を返すので、元を壊しません。
09学習レベル:基礎
オブジェクト
「名前と値」の組を束ねたものです。関連するデータをひとまとめにするときに使います。
const user = { name: '山田', age: 30, city: '大阪' };
// 分割代入:必要なものだけ取り出す
const { name, city } = user;
console.log(name, city);
// 名前を変えて取り出す/既定値を付ける
const { name: userName, country = '日本' } = user;
console.log(userName, country);
// 配列も同じように取り出せる
const [first, second] = ['金', '銀'];
console.log(first, second);
// スプレッドで複製しつつ一部を上書き(元は壊さない)
const updated = { ...user, age: 31 };
console.log(updated, user.age);
// 省略記法:変数名とキー名が同じなら 1 回でよい
const age = 31;
console.log({ name, age });
// 一覧として回す
for (const [key, value] of Object.entries(user)) {
console.log(`${key}:${value}`);
}
console.log(Object.keys(user), Object.values(user));
10学習レベル:基礎
関数
関数は、処理をまとめて必要なときに呼び出せるようにしたものです。まず基本の書き方を見てから、関数を渡したり、値を覚えたりする使い方へ進みます。
基本形 — function とアロー関数
name は関数が受け取る値の名前です。greet('山田') の '山田' が渡す値(引数)、return で呼び出し元に返す値が戻り値です。function で宣言する方法と、=> を使って関数を変数に入れる方法があります。
// function を使う関数宣言
function greet(name) {
return `${name}さん、こんにちは`;
}
// アロー関数の基本形
const addOne = (number) => {
return number + 1;
};
// 式が一つなら { } と return を省略できる。同じ処理になる
const addOneShort = (number) => number + 1;
// 処理を複数書くなら { } と return を使う
const rectangleArea = (width, height) => {
const size = width * height;
return size;
};
console.log(greet('山田')); // 山田さん、こんにちは
console.log(addOne(21), addOneShort(21)); // 22 22
console.log(rectangleArea(3, 4)); // 12
// rate を省略した場合は 0.1 を使う(既定値)
const addTax = (price, rate = 0.1) => Math.round(price * (1 + rate));
console.log(addTax(1000)); // 1100
console.log(addTax(1000, 0.08)); // 1080
どちらで書いても動きますが、役割で使い分けると、読む人が迷いません。
| 使う場面 | 書き方 | 例 |
|---|---|---|
| 名前を付けて使う関数 | function 宣言 | function greet(name) { … } |
| ほかの関数に渡す処理(コールバック) | アロー関数 | button.addEventListener('click', () => { … }) |
| オブジェクトやクラスの中の処理 | メソッド記法 | { greet(name) { … } } |
function greet() と書けば、関数を作っていることが一目で分かります。const greet = () => { … } は、変数の宣言にも見えます。
function 宣言は定義より前でも呼び出せます(巻き上げ)。const に入れたアロー関数は、その行より前では使えません。
アロー関数は、その場で短い処理を渡すときに向いています。
両者は this の扱いも異なるため、オブジェクト自身を this で参照するメソッドには、メソッド記法を使います。
this とメソッドの詳しい使い方は、実践編のクラスで扱います。ここでは「名前付きは function、渡す処理はアロー関数」と理解すれば十分です。
レスト引数とスプレッド構文 — 個数をまとめる・広げる
... は書く場所で役割が変わります。関数を定義するときの ...numbers は「レスト引数」で、残りの引数を一つの配列にまとめます。関数を呼ぶときの ...itemPrices は「スプレッド構文」で、配列の値を一つずつ引数として渡します。個数が決まっていない値を受け取ったり、手元の配列をそのまま渡したりするときに便利です。
function sum(...numbers) {
let total = 0;
for (const number of numbers) {
total += number;
}
return total;
}
console.log(sum(100, 200)); // 300
const itemPrices = [100, 200, 300];
console.log(sum(...itemPrices)); // 600
コールバック — 渡した先で呼ばれる関数
関数は、数値や文字列と同じように、ほかの関数へ渡せます。渡した先で呼び出される関数をコールバックといいます。下の例では sayHello を渡し、repeatTwice の中で呼び出します。繰り返し方はそのままで、渡す処理だけを入れ替えられます。
function repeatTwice(action) {
action();
action();
}
function sayHello() {
console.log('こんにちは');
}
repeatTwice(sayHello); // 「こんにちは」が 2 回出る
repeatTwice(sayHello) は関数そのものを渡します。sayHello() と書くと、渡す前にその場で実行してしまいます。
map() — 配列の各値を変換する
map() は配列に備わった機能です。配列の値を一つずつコールバックへ渡し、その戻り値を並べた新しい配列を作ります。元の配列は変えずに、同じ変換をすべての値に適用したいときに便利です。
function addTaxToPrice(price) {
return Math.round(price * 1.1);
}
const prices = [100, 200, 300];
const pricesWithTax = prices.map(addTaxToPrice);
console.log(pricesWithTax); // [110, 220, 330]
console.log(prices); // [100, 200, 300] のまま
map(addTaxToPrice) では、map() が価格を一つずつ addTaxToPrice() に渡します。戻ってきた税込み価格が pricesWithTax に並びます。
クロージャ — 前に作った値を覚えている関数
関数の中で別の関数を作ると、内側の関数は外側の変数を使い続けられます。この仕組みをクロージャといいます。下の count は makeCounter() の実行後も内側の関数に残ります。カウンターごとに値を分けて持てるので、全体で共有する変数を増やさずに済みます。
function makeCounter() {
// 呼び出すたびに新しい count を作るので、カウンター同士が影響しない。
let count = 0;
return () => {
count += 1;
return count;
};
}
const firstCounter = makeCounter();
const secondCounter = makeCounter();
console.log(firstCounter(), firstCounter()); // 1 2
console.log(secondCounter()); // 1
同じ形のデータと処理を複数まとめて使う方法は、実践編のクラスで学びます。
11学習レベル:基礎
DOM 操作 — HTML を読み書きする
ブラウザは HTML を読み込むと、要素をたどれる木構造(DOM)に変換します。 JavaScript はこの木を探して・読んで・書き換えることでページを動かします。
下のデモは、この HTML に対して動いています。
<h3 id="title">見出し</h3>
<p class="message">ここが書き換わります</p>
<ul id="list"></ul>
<button id="paint" type="button">色を変える</button>
見出し
// 1. 探す(CSS セレクタがそのまま使える)
const title = document.querySelector('#title');
const message = document.querySelector('.message');
const list = document.querySelector('#list');
// 2. 書き換える
title.textContent = 'JavaScript で書き換えた見出し';
message.textContent = `いまは ${new Date().toLocaleTimeString('ja-JP')} です`;
// 3. 作って足す
const fruits = ['りんご', 'みかん', 'ぶどう'];
for (const fruit of fruits) {
const li = document.createElement('li');
li.textContent = fruit;
list.append(li);
}
// 4. 見た目を変える
title.classList.add('is-active');
title.style.color = '#2f80b8'; // 直接スタイルを当てることもできる
// 5. まとめて処理する
for (const li of document.querySelectorAll('#list li')) {
li.style.fontWeight = '700';
}
// ボタンの動作は次の章で
document.querySelector('#paint').addEventListener('click', () => {
message.style.backgroundColor = '#eaf2f8';
});
「色を変える」を押すと、実際に背景色が変わります。
- querySelector()
- 条件に合う最初の 1 つ。無ければ
null。 - querySelectorAll()
- 合うもの全部。
for...ofで回せます。 - textContent
- 文字を読み書きする。安全なのでまずこちら。
- innerHTML
- HTML として解釈する。外部から来た文字を入れると危険(制作の心得参照)。
- createElement / append
- 要素を作って、子として加える。
- classList
add/remove/toggle/contains。見た目の変更は CSS のクラス切り替えで。- closest()
- 自分から親をたどって、条件に合う祖先を探す。イベント処理で多用します。
data-* 属性とは
data-* は、HTML の要素に自分で名前を付けた情報を持たせるための属性です。* は名前を表す記号で、実際には data-action="refresh" や data-ui="item-card" のように書きます。ブラウザがこれらに特別な動作を付けるわけではありません。JavaScript が「どのボタンか」を見分けたり、CSS が状態に合わせて見た目を変えたりするときの目印になります。
JavaScript では、要素の dataset から値を読み書きできます。data-action は dataset.action、data-max-count は dataset.maxCount に対応します。HTML の属性名にあるハイフンを、JavaScript 側では次の単語を大文字にして表します。
data-* を目印に要素を探す
getElementById() も、ページに一つだけの要素には有効です。ただ、同じ構造のカードが複数ある画面では、各子要素に固有の ID を振るより、カードを起点に data-ui を探すほうが再利用しやすくなります。CSS 用のクラス名を変えても JavaScript の参照が壊れません。
<section data-ui="item-card">
<span data-ui="item-count">0 件</span>
<button type="button" data-action="refresh" data-max-count="5">更新</button>
</section>
<script>
const card = document.querySelector('[data-ui="item-card"]');
const itemCount = card.querySelector('[data-ui="item-count"]');
const refreshButton = card.querySelector('[data-action="refresh"]');
const action = refreshButton.dataset.action; // 'refresh' を読む
const maxCount = Number(refreshButton.dataset.maxCount); // '5' を数値の 5 に変える
itemCount.textContent = `${maxCount} 件まで表示できます`;
card.dataset.state = 'ready'; // data-state="ready" を設定する
</script>
querySelector('[data-action="refresh"]') は、属性の値が refresh の要素を探します。dataset.action はその値を読み、dataset.state = 'ready' は HTML 側に属性を追加・更新します。data-* の値は文字列なので、数値として計算する場合は Number() で変換します。存在しない属性を dataset で読むと undefined です。id はラベルの for やページ内リンクなど、本来の用途でも使い続けます。
クラスを付け替えて表示する じゃんけんの部品を data-* で探す querySelector() querySelectorAll() textContent classList dataset
12学習レベル:基礎
イベント — 操作に反応する
「クリックされたら」「入力されたら」を addEventListener() で登録します。
HTML に onclick="…" と書く方法もありますが、
処理は JavaScript 側にまとめるのが今の作法です。
こんにちは、ゲストさん
// クリック
const counter = document.querySelector('[data-count]');
let count = 0;
counter.addEventListener('click', () => {
count += 1;
counter.textContent = `押した回数:${count}`;
});
// 入力のたびに反応する
const who = document.querySelector('#who');
const hello = document.querySelector('.hello');
who.addEventListener('input', (event) => {
const name = event.target.value.trim();
hello.textContent = `こんにちは、${name || 'ゲスト'}さん`;
});
// イベント委譲:親で 1 つだけ受けて、どれが押されたかを調べる
// ボタンが増えても登録し直さなくてよい
document.querySelector('.menu').addEventListener('click', (event) => {
const button = event.target.closest('button');
if (!button) return;
hello.style.color = button.dataset.color;
});
event.target… 実際に操作された要素。event.currentTargetは登録した側の要素です。event.preventDefault()… 既定の動作(リンクの移動、フォーム送信)を止めます。- よく使うイベント:
clickinputchangesubmitkeydownpointerdownscroll。 - スクロールなど大量に発火するイベントは、中で重い処理をしないこと。必要なら
requestAnimationFrameで間引きます。
入力のたびに計算し直す メニューを開閉する じゃんけんのボタンで 1 回勝負する addEventListener() event.target preventDefault()
13学習レベル:実践
非同期処理 — 待つ処理の書き方
通信やタイマーは「すぐには終わらない処理」です。
JavaScript は待っている間も止まらずに次へ進むので、
終わったら何をするかを書いておく必要があります。それが Promise と async / await です。
// 「あとで終わる処理」を作る
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
console.log('1. ここが先に動く');
// async を付けた関数の中では await で「終わるまで待つ」と書ける
async function main() {
const first = await wait(10, 'お茶');
console.log('3. 届いた:', first);
// 順番に待つ必要がないものは、まとめて待つほうが速い
const [a, b] = await Promise.all([wait(10, 'パン'), wait(10, '牛乳')]);
console.log('4. まとめて:', a, b);
// 失敗するかもしれない処理は try / catch で囲む
try {
await Promise.reject(new Error('通信に失敗しました'));
} catch (error) {
console.log('5. 捕まえた:', error.message);
}
}
main();
console.log('2. 待たずにここまで進む');
出力の順番に注目してください。1 → 2 → 3 → 4 → 5 の順で出ます。
async function loadUsers() {
try {
const response = await fetch('/api/users.json');
// fetch は 404 でも例外にならない。自分で確かめる
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const users = await response.json();
return users;
} catch (error) {
console.error('読み込みに失敗しました', error);
return [];
}
}
ファイルをダブルクリックして開いた状態(file://)では、セキュリティ上の制限で fetch や ES モジュールが動きません。試すときは、エディタの「ローカルサーバーで開く」機能を使うか、
ruby -run -e httpd . -p 8000 のような簡易サーバーを立ててください。
通信の流れは実践編のブラウザとサーバー、コードを役割ごとにファイルへ分ける方法は実践編のモジュールで学びます。
じゃんけんのファイルの読み込み じゃんけんの確認を await で待つ Promise async function await fetch()
失敗に備える — try / catch
通信は、電波が届かない、サーバーが止まっているなどの理由で失敗することがあります。処理が失敗すると、JavaScript は例外(エラー)を投げ、そのままではそこで処理が止まります。try { … } catch (error) { … } で囲んでおくと、try の中で例外が投げられたとき、catch に移って続きの処理を書けます。
// 文字を数値に直す。数値にならなければ、自分で例外を投げる
function parsePrice(text) {
const price = Number(text);
if (Number.isNaN(price)) {
throw new Error(`「${text}」は数値ではありません`);
}
return price;
}
try {
console.log(parsePrice('1280')); // 1280
console.log(parsePrice('千円')); // ここで例外が投げられ、catch へ移る
console.log('この行は実行されない');
} catch (error) {
// error.message に、投げたときの文が入っている
console.log(`読み取れませんでした:${error.message}`);
} finally {
console.log('finally は、成功しても失敗しても最後に実行される');
}
console.log('catch で受け止めたので、ここから先も動く');
try の中で例外が投げられると、残りの行は飛ばされ、catch に移ります。catch で受け止めたので、最後の行まで処理が続きます。
throwで自分から投げる …throw new Error('理由')と書くと、その場で例外を投げられます。上のparsePrice()のように、「この値では続けられない」ことを呼び出し元に知らせるときに使います。awaitの失敗も受け止められる …async関数の中では、awaitで待った処理の失敗も、同じtry/catchで受け止められます。上の「サーバーからデータを取る」例がこの形です。- 失敗したら何をするかを書く …
catchの中では、利用者に理由を知らせる、空のデータで続けるなど、次にどうするかを書きます。何も書かずに例外を受け止めるだけにすると、失敗したことに誰も気づけません。 - 囲むのは、失敗しうるところだけ … 通信、
JSON.parse()、保存など、外からの事情で失敗する処理を囲みます。自分のコードの書き間違いは、tryで隠さずに直します。
14学習レベル:実践
最近入った便利な機能
JavaScript は毎年更新されています。ここ数年で入って、 必要になったときに調べたいものをいくつか挙げます。この節は、初回は飛ばしてもかまいません。
const prices = [300, 100, 200];
// toSorted() は並べ替えた新しい配列を返す。
const sortedPrices = prices.toSorted((left, right) => left - right);
console.log(sortedPrices); // [100, 200, 300]
console.log(prices); // [300, 100, 200] のまま
// at(-1) で配列の最後の値を読める。
console.log(prices.at(-1)); // 200
言語仕様は毎年 6 月に更新され、ブラウザへの搭載はそれとは別のペースで進みます。 使う前に MDN の「ブラウザーの互換性」表か、Can I use で対応状況を確認してください。 ほかにも新しい機能がありますが、最初から一覧を覚える必要はありません。使いたい操作が出てきたら調べましょう。
15学習レベル:基礎
動かないときの調べ方
エラーは、どこで何が起きたかを知らせる、ブラウザからの手がかりです。 まずコンソールを開きます。
| よく出るエラー | たいていの原因 |
|---|---|
Cannot read properties of null | 要素が見つかっていない。セレクタの綴り違い、または defer の付け忘れ |
X is not defined | 変数名の打ち間違い、宣言し忘れ、スコープの外から使っている |
X is not a function | メソッド名の間違い(querySelectorAll を querySelector と書いた等) |
Unexpected token | 括弧や引用符の閉じ忘れ。エラーのすぐ上の行を疑う |
| 何も起きない | そもそも関数が呼ばれていない。console.log('ここ通った') を入れて確認 |
- コンソールを開く
赤い文字を読む。ファイル名と行番号が書いてあります。
- console.log を挟む
「どこまで動いたか」「変数に何が入っているか」を実際に見ます。
- 要素タブで DOM を見る
思ったクラスが付いているか、CSS のどの行が効いているかが分かります。
- 小さく切り分ける
怪しい部分だけを取り出して、単独で動かしてみます。
- それでも分からなければ検索
エラーメッセージをそのまま貼って検索。MDN の解説が出てくれば当たりです。
16学習レベル:実践
モダンな Web 制作で知っておいてほしいこと
文法とは別に、作り方の姿勢の話です。最初にこれを知っておくと、あとで作り直す回数がかなり減ります。
-
HTML → CSS → JavaScript の順で足す(段階的強化)
まず HTML だけで内容が読める状態を作り、CSS で整え、最後に JavaScript で便利にします。 JS が読み込めなくてもページが使えなくなることのない構造になり、表示も速くなります。
<details>や:has()で済むことに JS を使わない、という判断もここに含まれます。 -
デバイスの幅を決め打ちしない
「iPhone は 390px だから」と個別対応を始めると終わりません。 内容が崩れる幅で切り替えます。
clamp()やauto-fillを使えば、そもそも切り替え自体が減ります。 -
アクセシビリティは最初から考える
正しいタグ、ラベル、代替テキスト、キーボード操作、コントラスト。最初から確認しておくと、作り終わってから直す手間を減らせます。 マウスを外して Tab キーだけで操作してみるのが、いちばん手軽な自己点検です。
-
画像の大きさを確認する
大きな画像は表示を遅くすることがあります。用途に合う大きさと形式を選び、
width/heightを書き、画面外の画像には必要に応じてloading="lazy"を使います。 -
innerHTML に外から来た文字を入れない
利用者の入力や URL の値をそのまま
innerHTMLに入れると、スクリプトを埋め込まれる恐れがあります(XSS)。 文字を入れるだけなら必ずtextContent。外部サイトへのリンクにはrel="noopener noreferrer"を付けます。 -
フレームワークは急がなくてよい
React や Vue は「状態が複雑なアプリ」のための道具です。数ページのサイトなら素の HTML/CSS/JS のほうが速くて壊れにくい。 土台が分かっていないと、フレームワークの不具合は直せません。
-
ファイル構成と名前を決めておく
assets/stylesheets/assets/javascripts/assets/images/のように場所を決め、ファイル名は半角小文字とハイフンで。迷う時間が消え、他人(半年後の自分を含む)が読めるようになります。 -
バージョン管理を最初から使う
Git を入れて、区切りごとに記録(コミット)します。「昨日は動いていたのに」から復帰できるようになるだけで、挑戦できる幅が変わります。 GitHub に置けば、そのまま公開(GitHub Pages)もできます。
-
ブラウザの開発者ツールをいつも開いておく
要素タブで DOM と CSS、コンソールでエラー、ネットワークで読み込み、 Lighthouse で速度とアクセシビリティの点検。 説明を読んだら実際のページでも試してみましょう。
-
情報源を絞る
仕様と使い方は MDN Web Docs、対応状況は Can I use。古い記事は「その時点の正解」でしかありません。
floatで段組み、var、jQuery 前提の解説を見かけたら、まず年代を確認してください。
全部を覚えてから作り始める必要はありません。 小さくても、動くものを最後まで作るほうが、確実に身につきます。分からないことはそのつど調べればよく、そのために MDN があります。
17
次は JavaScript 実践編へ
HTML で構造を、CSS で見た目を、JavaScript で動きを作れるようになりました。次は、サイトやアプリを育てるためのコードの整理、保存、オフライン対応へ進みます。
ボタンと数字をページに置き、押すたびに数字が 1 増えるようにしてみましょう。次に、数字が 10 になったらボタンを押せなくします。変数・選択・DOM・イベントを一度に使えます。