JavaScript
jQuery

JavaScriptで先に処理を実行できるようイベントリスナを登録する


おことわり

3年前に書いたこの記事ですが、未だにいいねをしてくれる人がいて光栄です。

ただ、この記事で書かれてる内容はあくまで既存JavaScriptが変更できない時のための応急処置用です。

クライアントサイドのパフォーマンスに思いっきり影響するのであまり多用しないようにしてください。

以上 2019/2/26追記


前書き

JavaScriptで何かイベントがあった時に、すでに登録されているイベントリスナより先に処理を実行する方法を考えた。普通はイベントリスナの登録順を書き換えて実行順をいじればいいんだけど、それができない場合もあるため。


やり方

以下のコードを参照。

この例ではbutton要素が押された時に、buttonに登録されたイベントリスナが実行される前にalertが発生する。

function cap_event(e){

if($(e.target).is("button")){ //ここで要素を指定
//実行したい処理を書く
alert("button " + e.target.id + " clicked");
}
}
window.addEventListener('click',cap_event,true);

ターゲットの指定がやりやすいのでjQueryを利用している。jQueryはなくてもできる。

ポイントはwindowに対してイベントリスナを登録しているところと、登録時に3番目の引数(useCapture)をtrueにしているところ。

参考:

http://qiita.com/tkengo/items/39a9ecd426e53d6bd513


理屈

なぜこれで処理が最初に実行されるかという話だけど、JavaScriptにおけるイベントの伝搬順を利用してる。

JavaSrciptでは、キャプチャフェーズ→ターゲットフェーズ→バブリングフェーズの順でイベントリスナ実行される。(図はW3Cのサイトから引用)

image

onclickなんかは基本的にターゲットフェーズ・バブリングフェーズで実行されるようで、キャプチャフェーズに実行するようイベントリスナを登録(useCaptureをtrueに設定)すればその処理は先に実行される。

しかし、ターゲットとなる要素に対しては、ターゲットフェーズの処理しか起きないっぽい(この辺はもしかしたらブラウザの仕様によるかも)。以下のコードのようにターゲット要素に対してキャプチャフェーズにイベントリスナを設定しても、先に別のイベントリスナを登録していたら、それが先に実行される。

var b = document.getElementById('bid');

b.addEventListener('click',process1);
b.addEventListener('click',process2,true); //キャプチャフェーズのイベント
//button bid をクリックするとprocess1→process2の順で実行される

そこで今回のやり方ではwindowのキャプチャフェーズで、ターゲットを判別して先に実行させたい処理を実行させている。windowのキャプチャフェーズには一番最初にイベントが伝搬するので、一番最初に実行される。

function cap_event(e){

if($(e.target).is("#bid")){
alert("button " + e.target.id + " clicked");
}
}
window.addEventListener('click',cap_event,true);

参考:

http://www.w3.org/TR/DOM-Level-3-Events/

http://qiita.com/hosomichi/items/49500fea5fdf43f59c58