web-dev-qa-db-ja.com

特定の要素が伝播を停止した場合でも、ページ内の任意の場所でクリックを処理するにはどうすればよいですか?

古いコードを含むJavaScriptツールに取り組んでいるので、ツール全体を書き直すことはできません。

現在、メニューは下部に固定された位置に追加され、クライアントは、ユーザーがメニューの外側から何かを開始するときに自動的に閉じる必要があることを除いて、メニューを開閉するトグルボタンを持っていることを非常に好みますたとえば、ユーザーがページに戻って、何かを選択したり、フォームフィールドをクリックしたりします。

これは、clickbodyイベントで技術的に機能し、クリックでトリガーされますが、古いコードには多数のアイテムがあり、クリックイベントは内部リンクで処理され、return falseは、サイトがリンクのhrefタグに進まないようにするために、クリック機能に追加されました。

したがって、明らかに、このような一般的な関数は機能しますが、falseを返すと伝播が停止する内部リンクをクリックした場合は機能しません。

$('body').click(function(){
  console.log('clicked');
});

とにかくボディクリックイベントを強制する方法はありますか、またはグローバルなクリックイベントなどを使用してメニューを非表示にする方法はありますか?

何年も前に作成されたアプリケーションの他のすべてのクリックを書き換える必要はありません。特に、falseを返さずにそれらを書き換える手がかりがないため、それはモンスタータスクになりますが、hrefに移動させないでください。

34
Sander

最新のDOM実装のイベントには、capturingbubblingの2つのフェーズがあります。キャプチャフェーズは、ドキュメントのdefaultViewからイベントターゲットに流れる最初のフェーズです。次に、イベントターゲットからdefaultViewに戻るバブリングフェーズが続きます。詳細については、 http://www.w3.org/TR/DOM-Level-3-Events/#event-flow を参照してください。

イベントのキャプチャ段階を処理するには、addEventListenerの3番目の引数をtrueに設定する必要があります。

document.body.addEventListener('click', fn, true); 

悲しいことに、ウェスリーが述べたように、イベントのキャプチャフェーズは、古いブラウザでは確実に処理できないか、まったく処理できません。

考えられる解決策の1つは、クリックのイベント順序が次のようになるため、代わりにmouseupイベントを処理することです。

  1. マウスダウン
  2. マウスアップ
  3. クリック

mouseupイベントをキャンセルするハンドラーがないことが確実な場合は、これが1つの方法(そしておそらく間違いなくより良い方法)です。もう1つ注意すべきことは、ほとんどではないにしても(すべてではないにしても)多くのUIメニューがマウスを押すと消えることです。

57
Andy E

Andy Eと協力して、これは力の暗い側面です:

_var _old = jQuery.Event.prototype.stopPropagation;

jQuery.Event.prototype.stopPropagation = function() {
    this.target.nodeName !== 'SPAN' && _old.apply( this, arguments );
};     
_

http://jsfiddle.net/M4teA/2/

すべてのイベントがjQueryを介してバインドされている場合は、ここでそれらのケースを処理できることに注意してください。この例では、_<span>_を扱っていない場合、元の.stopPropagation()を呼び出すだけです。


防ぐことはできません、いいえ。

できることは、これらのイベントハンドラをコード内で手動で書き換えることです。これはトリッキーなビジネスですが、保存されたハンドラーメソッドにアクセスする方法を知っている場合は、回避することができます。私はそれを少し遊んでみましたが、これは私の結果です:

_$( document.body ).click(function() {
    alert('Hi I am bound to the body!');
});

$( '#bar' ).click(function(e) {
    alert('I am the span and I do prevent propagation');
    e.stopPropagation();
});

$( '#yay' ).click(function() {
    $('span').each(function(i, elem) {
        var events        = jQuery._data(elem).events,
            oldHandler    = [ ],
            $elem         = $( elem );

        if( 'click' in events ) {                        
            [].forEach.call( events.click, function( click ) {
                oldHandler.Push( click.handler );
            });

            $elem.off( 'click' );
        }

        if( oldHandler.length ) {
            oldHandler.forEach(function( handler ) {
                $elem.bind( 'click', (function( h ) {
                    return function() {
                        h.apply( this, [{stopPropagation: $.noop}] );
                    };
                }( handler )));
            });
        }
    });

    this.disabled = 1;
    return false;
});
_

http://jsfiddle.net/M4teA/

上記のコードはjQuery 1.7でのみ機能することに注意してください。これらのクリックイベントが以前のjQueryバージョンまたは「インライン」にバインドされている場合、コードを使用できますが、「古いハンドラー」に別の方法でアクセスする必要があります。

ここでは、たとえば、これらのハンドルがfalseを返すのではなく、明示的に.stopPropagation()を呼び出すという「完璧な世界」のシナリオを想定していることを知っています。だから、それはまだ役に立たないアカデミックな例かもしれませんが、私はそれを出すように感じました:-)

edit:ちょっと、_return false;_はうまく動作します。イベントオブジェクトは同じ方法でアクセスされます。

6
jAndy

これがfirstイベントハンドラーの動作であることを確認すると、次のようなことができます。

$('*').click(function(event) {
    if (this === event.target) { // only fire this handler on the original element
        alert('clicked');
    }
});

ページに多くの要素がある場合、これは非常に遅くなり、動的に追加されたものに対しては機能しないことに注意してください。

3
lonesomeday

本当にやりたいことは、イベントのキャプチャフェーズのイベントハンドラーをバインドすることです。ただし、これはIEでは私の知る限りサポートされていません。そのため、それほど便利ではないかもしれません。

http://www.quirksmode.org/js/events_order.html

関連する質問:

2

thisはキーです(vs evt.target)。例を参照してください。

document.body.addEventListener("click", function (evt) {
    console.dir(this);
    //note evt.target can be a nested element, not the body element, resulting in misfires
    console.log(evt.target);
    alert("body clicked");
});
<h4>This is a heading.</h4>
<p>this is a paragraph.</p>
2
Ron Royston

JQueryを使用して、ドキュメントDOMにイベントリスナーを追加できます。

    $(document).on("click", function () {
        console.log('clicked');
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
1
Ryan

私はこれがあなたが必要なものだと思う:

$("body").trigger("click");

これにより、どこからでもボディクリックイベントをトリガーできます。

0
James Johnson