HTML・CSS・JavaScript入門:インタラクティブなWebページを作る
現代のWebサイトは、連携して動く3つの技術で作られています。HTMLはコンテンツの構造を定義し、CSSは見た目とレイアウトを定義し、JavaScriptはボタンのクリックや文字入力、タブの選択など、ユーザーの操作にページを反応させます。この記事では、3つを組み合わせた小さな例を作りながら、それぞれの役割と、役割を分けることの利点を説明します。
それぞれの言語の役割
家づくりにたとえてみましょう。HTMLは骨組みと壁で、リビングやキッチン、ドアの場所を決めます。CSSは塗装や素材、家具の配置にあたり、住み心地のよい見た目を作ります。JavaScriptは電気の配線とスイッチで、スイッチを押せば明かりがつき、ドアを開けば呼び鈴が鳴ります。
この役割を分けておくと便利です。サイト全体の色を変えたいときはCSSだけを編集すればよく、構造には触れません。操作に反応する機能を追加したいときは、見た目を変えずにJavaScriptを書けます。こうするとコードは読みやすく、長期間の保守もしやすくなります。
プロジェクトの構成
`interactive-demo` というフォルダを作り、その中に3つのファイルを置きます。
- `index.html`:構造
- `style.css`:見た目
- `script.js`:インタラクティブな動作
3つのファイルは同じフォルダに置きます。そうすれば、HTMLからの参照を完全なアドレスで書く必要がありません。
ステップ1:HTMLの構造
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>インタラクティブ・スコアカウンター</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main class="card">
<h1>スコアカウンター</h1>
<p id="score-text">現在のスコア:<span id="score">0</span></p>
<div class="buttons">
<button id="add-btn" type="button">+1 ポイント</button>
<button id="reset-btn" type="button">リセット</button>
</div>
<p id="message" aria-live="polite"></p>
</main>
<script src="script.js"></script>
</body>
</html>
このコードの重要な要素は3つのグループです。`id="score"` を持つスコアの数字、それぞれ独自の `id` を持つ2つのボタン、そして通知を表示する段落 `id="message"` です。`aria-live="polite"` は、内容が変わったときにスクリーンリーダーに新しいテキストを読み上げさせる指定で、画面が見えない利用者も他の人と同じ情報を受け取れます。
`<script>` を `<body>` の末尾に置くと、ページ内のすべての要素が読み込まれた後でJavaScriptが動き始めます。そのため、`id` で要素を探してもエラーになりません。
ステップ2:CSSで装飾する
:root {
--bg: #f0f4fb;
--card: #ffffff;
--accent: #2255aa;
--text: #1a1a1a;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg);
color: var(--text);
font-family: system-ui, sans-serif;
}
.card {
background: var(--card);
padding: 32px;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
text-align: center;
width: min(90vw, 360px);
}
.buttons {
display: flex;
gap: 12px;
justify-content: center;
}
button {
padding: 10px 18px;
border: none;
border-radius: 8px;
background: var(--accent);
color: #fff;
font-size: 16px;
cursor: pointer;
transition: transform 0.1s ease, opacity 0.2s ease;
}
button:hover {
opacity: 0.9;
}
button:active {
transform: scale(0.96);
}
#reset-btn {
background: #8a94a8;
}
ルールを1つずつ読んでみましょう。
- `:root` は色の変数を保存します。サイトの主要な色を変えたいときは、1行を編集するだけで済みます。変数は `var(--名前)` の形で使います。
- `body` はFlexboxを使い、カードを縦横ともに中央に配置します。`min-height: 100vh` によって、画面全体の高さを使えるようにしています。
- `.card` はドット(`.`)で始まります。これはclassであり、複数の要素で共有できるためです。`width: min(90vw, 360px)` は、大きな画面でカードが広がりすぎないようにしつつ、スマートフォンでもはみ出さないようにします。
- `button:hover` と `button:active` は、マウスがボタンの上にあるときと押されたときの状態を指定します。これによって、ボタンが反応していることが利用者に伝わります。
- `#reset-btn` は `#` で始まります。これはidを指定しており、要素を1つだけ特定します。
classとidの違いは重要です。classは同じスタイルを持つ要素をまとめるためのもので、idは特定の要素を1つだけ指し示すためのものです。
ステップ3:JavaScriptでページを動かす
const scoreEl = document.getElementById("score");
const addBtn = document.getElementById("add-btn");
const resetBtn = document.getElementById("reset-btn");
const messageEl = document.getElementById("message");
let score = 0;
function render() {
scoreEl.textContent = score;
if (score >= 10) {
messageEl.textContent = "素晴らしい!10ポイントに到達しました。";
} else {
messageEl.textContent = "";
}
}
addBtn.addEventListener("click", function () {
score = score + 1;
render();
});
resetBtn.addEventListener("click", function () {
score = 0;
render();
});
render();
このファイルは作業を明確な部分に分けています。
1. `document.getElementById` はHTML内の要素をidで探し、変数に保存します。最初に一度だけ探しておくと、コードが速く読みやすくなります。
2. `let score = 0` はプログラムの状態を保持します。この値が、プログラム全体が使う唯一の正しい値です。
3. 関数 `render` は `score` を読み取り、その値に合わせて画面を更新します。これは「状態から描画する」と呼ばれる考え方です。どの操作でも画面の文字を直接書き換えるのではなく、値を変えたら描画関数が表示を更新します。
4. `addEventListener("click", ...)` は、クリックイベントに関数を結び付けます。利用者がボタンをクリックするたびに、その関数が実行されます。
5. 末尾の `render()` の呼び出しにより、ページを開いた時点でスコア0が表示されます。
試してみてください。+1 を押すたびに数字が増え、10に達するとメッセージが表示され、リセットを押すと0に戻ります。
ステップ4:コードをより堅牢にする
この例は動きますが、次のような習慣を身につけるとコードがさらに強くなります。
- **要素が存在するか確認する:** idを打ち間違えると `getElementById` は `null` を返し、その値を使った時点でエラーになります。大きなプロジェクトでは、使う前に確認しましょう。
- **既定では `const` を使う:** 値が本当に変わる場合にだけ `let` を使います。たとえば `score` のような変数です。
- **関数の役割を絞る:** 各関数は1つの仕事だけをします。`render` は状態を表示するだけで、スコアを自分で計算しません。
安全確認の例は次のとおりです。
if (!scoreEl) {
console.error("HTML に #score の要素が見つかりません");
}
`console.error` はブラウザのConsoleタブ(F12キーで開く)にメッセージを表示するので、問題の場所を素早く見つけられます。
CSSかJavaScriptか:簡単な判断基準
初心者は、ある効果をどちらの言語で実装すべきか迷うことがあります。簡単な基準があります。見た目だけの変化で、利用者の操作に依存しないならCSSを使います。たとえば、ホバー時の色変更や要素の間隔などです。動作がデータや操作に依存する場合、たとえばクリック回数を数えたり、入力された値を保存したりするならJavaScriptを使います。
たとえば、ホバー時にボタンの色を変えるのはCSSの `:hover` で実現できます。しかし、ボタンが何回押されたかを数えるにはJavaScriptが必要です。
classで見た目を切り替える
スコアが10に達したときにカードの色を変えたいとします。JavaScriptの中にスタイルを直接書くのではなく、JavaScriptからclassを追加し、見た目はCSSで定義します。
.card.success {
background: #e6f7ec;
border: 2px solid #2e9e5b;
}
const cardEl = document.querySelector(".card");
function render() {
scoreEl.textContent = score;
if (score >= 10) {
messageEl.textContent = "素晴らしい!10ポイントに到達しました。";
cardEl.classList.add("success");
} else {
messageEl.textContent = "";
cardEl.classList.remove("success");
}
}
この方法では役割がはっきり分かれます。JavaScriptは「今の状態は成功だ」と伝え、CSSは成功時の見た目を決めます。JavaScriptを変えずに、デザインだけを自由に変更できます。
追加の例:シンプルなタブメニュー
この例は、クリックに応じて内容を切り替えるものです。実際の多くのWebサイトで見られるパターンです。
<nav>
<button class="tab-btn active" data-tab="home">ホーム</button>
<button class="tab-btn" data-tab="about">自己紹介</button>
</nav>
<section class="tab-panel active" id="home">私のWebサイトへようこそ。</section>
<section class="tab-panel" id="about">私はWebの作り方を学んでいる学生です。</section>
.tab-panel { display: none; }
.tab-panel.active { display: block; }
.tab-btn.active { background: #2255aa; color: #fff; }
const buttons = document.querySelectorAll(".tab-btn");
const panels = document.querySelectorAll(".tab-panel");
buttons.forEach(function (btn) {
btn.addEventListener("click", function () {
const target = btn.dataset.tab;
buttons.forEach(b => b.classList.remove("active"));
panels.forEach(p => p.classList.remove("active"));
btn.classList.add("active");
document.getElementById(target).classList.add("active");
});
});
`querySelectorAll` は、セレクタに一致するすべての要素のリストを返します。`forEach` はそのリストを順に回り、各ボタンにクリック処理を結び付けます。`btn.dataset.tab` は `data-tab` 属性の値を読み取るので、ボタンごとに別の関数を書く必要がなくなります。
JavaScriptでよくあるミス
- **HTMLが読み込まれる前に要素を使う:** `<script>` を `<body>` の末尾に置くか、scriptタグに `defer` を付けて解決します。
- **括弧や引用符の閉じ忘れ:** すべての括弧が閉じているか確認し、エラーが出たらブラウザのConsoleを見ます。
- **`===` の代わりに `=` で比較する:** 値を厳密に比較するには `===` を使います。
- **ループの中で何度もページを更新する:** 可能なら変更をまとめて、一度だけ更新します。そうするとページが滑らかに動きます。
初心者へのアドバイス
- まずHTMLで構造を書き、CSSとJavaScriptを加える前に、ページが意味の通る形で読めるか確認します。
- CSSは小さな単位で追加し、変更ごとにブラウザで結果を確認します。
- JavaScriptは一度に1つの関数を追加し、次に進む前にテストします。
- 問題の発見にはConsoleを使い、エラーメッセージを最後まで読んでからコードを直します。
- カウンター、時計、電卓、TODOリストなど、自分でも小さなプロジェクトを作って練習します。
保存と実行の方法
1. `interactive-demo` という名前の新しいフォルダを作ります。
2. そのフォルダに `index.html`、`style.css`、`script.js` の3つのファイルを作り、各ステップのコードを貼り付けます。
3. ファイルはUTF-8で保存します。メモ帳なら「名前を付けて保存」で文字コードをUTF-8に設定します。そうしないと日本語が文字化けすることがあります。
4. ファイル名を確認します。Windowsではファイル名拡張子を表示する設定を先に有効にしておくと、`script.js.txt` のような誤った名前を防げます。
5. `index.html` をダブルクリックするか、ブラウザのウィンドウにドラッグします。
6. VS Codeを使う場合は Live Server を追加し、`index.html` を右クリックして「Open with Live Server」を選びます。保存するたびにページが自動で再読み込みされます。
実際に実行した結果
以下は、スコアカウンターを実際のブラウザで動かし、ボタンを順番に押して確認した結果です。テストではタイ語版のコードを使ったため、表示されるメッセージはタイ語です。
- **最初:** スコア 0、メッセージなし、カードは白色。
- **+1 を9回押す:** スコア 9、メッセージなし、カードは白色のまま。
- **10回目の +1:** スコア 10。「ยอดเยี่ยม! คุณทำได้ถึง 10 คะแนนแล้ว」というメッセージが出て、JavaScriptが `success` クラスを追加したためカードが緑色になります。
- **リセットを押す:** スコアが 0 に戻り、メッセージが消え、JavaScriptが `success` クラスを外したためカードが白に戻ります。
タブメニューの例も実際のブラウザで確認しました。最初のタブはホームで、「เกี่ยวกับ(自己紹介)」を押すと対応するパネルが表示され、そのボタンが強調表示されます。「หน้าแรก(ホーム)」を押すと、ホームのパネルに戻ります。テストのメッセージもタイ語でしたが、動作はどの言語でも同じです。
次のステップ
3つの言語に慣れたら、次のことにも挑戦できます。レイアウトを高度にするCSSのFlexboxとGrid、サーバーからデータを読み込む `fetch` とJSON、コードを管理するGit、そしてプロジェクトが大きくなったらReactのようなフレームワークです。
まとめ
HTMLは構造を、CSSは見た目を、JavaScriptは操作への反応を担当します。この3つの役割を分けておくと、コードは理解しやすく、変更しやすく、拡張しやすくなります。スコアカウンターやタブメニューのような小さな例から始め、少しずつ複雑さを増やしていきましょう。毎日自分でコードを書いて試すことが、読むだけの学習よりもずっと速く上達させてくれます。