前回 はdelegateの概念的な説明をしました。
JavaScriptでdelegateをつかうメリットを先生と生徒に例えてご説明しましょう - みかづきブログ その3
今回は実際にdelegateをつくる関数をつくってみようと思います。
仕様を考える
とりあえず必要最小限の機能でつくってみようと思います。
- 実際にイベントを張る要素(前回でいうところの教室)
- イベントを発火させたい要素のセレクタ(前回でいうところの生徒)
- イベント名(前回でいうところの"click")
- コールバック関数
を渡したらデリゲートをつくってくれるように設計してみましょう。
インターフェイスを考える
インターフェイスですが、jQueryのonにならって、
/** * @param parent // 実際にイベントを張る要素 * @param eventName // イベント名 * @param selector // イベントを発火させたい要素のセレクタ * @param callback // コールバック関数 **/ function delegate(parent, eventName, selector, callback) { }
という感じでどうでしょうか。
コードを考える
まずは今回書くコードを言葉で表すとどうなるか考えましょう。
親にイベントを張って、イベントのターゲットがセレクタにマッチしたら、コールバックのthisをターゲットにして実行。
こんな感じでしょうか。
コードを書く
言葉をコードにしてきましょう。
function delegate(parent, eventName, selector, callback) { parent.addEventListener(eventName, function(evt) { // 親要素(教室)にイベントを張る if (_isMatch(selector, evt.target)) { // イベントのターゲットがセレクタにマッチするかチェック(後述) callback.call(evt.target); // コールバックのthisをターゲットに設定して実行 } }, false); } function _isMatch(selector, dom) { var div = doc.createElement("div"); // ダミー要素を作る div.appendChild(dom.cloneNode(false)); // domのコピーをダミー要素にappend return !!div.querySelector(selector); // ダミー要素のquerySelectorの引数にセレクタを渡してnullかどうかを確認 }
イベントのターゲットがセレクタにマッチする処理は切り出してみました。
はじめは正規表現を使おうと思ったのですが、いろんなセレクタに対応させるのがめんどくさそうだったのでダミー要素のquerySelectorで対応しています。
DEMO
querySelectorをつかってしまったのでレガシーブラウザでは動きません。
また、対象の要素が子要素を持っていた際の挙動はjQueryのdelegateとは異なります。
とりあえず今回は以上です。
【追記 その1】
意気揚々とブログを書いてからmatchesの存在を知りました。
function delegate(parent, eventName, selector, callback) { parent.addEventListener(eventName, function(evt) { // 親要素(教室)にイベントを張る if (evt.target.matches(selector, parent)) { // イベントのターゲットがセレクタにマッチするかチェック callback.call(evt.target); // コールバックのthisをターゲットに設定して実行 } }, false); }
これでよかったんですね。勉強になりました。
【追記 その2】
小要素をクリックした際に対応したバージョンをつくりました。