こんにちは、かつコーチです。
前回は、JavaScriptからスタイルを操作する方法を解説しました。
今回のテーマは「イベント」です。
ボタンをクリックしたら何かが起きる、フォームに入力したら文字数が表示される。
こういった「ユーザーの操作をきっかけに処理を実行する」仕組みを作るために欠かせないのが addEventListener です。
この記事では、基本の書き方からよくあるつまずきポイントまで、順番に解説していきます。
addEventListenerとは?
イベントとイベントリスナーの関係
イベントとは、クリックやキー入力、マウスの移動など「ブラウザ上で起きる出来事」のことです。
イベントリスナーとは、そのイベントが発生したときに「実行してほしい処理」をあらかじめ登録しておく仕組みのことです。
addEventListener は、この「どの要素に」「どんなイベントが起きたら」「何を実行するか」を指定するためのメソッドです。
なぜonclick属性ではなくaddEventListenerを使うのか
HTMLタグに直接 onclick="..." と書く方法もありますが、実務ではほとんど使いません。
理由は主に2つあります。
1つ目は、HTMLとJavaScriptの処理が混ざってしまい、コードが読みにくくなることです。
2つ目は、1つの要素に対して複数の処理を登録できないことです。
onclick 属性は上書きされてしまうため、後から追加した処理が前の処理を消してしまいます。
一方 addEventListener は、同じ要素・同じイベントに対して複数のリスナーを登録できるので、実務での拡張性が段違いに高くなります。
基本の書き方
手順1:要素を取得する
まずはイベントを設定したい要素を取得します。
const button = document.querySelector("#myButton");
querySelector を使うと、CSSセレクタと同じ書き方で要素を1つ取得できます。
手順2:addEventListenerでイベントを登録する
取得した要素に対して、addEventListener でイベントを登録します。
const button = document.querySelector("#myButton");
button.addEventListener("click", function () {
console.log("ボタンがクリックされました");
});
addEventListener の第1引数には「イベントの種類」を文字列で指定します。
第2引数には「イベントが発生したときに実行する関数」を指定します。
この関数のことをコールバック関数と呼びます。
手順3:アロー関数で書く
コールバック関数はアロー関数で書くこともできます。
現場のコードでは、こちらの書き方の方がよく見られます。
const button = document.querySelector("#myButton");
button.addEventListener("click", () => {
console.log("ボタンがクリックされました");
});
書き方が変わっただけで、動きはまったく同じです。
よく使うイベントの種類一覧
代表的なイベントの種類をまとめておきます。
| イベント名 | 発生するタイミング |
|---|---|
click | 要素をクリックしたとき |
input | フォームの値が入力・変更されたとき |
submit | フォームが送信されたとき |
keydown | キーが押されたとき |
mouseover | マウスが要素の上に乗ったとき |
load | ページの読み込みが完了したとき |
まずは click と input の2つを使いこなせれば、多くの場面で困りません。
つまずきやすい設定・注意点
イベントオブジェクトを受け取る
コールバック関数の引数には、発生したイベントの情報が入ったイベントオブジェクトを受け取ることができます。
const button = document.querySelector("#myButton");
button.addEventListener("click", (event) => {
console.log(event.target);
// クリックされた要素そのものが取得できる
});
event.target を使うと、実際に操作された要素を特定できます。
複数のボタンで同じ処理を共通化したいときなどに、非常によく使うテクニックです。
要素が取得できていないとエラーになる
かつコーチが実際につまずいた話をします。
HTMLファイルの <head> 内に <script> タグを書いて、そのままボタンにイベントを設定しようとしたときのことです。
const button = document.querySelector("#myButton");
button.addEventListener("click", () => {
console.log("クリックされました");
});
コンソールには次のエラーが表示されました。
Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
原因は、<script> タグが実行されるタイミングで、まだ #myButton がHTML上に読み込まれていなかったことです。
document.querySelector は、その時点でDOMに存在しない要素を探すと null を返します。
null に対して addEventListener を呼び出そうとしたので、エラーになったというわけです。
❌ Before:DOMの読み込みを待たずに要素を取得する
// <head>内にそのまま書いた場合、まだbodyが読み込まれていない
const button = document.querySelector("#myButton");
button.addEventListener("click", () => {
console.log("クリックされました");
});
// → Uncaught TypeError: Cannot read properties of null
✅ After:DOMContentLoadedを使って読み込み完了を待つ
document.addEventListener("DOMContentLoaded", () => {
const button = document.querySelector("#myButton");
button.addEventListener("click", () => {
console.log("クリックされました");
});
});
DOMContentLoaded イベントは、HTML全体の読み込みと解析が完了したタイミングで発生します。
このイベントの中で要素取得とイベント登録を行えば、「要素がまだ存在しない」というエラーを防げます。
なお <script> タグを </body> の直前に置く方法でも同じ問題を回避できますが、DOMContentLoaded を使う書き方の方が、スクリプトの置き場所に依存しないので安全です。
応用・一歩先の使い方
removeEventListenerでイベントを解除する
登録したイベントは、removeEventListener で解除できます。
const button = document.querySelector("#myButton");
function handleClick() {
console.log("1回だけ実行したい処理");
button.removeEventListener("click", handleClick);
}
button.addEventListener("click", handleClick);
ここでのポイントは、コールバック関数を無名関数ではなく名前付きの関数として定義しておくことです。
removeEventListener は、登録したときと同じ関数を指定しないと解除できません。
無名関数やアロー関数をその場で渡してしまうと、後から解除できなくなるので注意しましょう。
onceオプションで1回だけ実行する
「1回だけ実行して自動的に解除したい」という場合は、第3引数に { once: true } を渡す方法もあります。
const button = document.querySelector("#myButton");
button.addEventListener(
"click",
() => {
console.log("1回だけ実行されます");
},
{ once: true }
);
こちらの方がコードがシンプルになるので、単発の処理であればこの書き方がおすすめです。
まとめ
この記事のポイント
addEventListenerは「どの要素に」「どんなイベントが」「何を実行するか」を登録する仕組みonclick属性より複数のリスナーを登録できるaddEventListenerが実務では基本- 要素取得が
nullになるエラーは、DOMContentLoadedで読み込み完了を待つことで防げる removeEventListenerで解除する場合は、コールバック関数を名前付きで定義しておく
次に読むべき記事
イベントの基本を押さえたら、次は「イベントがどう伝わっていくか」という仕組みを理解しましょう。
→ 次の記事:イベントの伝播:バブリングとキャプチャリング
