はじめに
趣旨は表題のとおりです
「イベントリスナー」説明できますか?
更に「イベントハンドラー」2つの違いと関係を説明できますか?
現在のGUIを持つプログラムの多くはイベント制御を主軸に設計されています。
ですのでボタンや画像を使った凝ったアプリを作りたいという場合には「イベント〇〇」を知る必要があります。
筆者は今でこそ使い分けられているものの、プログラミング入門当時これらの区別がつくまでに数ヶ月を要しました。
数ヶ月の成果をアウトプット練習を兼ねて書き留めたものがコレです。
この記事は、厳密な部分を抜きにした各用語と関係の概要理解が目的です。
枝葉末節の正確性についてはご容赦ください。
本記事は可能な限りプログラミング未経験の方にも分かるように、極力コードを使わない説明を心がけて執筆しました
一部やむを得ない部分はJavascriptによるDOM操作をベースに解説している箇所があるので簡単なJavascriptを書いたことがあると理解が早いと思います。
クリックに対してメッセージを出力するボタンを作ったことがある、位で十分です。
用語
イベント(Event)
日常生活で言う「イベント」は平時に無い特別な催しを指すことが一般的です。
プログラムにおける「イベント」は、プログラム動作中に起きるあらゆる出来事の総称です。
- ボタンがクリックされた
- 文字が入力された
- サーバーから応答が返った
- プログラム起動から一定時間が経った
等々、起きる事全てがイベントです。
イベントハンドラ(Event Handler)
イベントの発生に応じて実行するように紐付けられた処理です。
名前の「Handle」は取っ手や持ち手でなく、「扱う」「担う」「応対する」の意味です。
例えばこの記事の「いいね」ボタンを押すとハートマークが緑色に変化しますよね。
この「緑に変化させる処理」が、ボタンのクリックというイベントに対して紐付けられたイベントハンドラです。
イベントターゲット(Event Target)
イベントリスナーの説明の前に、イベントターゲットを知っておきましょう。
イベントターゲットは主にJavascriptにおける名称です。
言語やライブラリによって名前が異なることがありますが、同等の機能は殆どの言語に存在します。
ある要素Aに対してイベントが起きることを想定して、イベントを検知できるように実装された要素A をイベントターゲットと呼びます。
例えば、ボタンは当然クリックというイベントを受け付ける想定があります。
文字入力欄は、キー入力というイベントを受け付けて、欄に入力された文字をリアルタイムで表示します。
このようにイベントを受け付ける想定がされているボタン要素や入力欄要素は後述のイベントリスナーを登録することでそのイベントを検知し紐づいた動作を行うことができるようになります。
言い換えれば、イベントリスナーを登録する手段を持つものをイベントターゲットということもできます。
イベントリスナー(Event Listener)
イベントターゲットがイベントを検知して制御できる機能を実現する仕組みがイベントリスナーです。
イベントリスナーは発生するイベントの種類と対応するハンドラのセットを携えてイベントターゲットに抱え込まれ、ターゲットにイベントが発生するとハンドラを実行します。
ターゲットは複数のリスナーを持つことができ、様々なイベントに対して反応することができます。
ターゲットが受付窓口であればリスナーは対応マニュアルを持った担当者といったところです。
名称の「Listener」ワードチョイスについてですが、筆者は由来を明記した文献を見たことがありません。
耳を澄ましてイベントの発生通知が聞こえてくるのを期待して耳を澄ますといったニュアンスでしょうか。
イベントディスパッチャー
イベントに耳を澄ますのがイベントリスナーであれば、イベントを発信して聞かせるのがイベントディスパッチャーです。
イベントディスパッチャーを直接操作する機会は、リスナーやハンドラに比べれば比較的少ないかもしれません。
しかしこれ無しではリスナーもハンドラも機能しないため重要な概念であることは言うまでも無いでしょう。
多くの場合においてディスパッチャーの実体は呼び出し可能な関数やメソッドで、引数に発信するイベントを受け付けます。
ディスパッチャーは自分でイベントを作成して扱いたいときに使うことになります。
Javascriptからしばしば利用するWebAPIや、多くのGUIフレームワークにはよく使われるイベントが予め定義されています。
例えば「クリックとはこういう動作である」と自分で定義しなくても
eventTarget.addEventListener("click", (event) => {handler(event)} )
が動くのはそれが理由です。
こういった既成イベントでない独自イベントを作り任意のタイミングで発生させたいという場合にディスパッチャーを呼び出します。
閑話休題: Javascriptにおけるlistenerとhandler
これまで例に出してきたJavascriptですが、イベント処理のコードが易しいため採用しているものの特に名称についてはいくつかややこしいポイントがあります。
筆者が理解に苦しんだ部分を整理しておきます。
Listener
この章ではaddEventListenerの第二引数を英字表記listener、上で説明したイベントリスナーはカタカナ表記リスナーで書き分けます。
直前の例に上げたaddEventListener(type, listener)
はイベントターゲットのメソッドで、名前の通りターゲットにリスナーを登録します。
第二引数に関数やhandleEvent()
メソッドを持つオブジェクトを指定し、これが第一引数で指定するイベントに対応するハンドラになるのですが、ハンドラに相当するはずの第二引数の引数名がlistener
です。
Javascriptではイベントの扱いについて「listenerをハンドラとして呼び出す」ではなく 「イベントの発生通知をlistenerに聞かせる」 という考え方のようです。
視点をlistener側に持てば確かにlistenerはイベント発生を期待して耳を澄ましているリスナーと言えます。
引数のlistenerは俯瞰的立場では(イベントターゲットに登録されるリスナーの)ハンドラであると補正すると良いかもしれません。
handler
Javascriptを書いた経験のある方は
eventTarget.onClick = handler()
という書き方でもイベントに対する処理を記述できることを知っていると思います。
このonClick
はハンドラと呼ばれます。
筆者は最初
「ハンドラはhandler()
の方じゃないの? だって呼び出される処理はhandler()
でしょう?」
と思ったものです。
結論を言うと、onClick
はハンドラで正解です。
一部のイベントについては、イベント発生時に実行される関数がon〇〇
の名前で定義されています。
既定ではこれらはnull
に設定されており、関数に代入する形で本命のハンドラであるhandler()
を設定します。
イベントが発生すると、ハンドラとしてon〇〇
が呼び出されます。そこに本命ハンドラが代入されていれば、それはon〇〇
の名において処理を実行するわけです。
イベントループ
イベント発生の有無を判定してハンドラを呼び出せるように待機するループプログラムです。
下図のようにイベントの監視は一般にループを使います
中でも起動から終了まで動き続けプログラム全体のイベントを統括制御する大きなループをメインループと呼びます。
各々の関係まとめ
- プログラム実行時に起きる色々な出来事をイベントとして定義することで柔軟な動作を実現できる
- イベントハンドラにはイベントが起きたとき実行したい処理を実装する
- イベントリスナーを使ってイベントとイベントハンドラを紐付ける
- イベントディスパッチャーを使って自由にイベントを発生させることができる
- イベントを受け付けるリスナーはイベントが起きる対象であるイベントターゲットに登録する
あとがき
イベントに対応して動く仕組みの実装方法は色々あります。
今回JavascriptのDOMをベースに説明しましたが、言語やフレームワーク、ライブラリによっては上記の説明は当てはまらないことがあります。
しかし1つの例で概念を理解すれば、他の環境においても「Javascriptでいう〇〇にあたる機能」とおおよその見当をつけられることで調べやすくなったり、理解しやすくなるのでは、と筆者は思います。
イベントを扱えるようになると、作れるものがぐんと増えます。
Webサイト、スマホアプリ、ゲーム、Bot。リッチな動きを実装できるようになるとプログラミングは一段と楽しくなるでしょう。
皆さんの「作りたい」を実現できる一助になることを願っています。
ありがとうございました。