web-dev-qa-db-de.com

Wie gehe ich mit einem Klick an einer beliebigen Stelle der Seite um, auch wenn ein bestimmtes Element die Verbreitung stoppt?

Wir arbeiten an einem JavaScript-Tool, das älteren Code enthält, Das gesamte Tool kann also nicht neu geschrieben werden.

Nun wurde ein Menü an der Unterseite hinzugefügt, und der Kunde hätte sehr gerne einen Umschaltknopf zum Öffnen und Schließen des Menüs, außer dass das Schließen automatisch erfolgen muss, wenn ein Benutzer etwas außerhalb des Fensters unternimmt B. ein Menü, wenn ein Benutzer wieder auf die Seite geht und etwas auswählt oder auf ein Formularfeld klickt.

Dies könnte technisch mit einem click -Ereignis auf der body funktionieren, das bei jedem Klick ausgelöst wird Es gibt jedoch zahlreiche Elemente im älteren Code, bei denen ein Click-Ereignis auf einem internen Link behandelt wurde und return false zur Click-Funktion hinzugefügt wurde. damit die Site nicht mit dem Tag href des Links fortfährt.

Eine allgemeine Funktion wie diese funktioniert also eindeutig, jedoch nicht, wenn auf einen internen Link geklickt wird, bei dem die Rückgabe falsch die Ausbreitung stoppt.

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

Gibt es eine Möglichkeit, das Body Click-Ereignis trotzdem zu erzwingen, Oder gibt es eine andere Möglichkeit, das Menü verschwinden zu lassen, indem Sie ein globales Klick-Event oder ähnliches verwenden?

Ohne alle anderen Klicks in der Anwendung, die vor Jahren erstellt wurden, neu schreiben zu müssen ... Das wäre eine Monsteraufgabe, zumal ich keine Ahnung habe, wie ich sie ohne die Rückgabe false umschreiben würde, sie aber trotzdem nicht zulassen würde gehe zu ihrer href.

32
Sander

Ereignisse in modernen DOM-Implementierungen haben zwei Phasen: capturing und bubbling. Die Erfassungsphase ist die erste Phase, die vom defaultView des Dokuments zum Ereignisziel fließt, gefolgt von der Blasenphase, die vom Ereignisziel zurück zum defaultView fließt. Weitere Informationen finden Sie unter http://www.w3.org/TR/DOM-Level-3-Events/#event-flow .

Um die Erfassungsphase eines Ereignisses abzuwickeln, müssen Sie das dritte Argument für addEventListener auf true setzen:

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

Wie Wesley erwähnt hat, kann die Erfassungsphase eines Ereignisses in älteren Browsern leider nicht zuverlässig oder überhaupt nicht gehandhabt werden.

Eine mögliche Lösung besteht darin, stattdessen das mouseup-Ereignis zu behandeln, da die Reihenfolge der Ereignisse für Klicks lautet:

  1. maus nach unten
  2. mouseup
  3. klicken

Wenn Sie sicher sein können, dass Sie keine Handler haben, die das mouseup-Ereignis abbrechen, ist dies ein Weg (und sicherlich ein besserer Weg). Zu beachten ist auch, dass viele (wenn nicht alle) UI-Menüs bei gedrückter Maustaste ausgeblendet werden.

55
Andy E

In Zusammenarbeit mit Andy E ist dies die dunkle Seite der Kraft:

var _old = jQuery.Event.prototype.stopPropagation;

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

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

Denken Sie daran, wenn alle Ereignisse über jQuery gebunden wurden, können Sie diese Fälle einfach hier bearbeiten. In diesem Beispiel rufen wir einfach das Original .stopPropagation() auf, wenn es sich nicht um einen <span> handelt.


Sie können die Verhinderung nicht verhindern, nein.

Sie können diese Ereignishandler manuell in Code umschreiben. Dies ist eine knifflige Angelegenheit, aber wenn Sie wissen, wie Sie auf die gespeicherten Handler-Methoden zugreifen, können Sie dies umgehen. Ich habe ein bisschen herumgespielt, und das ist mein Ergebnis:

$( 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;
});

Beispiel: http://jsfiddle.net/M4teA/

Der obige Code funktioniert nur mit jQuery 1.7. Wenn diese Klickereignisse an eine frühere jQuery-Version oder "Inline" gebunden waren, können Sie den Code zwar weiterhin verwenden, müssen jedoch auf den "alten Handler" anders zugreifen.

Ich weiß, ich gehe davon aus, dass viele Szenariosituationen "perfekte Welt" sind, beispielsweise, dass diese Handles explizit .stopPropagation() aufrufen, anstatt false zurückzugeben. Es könnte also immer noch ein unbrauchbares akademisches Beispiel sein, aber ich hatte das Gefühl, es herauszuholen :-)

edit: hey, return false; wird gut funktionieren, auf die Ereignisobjekte wird auf dieselbe Weise zugegriffen.

6
jAndy

Wenn Sie sicherstellen, dass dies der first -Ereignishandler ist, könnte dies wie folgt funktionieren:

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

Wenn sich auf Ihrer Seite viele Elemente befinden, ist dies wirklich sehr langsam und funktioniert nicht für dynamisch hinzugefügte Elemente.

3
lonesomeday

Was Sie wirklich tun möchten, ist das Binden des Ereignishandlers für die Erfassungsphase des Ereignisses. Soweit ich weiß, wird das in IE jedoch nicht unterstützt, so dass dies möglicherweise nicht so nützlich ist.

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

Verwandte Fragen:

2

Sie können jQuery verwenden, um einen Ereignislistener im Dokument-DOM hinzuzufügen.

    $(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

Ich denke, das ist was du brauchst:

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

Auf diese Weise können Sie das Body-Click-Ereignis von überall aus auslösen.

0
James Johnson

this ist der Schlüssel (vs evt.target). Siehe Beispiel.

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>

0
Ron Royston