JavaScript イベント
イベントはユーザーまたはブラウザから送られる「シグナル」です。ユーザーがボタンをクリックしたり、キーを押したり、フォームを送信したり、ブラウザがページの読み込みを完了したり...这些都是イベントです。コードはこれらのイベントを「リスニング」し、適切なタイミングで応答します。ドアベルが鳴ってドアを開けるようなもので、ドアベルがイベント、ドアを開けるのが応答です。
イベントバインドの方法
JavaScript にはイベントハンドラを要素にバインドする3つの方法があります。
1. HTML 属性(非推奨)
HTML
<button onclick="alert('この方法は使わないでください')">クリック</button>
HTML と JS が混在し、メンテナンスが困難です。非推奨です。
2. DOM プロパティ
HTML
<button id="myBtn">クリック</button>
<script>
const btn = document.getElementById('myBtn');
btn.onclick = function() {
console.log('クリックされました');
};
</script>
HTML と JS は分離されますが、各イベントには1つのハンドラしか持てません。後からの代入は以前のものを上書きします。
3. addEventListener(推奨)
HTML
<button id="btn1">ボタン 1</button>
<button id="btn2">ボタン 2</button>
<script>
const btn1 = document.getElementById('btn1');
const btn2 = document.getElementById('btn2');
btn1.addEventListener('click', function() {
console.log('ハンドラ 1');
});
btn1.addEventListener('click', function() {
console.log('ハンドラ 2');
});
</script>
同じイベントに複数のハンドラを持たせることができ、removeEventListener で削除することもできます。最も柔軟なアプローチです。
💡 3つの方法の選択肢:最初の2つを忘れ、常に
addEventListener を使いましょう。既存のリスナーを上書きせず、バブリング/キャプチャのフェーズを制御できます。現代的な開発での唯一の選択肢です。
addEventListener と removeEventListener
HTML
<script>
element.addEventListener('eventName', handler, options);
element.removeEventListener('eventName', handler);
</script>
⚠️
removeEventListener には addEventListener と同じ関数参照が必要です。匿名関数は削除できません!ハンドラを削除する必要がある場合は、関数を別々に定義してください。
HTML
<button id="btn">テスト</button>
<script>
const btn = document.getElementById('btn');
function handler() { console.log('1回だけトリガーされます'); }
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler); // 削除成功
btn.addEventListener('click', function() { console.log('匿名'); });
btn.removeEventListener('click', function() { console.log('匿名'); }); // 削除失敗!
</script>
一般的なイベントタイプ
| カテゴリ | イベント | 説明 |
|---|---|---|
| マウス | click、dblclick |
シングルクリック、ダブルクリック |
| マウス | mouseover、mouseout |
マウスが入る/出る |
| キーボード | keydown、keyup |
キーが押された/離された |
| フォーム | submit |
フォームが送信された |
| フォーム | change |
フォーカスを失った後に値が変更された |
| フォーム | input |
値がリアルタイムで変更される(各文字入力ごと) |
| ページ | load |
ページとすべてのリソースが完全に読み込まれた |
| ページ | DOMContentLoaded |
HTML の解析が完了(画像を待たない) |
💡
change vs input:input は入力中に発火し、change は入力完了後にフォーカスを失った時に発火します。リアルタイム検索には input を、フォームバリデーションには change を使いましょう。
イベントオブジェクト
イベントハンドラはイベントオブジェクトのパラメータを受け取ります。通常 event または省略形の e と呼ばれます。
| プロパティ/メソッド | 説明 |
|---|---|
e.target |
イベントをトリガーした要素(実際にクリックされたもの) |
e.type |
イベントタイプ(例:'click') |
e.preventDefault() |
デフォルト動作を防止(例:リンクのナビゲーション、フォームの送信を防止) |
e.stopPropagation() |
イベントのバブリングを停止 |
例:ボタンクリックカウンター
HTML
<button id="counter">0 回クリック</button>
<script>
const btn = document.getElementById('counter');
let count = 0;
btn.addEventListener('click', function(e) {
count++;
btn.textContent = count + ' 回クリック';
console.log('イベントタイプ:', e.type);
console.log('ターゲット要素:', e.target.tagName);
});
</script>
例:キーボード検出
<p>任意のキーを押して情報を確認してください</p>
<div id="info" style="padding: 10px; border: 1px solid #ccc; min-height: 30px;"></div>
<script>
const info = document.getElementById('info');
document.addEventListener('keydown', function(e) {
info.innerHTML = 'キー: <b>' + e.key + '</b> | KeyCode: ' + e.keyCode + ' | Ctrl: ' + e.ctrlKey + ' | Shift: ' + e.shiftKey;
});
</script>
例:フォームのインターセプト
HTML
<form id="myForm">
<input type="text" id="name" placeholder="名前を入力" required>
<button type="submit">送信</button>
</form>
<p id="result"></p>
<script>
const form = document.getElementById('myForm');
const result = document.getElementById('result');
form.addEventListener('submit', function(e) {
e.preventDefault();
const name = document.getElementById('name').value;
result.textContent = 'こんにちは、' + name + 'さん!フォームはインターセプトされ、実際には送信されませんでした。';
});
</script>
例:イベント委譲
HTML
<ul id="menu">
<li>ホーム</li>
<li>概要</li>
<li>お問い合わせ</li>
<li>ヘルプ</li>
</ul>
<p id="selected"></p>
<script>
const menu = document.getElementById('menu');
const selected = document.getElementById('selected');
menu.addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
selected.textContent = '選択しました: ' + e.target.textContent;
}
});
</script>
💡 上記のテクニックは「イベント委譲」と呼ばれます。リスナーを親要素にバインドし、
e.target を使ってどの子要素が実際にクリックされたかを判定します。各 li に個別にイベントをバインドする必要はなく、新しい子要素も自動的に動作します。これがバブリングメカニズムの力です。
イベントのバブリングとキャプチャ
DOM イベントの伝播には3つのフェーズがあります。
- キャプチャフェーズ:イベントが
windowから下方向にターゲット要素へ移動 - ターゲットフェーズ:イベントが実際のトリガー要素に到達
- バブリングフェーズ:イベントがターゲット要素から上方向に
windowへ戻る
デフォルトでは、addEventListener はバブリングフェーズでトリガーされます。第3パラメータを true に設定すると、キャプチャフェーズでトリガーされます。
window
↓ キャプチャ
document
↓
body
↓
ターゲット要素 ← ターゲットフェーズ
↑ バブリング
body
↑
document
↑
window
💡 ほとんどの場合、キャプチャを気にする必要はありません。デフォルトのバブリングで十分です。
e.stopPropagation() はイベントの伝播を停止できますが、過度に使うとイベント委譲が壊れる可能性があります。
📖 まとめ
- 3つのバインド方法の中で、
addEventListenerだけが推奨されます。複数のリスナーに対応し、削除可能で、伝播フェーズを制御できます。 removeEventListenerには同じ関数参照が必要です。匿名関数は削除できません。- コアイベントオブジェクト
eのプロパティ:target(誰がトリガーしたか)、type(何のイベントか)、preventDefault()(デフォルト動作を防止)、stopPropagation()(伝播を停止)。 changevsinput:前者はブラー時に発火、後者はリアルタイムで発火。- イベントバブリングにより「イベント委譲」が可能に。親でリスニングし、
e.targetで子を処理。 DOMContentLoadedはloadより早く発火し、DOM の初期化に適している。
❓ よくある質問
Q
e.target と e.currentTarget の違いは何ですか?A
e.target はイベントを実際にトリガーした要素(クリックしたもの)、e.currentTarget は現在ハンドラを実行している要素(リスナーを持っているもの)です。イベント委譲では異なり、直接バインドでは同じです。Q フォーム送信後にページが更新されるのはなぜですか?
A
<form> の submit イベントのデフォルト動作は送信とページの更新です。e.preventDefault() を呼び出して防止し、JS でデータを処理しましょう。Q
onclick と addEventListener('click') を一緒に使えますか?A はい、ただし
onclick の代入は以前に onclick でバインドされた関数を上書きしますが、addEventListener は上書きしません。混在させると混乱するので、安心のために addEventListener に統一しましょう。📝 演習
- 基礎:ボタンを作成し、
addEventListenerでclickイベントをバインドしてください。クリックごとにボタンの幅が10px 増加するようにしてください。 - 中級:キーボード楽器を実装してください。
keydownイベントをリスニングし、A/S/D/F/G が押された時にページに異なる音階名(ド/レ/ミ/ファ/ソ)を表示してください。 - チャレンジ:イベント委譲を使って動的メニューを実装してください。「アイテム追加」ボタン付きの
ulリスト。新しく追加されたliアイテムもクリック時にハイライトされるようにしてください(新しいli要素に個別にイベントをバインドする必要はありません)。 - ボーナス:
inputイベントでリアルタイム検索を実装してください。入力フィールドに入力すると、下のリストに入力テキストを含むアイテムのみが表示されます。



