はじめに
JavaScriptのイベントハンドラは、クリック、入力、送信、スクロールなどの操作に応じて処理を動かす仕組みです。そのため、静的なHTMLに反応を加えたい場面では、addEventListenerを中心に組み立てると保守しやすくなります。
初心者向けに押さえたいのは、onclickのようなプロパティ代入より、複数登録や削除に向いたaddEventListener()を優先する考え方です。一方、既存コードを読む場面では古い書き方も出てくるため、違いを知っておくと修正範囲を判断しやすいでしょう。
使い方の中心は、対象要素を取得し、イベント名を渡し、発生時に呼ばれる関数を書く流れになります。イベントハンドラの応用例では、フォーム検証、モーダル、タブ、検索、スクロール演出などを扱いるのが基本です。
- JavaScript: ECMAScript 2024相当の構文
- HTML: HTML Living Standard
- ブラウザ: Google Chrome 126以降 / Firefox 127以降 / Safari 17以降を想定
- JavaScriptのイベントハンドラの基本構造と使い方
click、input、submitなど主要イベントの扱い- フォーム、タブ、モーダルなどの応用例
- イベント伝播、削除、非同期処理に関する注意点
- カスタマイズサンプルコードを読み替える観点
イベントハンドラとは
結果: 期待される表示は、ボタンを押したタイミングで段落に「クリックされました」と入る状態です。
この短いコードが、イベントハンドラの最小形です。buttonがイベントを受け取る対象、clickがイベント名、アロー関数が発生時に動く処理になります。
一般に、イベントはブラウザが発生させる通知だと整理できます。ユーザーが<button>を押す、<input>へ文字を入れる、<form>を送信する、といった変化をJavaScript側で受け取るのがイベントハンドラです。
公式ドキュメントによれば、MDNのaddEventListener解説では、指定した種類のイベントが対象へ届けられたときに関数を呼ぶ仕組みとして説明されているのが目安です。そのため、現在の実装ではEventTargetのaddEventListenerを軸に考えるのが自然です。
一方、element.onclick = function () {}のような書き方も動作します。ただし、同じプロパティへ再代入すると前の処理が上書きされるため、複数の処理を組み合わせる画面ではaddEventListener()のほうが扱いやすいと言えるでしょう。
これに関連して、JavaScriptの配列処理を併用する画面では、forEach・mapの使い分けも理解しておくと、一覧要素へのイベント登録が読みやすくなります。複数のボタンへ同じ処理を付ける場合、querySelectorAllとforEachを組み合わせる場面が多いからです。
💡 Tips: イベントハンドラは「要素」「イベント名」「処理」の組み合わせで読むと理解しやすくなるのがポイントです。初心者向けの学習では、最初から複雑なUIを作るより、1個のボタンで反応を確認する流れが現実的です。
| 分類 | イベント | 主な対象 | 使い方の要点 | 注意点 |
|---|---|---|---|---|
| クリック | click | <button> | ボタン操作の起点にする | 連打対策を考える |
| ダブルクリック | dblclick | <div> | 特殊操作に限定する | モバイルでは相性に注意 |
| 入力 | input | <input> | 入力中に値を読む | 処理が重い場合は間引く |
| 変更 | change | <select> | 選択確定時に使う | inputとの発火時点が違う |
| 送信 | submit | <form> | 検証後に送信を制御する | preventDefaultの要否を見る |
| キー入力 | keydown | document | ショートカットに使う | 入力欄との衝突に注意 |
| キー解放 | keyup | <input> | 入力後の判定に使う | IME入力を考慮する |
| フォーカス | focus | <input> | 入力開始を検知する | 伝播しない性質を知る |
| フォーカス解除 | blur | <textarea> | 入力完了後の検証に向く | 即時エラー表示は慎重に扱う |
| マウス進入 | mouseenter | <nav> | ホバーUIに使う | タッチ端末では代替が必要 |
| マウス退出 | mouseleave | <nav> | 開いたUIを戻す | 子要素との関係を確認する |
| マウス移動 | mousemove | window | ドラッグ中の座標に使う | 高頻度発火を抑える |
| ポインター押下 | pointerdown | <canvas> | マウスとタッチをまとめる | 古い環境の対応を見る |
| ポインター移動 | pointermove | document | ドラッグ操作を追う | 不要時は解除する |
| ポインター解放 | pointerup | document | ドラッグ終了を扱う | 範囲外解放を考える |
| 読み込み | DOMContentLoaded | document | DOM構築後に初期化する | 画像読込完了とは違う |
| ページ読込 | load | window | 画像も含めて待つ | 初期表示が遅れやすい |
| スクロール | scroll | window | 表示位置に応じて処理する | 連続発火に注意する |
| リサイズ | resize | window | 画面幅の変化を読む | CSSで済む処理はCSSへ寄せる |
| コピー | copy | document | コピー操作を検知する | 過度な制限は避ける |
| 貼り付け | paste | <input> | 貼り付け値を整える | ユーザー操作を妨げない |
| ドラッグ開始 | dragstart | <li> | 移動対象を記録する | アクセシビリティを補う |
| ドロップ | drop | <ul> | 移動先で処理する | dragoverの制御が必要 |
| アニメーション終了 | animationend | <div> | CSS演出後に片付ける | 中断時の状態を見る |
| 遷移終了 | transitionend | <div> | 開閉後の処理に使う | 複数プロパティで複数回動く |
| エラー | error | <img> | 代替画像へ切り替える | 無限再試行を避ける |
| メディア再生 | play | <video> | 再生開始を検知する | 自動再生制限を考慮する |
| メディア停止 | pause | <video> | 停止時のUIを戻す | 状態管理と合わせる |
| カスタム | CustomEvent | document | 独自の通知を作る | 名前の衝突を避ける |
| 解除 | removeEventListener | Element | 不要な処理を外す | 同じ関数参照が必要 |
イベントハンドラの使い方
使い方は、document.getElementByIdやdocument.querySelectorで対象を取得し、addEventListenerでイベント名と関数を渡す流れになります。その構造を覚えると、クリック、入力、キー操作の違いはイベント名と読み取る値の違いとして整理できます。
サンプルコード1:クリックイベント
これが最も短いクリック処理の形です。textContentで表示文字列を変えるため、alertより画面上の変化が追いやすくなるのが一般的です。
結果: 期待される表示は、保存ボタンを押すと「保存待ちの処理を受け付けました」に変わる状態です。
サンプルコード2:マウスオーバーイベント
その要素へポインターが入ったときと出たときを分けると、簡単なホバー演出を作れます。ただし、タッチ端末では同じ操作感にならないため、クリック操作でも同等の情報へ届く設計が必要です。
結果: 期待される表示は、ポインターが重なった間だけ背景色が黄色系へ変わる状態です。
サンプルコード3:キーボードイベント
このとき、入力値そのものを扱うならkeyupよりinputが合う場合もあります。キーそのものを判定したい場面では、KeyboardEventのkeyを読みます。
結果: 期待される表示は、入力欄でEnterキーを押すと、その時点の入力値が段落へ反映される状態です。
同様に、配列の各要素へイベントを設定する処理ではNodeListを回す必要があります。forEachでreturnを扱う考え方を知っていると、途中終了したい処理と全件処理したい処理を分けやすくなります。
イベントハンドラの応用例
応用例では、イベントが発生したあとに何を更新するかが主役になるのが現実的です。フォームなら入力値、モーダルなら表示状態、検索なら候補一覧というように、対象データを明確にするとイベントハンドラの責務が過度に広がりません。
サンプルコード4:フォームバリデーション
具体的には、submitイベントで送信前の値を読み、条件に合わない場合だけpreventDefault()で通常送信を止めます。HTMLのrequiredやtype='email'と組み合わせると、JavaScript側の処理を絞れます。
結果: 期待される表示は、送信操作のあとにメール形式の判定メッセージが表示される状態です。
サンプルコード5:ドラッグ操作
これらのドラッグ処理では、開始時のずれを記録し、移動中にstyle.leftとstyle.topを更新します。一方、実運用ではキーボード操作やスクリーンリーダーへの代替操作も検討対象です。
結果: 期待される表示は、青い四角形を押したまま動かすと位置が変わる状態です。
サンプルコード6:スライダー
その場で表示する番号をcurrentとして保持すると、前後ボタンの処理が単純になります。画像を使う場合はaltを入れ、読み込み失敗時の代替表示も考えるとよいでしょう。
結果: 期待される表示は、前後ボタンで「スライドA」「スライドB」「スライドC」が循環する状態です。
サンプルコード7:モーダルウィンドウ
ただし、モーダルは表示非表示だけでは完成しません。フォーカス移動、Escキー、背景スクロールの扱いなどの注意点があり、初心者向けの段階では最低限の開閉から理解するとよいです。
結果: 期待される表示は、「開く」で設定画面が表示され、「閉じる」で非表示へ戻る状態です。
サンプルコード8:アコーディオンメニュー
同様に、開閉UIではhidden属性やaria-expandedを更新すると、状態を読み取りやすくなります。見た目だけをdisplayで切り替えるより、意味も合わせて変える構成です。
結果: 期待される表示は、詳細ボタンを押すたびに説明文の表示と非表示が切り替わる状態です。
サンプルコード9:タブ切り替え
使い分けると、タブは「現在選ばれている識別子」を状態として持つUIです。datasetに対応先のIDを入れておけば、HTMLとJavaScriptの対応関係が追いやすくなります。
結果: 期待される表示は、AまたはBのボタンを押すと対応する内容だけが表示される状態です。
サンプルコード10:インクリメンタルサーチ
これにより、入力欄の値に合わせて候補を絞り込めます。大量データやAPI検索ではdebounceを入れる注意点がありますが、小さな配列なら即時更新で十分に読めるコードになります。
結果: 期待される表示は、入力文字に一致する果物名だけがリストに残る状態です。
こうした検索UIは、別記事のJavaScriptイベントの使い方と応用例とも相性がよい領域です。イベントハンドラを部品として見れば、入力、絞り込み、描画の責務を分けて改善できます。
注意点と対処法
注意点として特に押さえたいのは、イベントの登録先、伝播、解除、処理頻度です。そのため、意図しない二重実行や画面の重さが出たときは、イベントハンドラの数と発火条件を確認します。
一般に、clickは内側の要素から外側の要素へ伝わりますし、ここがポイントです。この伝播を止めたい場合はstopPropagation()を使えますが、親要素でまとめて処理するイベントデリゲーションを壊す可能性もあるため、使う範囲を限定するのが現実的です。
一方、通常動作を止めたい場面ではpreventDefault()を使いるのが基本です。フォーム送信やリンク遷移を止める処理で使われますが、ユーザーが期待する標準動作まで止めると操作性が落ちるため、必要な条件だけに絞りますし、ここがポイントです。
初心者がつまずきやすいのは、removeEventListenerへ匿名関数を渡しても解除できない点です。削除したいイベントハンドラは、同じ関数参照を渡す必要があると整理できます。
scrollやresizeは短時間に何度も発火するのが目安です。重い処理を直接入れると描画が詰まりやすいため、requestAnimationFrameや間引き処理を検討します。結果: 期待される出力は、ログ有効時に対象ボタンを押すとコンソールへメッセージが出る状態です。
この例では、logClickを名前付き関数にしているため、登録と解除で同じ参照を渡せます。注意点を減らすには、後から外す可能性があるイベントハンドラを匿名関数だけで書き切らない判断が必要になります。
公式ドキュメントによれば、MDNのEvent解説では、イベントがDOM要素やDocument、Windowなどと結びつく仕組みを確認できると理解できるのがポイントです。そのため、対象がElementなのかwindowなのかを見分けることが、原因調査の近道になるのが基本です。
| 問題 | 起こりやすい原因 | 対処法 |
|---|---|---|
| 処理が二重に動く | 初期化が複数回走っている | 登録場所を見直し、必要なら解除する |
| 解除できない | 匿名関数を別物として渡している | 名前付き関数を使う |
| 親の処理も動く | イベントが伝播している | stopPropagationの必要性を確認する |
| 送信されない | preventDefaultで止めている | 条件分岐の位置を見る |
| 画面が重い | 高頻度イベントで描画している | 処理を間引く |
カスタマイズ方法
カスタマイズ方法では、イベントハンドラを単発の反応ではなく、部品同士をつなぐ通知として扱います。カスタマイズサンプルコードを読むときは、発火する側、受け取る側、更新される状態を分けると理解しやすくなります。
具体的には、CustomEventで独自イベントを作る、親要素へイベントデリゲーションを置く、非同期処理と組み合わせる、画像やスクロールに合わせてUIを変える、といった応用例があると覚えるとよいでしょう。初心者向けでも、部品単位に分ければ段階的に読める構成です。
サンプルコード11:カスタムイベント
カスタムイベントは、アプリ内の独自通知を作りたいときに使いるのが一般的です。公式ドキュメントによれば、MDNのCustomEvent解説では、detailへ追加データを持たせられるインターフェースとして扱われています。
結果: 期待される出力は、コンソールに「Satoさんがログインしました」と出る状態です。
サンプルコード12:イベントデリゲーション
その方法では、子要素それぞれにイベントハンドラを付けず、親要素でまとめて受け取ります。動的に追加される項目にも同じ処理が効くため、リストUIでよく使われます。
結果: 期待される表示は、リスト項目を押すたびにdoneクラスの有無が切り替わる状態です。
サンプルコード13:イベントの停止と再開
これを状態管理として扱うと、停止中かどうかをbooleanで保持できます。イベントを完全に削除するのではなく、処理の中で分岐する使い方もあります。
結果: 期待される表示は、一時停止中は加算ボタンを押しても数値が増えない状態です。
サンプルコード14:動的追加と削除
一方、イベントハンドラ自体を必要な期間だけ付けたい場合は、addEventListenerとremoveEventListenerを対にします。カスタマイズサンプルコードとしては、編集モード中だけドラッグを許可するような場面に近いです。
結果: 期待される表示は、編集モード中だけ対象を押すと「選択中」に変わる状態です。
サンプルコード15:非同期処理との連携
非同期処理では、クリック後にfetchやPromiseの完了を待ちます。ただし、外部APIの実在しない応答を前提にすると説明が不正確になるため、下の例ではsetTimeoutで疑似的な待ち時間を作ります。
結果: 期待される表示は、ボタンを押すと一度「読み込み中」になり、その後「データを受け取りました」へ変わる状態です。
サンプルコード16:画像プリロード
画像の読み込みでは、Imageオブジェクトのloadとerrorを使って成功と失敗を分けます。そのため、表示ボタンを押す前に読み込み状態を把握できます。
結果: 期待される表示は、画像の読み込み状態に応じてメッセージが変わり、準備後にボタンで画像が追加される状態です。
サンプルコード17:スムーズスクロール
基本的に、現在のブラウザではscrollIntoViewへbehavior: 'smooth'を渡すだけで滑らかな移動を作れます。複雑な計算を増やす前に、標準APIで足りるかを確認します。
結果: 期待される表示は、リンクを押すと対象セクションまで滑らかに移動する状態です。
サンプルコード18:スクロールイベントとアニメーション
ただし、スクロールイベントで毎回すべての要素を計算すると負荷が増えます。単純な学習用ではgetBoundingClientRectで位置を読み、実務寄りの実装ではIntersectionObserverも候補になります。
結果: 期待される表示は、対象要素が画面内に入ると透明度が上がる状態です。
サンプルコード19:リサイズイベント
リサイズ処理はレスポンシブUIの補助に使えます。一方、表示切り替えだけならCSSの@mediaで済むことが多く、JavaScriptは状態の同期が必要な場面へ絞ると保守しやすくなります。
結果: 期待される表示は、画面幅が768px以上かどうかでラベルが切り替わる状態です。
サンプルコード20:トグルボタン
カスタマイズサンプルコードの最後は、状態に応じてクラスとラベルを同時に変える例です。こうした応用例では、classList.toggleとclassList.containsを組み合わせると読みやすくなります。
結果: 期待される表示は、ボタンを押すたびにdark-modeクラスとボタン文字列が切り替わる状態です。
カレンダーUIのように日付選択とクリック処理が絡む画面では、JavaScriptでカレンダーを作成するサンプルも参考になります。拡張子やファイル名に応じた処理へ広げる場合は、JavaScriptで拡張子を扱う方法と組み合わせると、アップロード前の確認UIを作りやすくなると考えられますし、ここがポイントです。
まとめ
JavaScriptのイベントハンドラは、ユーザー操作と画面更新をつなぐ中核の仕組みです。使い方は、対象要素を取得し、イベント名を選び、処理を関数として渡す流れで整理できるのが目安です。
そのうえで、応用例ではフォーム検証、モーダル、タブ、検索、スクロール、リサイズなどへ広げられます。注意点としては、イベント伝播、通常動作の停止、解除できない匿名関数、高頻度イベントの負荷を確認する必要があると言えるでしょう。
カスタマイズサンプルコードを読むときは、発火元、イベント名、変更される状態を分けて追うと理解しやすくなるのが現実的です。初心者向けの学習でも、addEventListener、event.target、classList、dataset、preventDefaultを押さえれば、多くのUI処理を読み解けるでしょう。
※本記事は実在のエンジニア複数名で構成される Japanシーモア編集部が、AI支援を活用して作成・校正・公開しています。


