web-dev-qa-db-ja.com

単一要素上の複数のJSイベントハンドラー

私は既存のWebアプリで作業しています。アプリにはさまざまなフォームにさまざまな送信ボタンがあり、通常のhttp投稿を使用するもの、onClick関数を定義するもの、およびのクラスを使用してjsイベントハンドラーをボタンにバインドするものがあります素子。

私がしたいことは、ボタンにクラスを追加するだけでこれらのボタンに別のイベントハンドラをバインドすることですが、決定したいのは、新しいイベントハンドラが実行されることが保証されるか、フォーム送信アクションのいずれかが発生する可能性があることです新しい機能がヒットしないことを意味する前に。

シナリオ例では、これらのボタンにクラスを追加して、すべてのAPIへの使用を単純に記録する一般的なjs関数にそれらをすべてバインドしたいと考えています。フォーム送信がページから移動したためにロギング機能が呼び出されないリスクはありますか?

私は多くのjs開発を行っていません。これを100回テストし、それを実行するだけで幸運になれます。


以下は、例の1つでテストしたコードです-繰り返しますが、複数のイベントをバインドする方法を尋ねるのではなく、仕様の理解とすべてのハンドラーの実行が保証されるかどうかに関する質問です。

_$(document).ready(function(){
    $('.testingBtn').click(function() {
        window.location.replace("http://stackoverflow.com");
    });
    $( ".testingBtn" ).click(function(){
        alert('submitting!');
    });
});


<input class="testingBtn" type="submit" id="submitform" value="Complete Signup" />
_

上記のように、複数のイベントをバインドできます。この例では、別のURLにリダイレクトされますが、これはform.submit()などになります。テストでは、常に最初にアラートが発生しますが、レース条件で幸運を得る?

26
rhinds

JSでは、reallyはイベントハンドラーが呼び出される順序を制御できませんが、慎重な委任と適切に配置されたリスナーを使用すると、可能です。

委任は、イベントモデルの最も強力な機能の1つです。ご存じかもしれませんが、JSでは、イベントはdomの最上部に渡され、そこからイベントが適用される要素まで伝播します。したがって、グローバルオブジェクトにアタッチされたイベントリスナーは、そのハンドラーpriorを要素自体にアタッチされたリスナーに呼び出します。 。

window.addEventListener('click',function(e)
{
    e = e || window.event;
    var target = e.target || e.srcElement;
    console.log('window noticed you clicked something');
    console.log(target);//<-- this is the element that was clicked
}, false);//<-- we'll get to the false in a minute

ハンドラー内のイベントオブジェクトに実際にアクセスできることに注意することが重要です。この場合、イベントオブジェクトはそのままにしておきます。そのため、ターゲットに伝播し続け、途中で次のようなものに遭遇する可能性があります。

document.getElementById('container').addEventListener('click', function(e)
{
    e = e || window.event;
    var target = e.target || e.srcElement;
    if (target.tagName.toLowerCase() !== 'a' || target.className.match(/\bclickable\b/))
    {
        return e;//<return the event, unharmed
    }
    e.returnValue = false;
    if (e.preventDefault)
    {
        e.preventDefault();
    }
}, false);

これで、このハンドラーは、ウィンドウレベルのリスナーがヘルパーを呼び出した後に呼び出されます。今回は、クリックされた要素にclickableクラスがない場合、または要素がリンクである場合、イベントが変更されます。イベントはキャンセルされますが、まだ存続しています。このイベントは、domのさらに下へと自由に伝播できるため、次のようなものに遭遇する可能性があります。

document.getElmentById('form3').addEventListener('click',function(e)
{
     e = e || window.event;
     if (e.returnValue === false || e.isDefaultPrevented)
     {//this event has been changed already
         //do stuff, like validation or something, then you could:
         e.cancelBubble = true;
         if (e.stopPropagation)
         {
             e.stopPropagation();
         }
     }
}, false);

ここで、stopPropagationを呼び出すことにより、イベントは強制終了されます。イベントが既に変更されていない限り、domをさらに下ってターゲットに伝播することはできません。そうでない場合、イベントオブジェクトはDOMをさらに下に移動し、何も起こらなかったようになります。

ターゲットノードに到達すると、イベントは2番目のフェーズであるバブルフェーズに入ります。 DOMの深部に伝播するのではなく、トップレベルに戻ります(グローバルオブジェクト、ディスパッチされた場所、それがどこから来たのかなど)。

バブルフェーズでは、伝播フェーズと同じルールがすべて適用されますが、逆の場合のみです。イベントオブジェクトは、ターゲット要素に最も近い要素を最初に検出し、グローバルオブジェクトを最後に検出します。

ここには多くの便利でわかりやすい図があります 。私はそれを良い 'ol quirksmodeより良いものにすることはできないので、彼らがそこに言っていることを読むことをお勧めします。

結論:2つのイベントリスナーを扱う場合は、両方を異なるレベルでアタッチして、好みの方法で並べ替えます。

両方が呼び出されることを保証する場合は、最後に呼び出されるハンドラーでのイベントの伝播のみを停止します。

同じイベントの同じ要素/オブジェクトにアタッチされた2つのリスナーがある場合、最初にアタッチされたリスナーが最初に呼び出されないという状況に出くわすことはありません。

それだけで、私は理にかなっていることを望んで、私は寝に行きます

51

jQueryはこれを簡単にします。

$(document).on('click', '.someclass', function() {
  doStuff();
});

$(document).on('click', '.someclass', function() {
  doMoreStuff();
});

ハンドラーは、クリックすると両方が起動します。 jQueryは、ハンドラーのキューを保持します。また、選択したセレクターに一致するドキュメントのクリックを処理するため、ボタンが作成されたときに関係なくトリガーできます。

9
Alex Wayne

私はこれと同様の問題を抱えています/抱えていました。ただし、既存の「クリック」イベント(Wicketフレームワークによって追加される)の順序に影響を与えることはできません。しかし、私はまだ新しいカスタムイベントを実行する必要があります  フレームワークによって処理される「クリック」または「変更」イベントのいずれか。

幸いなことに、実際に順番に実行されるイベントがいくつかあります。 「mousedown」と「mouseup」は、「click」の前に発生します。
http://en.wikipedia.org/wiki/DOM_events

$(document).on('mousedown', function (event) {
  event = event || window.event
  var target = event.target || event.srcElement;
  console.log(target + ' before default event'); // Hold mouse button down to see this message in console before any action is executed
});

OR

$(document).on('mouseup', function (event) {
  event = event || window.event
  var target = event.target || event.srcElement;
  alert(target + ' before default event'); // You may not notice this event fires when the page changes unless this is an alert
});

これにより、実際のイベントが実行される前に、ロギングが(例えば、ajax経由で)行われます。 (ajax)リンク経由のページ変更。

もちろん、追加のイベント処理を行う必要があるかどうかを検出するためのより洗練された手段が必要になる場合がありますが、たとえばこのための「ターゲット」情報を使用できます。 =>このスクリプトは、ページ上のすべてを監視します。これが、これを行うために必要な方法です。

4