1. ホーム
  2. jquery

[解決済み] jQueryのイベントハンドラは、常にバインドされた順に実行されます - これを回避する方法はありますか?重複

2022-05-16 09:37:31

質問

jQueryのイベントハンドラは、常にバインドされた順番で実行されるのが煩わしい場合があります。例えば

$('span').click(doStuff1);
$('span').click(doStuff2);

をクリックすると doStuff1() が発生し、その後に doStuff2() .

doStuff2()をバインドする際に、オプションで の前に doStuff1()を使っていますが、これを簡単に実行する方法はないようです。

ほとんどの人は、このようにコードを書けばいい、と言うのでしょうね。

$('span').click(function (){
    doStuff2();
    doStuff1();
});

しかし、これは単純な例であり、実際にはそうすることが常に便利であるとは限りません。

イベントをバインドしたいとき、バインド先のオブジェクトがすでにイベントを持っている場合があります。この場合、新しいイベントを他の既存のイベントより先に発生させたいだけかもしれません。

では、jQueryでこれを実現する最善の方法は何でしょうか?

どのように解決するのですか?

回答を更新しました。

jQueryは1.8でイベントが保存される場所を変更しました。内部APIをいじくり回すのがなぜ悪い考えなのか、おわかりいただけたでしょうか :)

新しい 内部 DOMオブジェクトのイベントにアクセスするAPIは、各インスタンスに縛られることなく、グローバルなjQueryオブジェクトを通して利用可能で、第1パラメータとしてDOM要素、第2パラメータとしてキー(私たちは "events" )が渡されます。

jQuery._data(<DOM element>, "events");

というわけで、jQuery 1.8用に修正したコードは以下の通りです。

// [name] is the name of the event "click", "mouseover", .. 
// same as you'd pass it to bind()
// [fn] is the handler function
$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.on(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    this.each(function() {
        var handlers = $._data(this, 'events')[name.split('.')[0]];
        // take out the handler we just inserted from the end
        var handler = handlers.pop();
        // move it at the beginning
        handlers.splice(0, 0, handler);
    });
};

そして、ここでは プレイグラウンド .


オリジナルの回答

Seanが発見したように、jQueryはすべてのイベントハンドラを要素の data インターフェースを通して公開します。具体的には element.data('events') . これを使えば、いつでも特定の位置に任意のイベントハンドラを挿入できる簡単なプラグインを書くことができます。

ここに、リストの最初にハンドラを挿入する簡単なプラグインがあります。任意の位置に項目を挿入するために、これを簡単に拡張することができます。これは単なる配列操作です。しかし、私はjQueryのソースを見ていないし、jQueryのマジックが起こるのを見逃したくないので、通常、ハンドラを追加するには bind を使ってハンドラを追加し、それから配列をシャッフルします。

// [name] is the name of the event "click", "mouseover", .. 
// same as you'd pass it to bind()
// [fn] is the handler function
$.fn.bindFirst = function(name, fn) {
    // bind as you normally would
    // don't want to miss out on any jQuery magic
    this.bind(name, fn);

    // Thanks to a comment by @Martin, adding support for
    // namespaced events too.
    var handlers = this.data('events')[name.split('.')[0]];
    // take out the handler we just inserted from the end
    var handler = handlers.pop();
    // move it at the beginning
    handlers.splice(0, 0, handler);
};

つまり、例えばこのマークアップの場合、次のように動作します ( この例 ):

<div id="me">..</div>

$("#me").click(function() { alert("1"); });
$("#me").click(function() { alert("2"); });    
$("#me").bindFirst('click', function() { alert("3"); });

$("#me").click(); // alerts - 3, then 1, then 2

しかし というのは .data('events') は私が知る限りでは公開 API の一部ではないので、jQuery の更新により、例えば添付イベントの基本的な表現が配列から他のものに変更された場合、あなたのコードが壊れる可能性があります。

免責事項: 何でも可能なので :) 、これがあなたの解決策です。しかし、私なら既存のコードをリファクタリングする側に回ります。