ReactJS SyntheticEvent stopPropagation () funktioniert nur mit React-Ereignissen?

126

Ich versuche, event.stopPropagation () in einer ReactJS-Komponente zu verwenden, um zu verhindern, dass ein Klickereignis in die Luft sprudelt und ein Klickereignis auslöst, das mit JQuery im Legacy-Code angehängt wurde. Es scheint jedoch, dass stopPropagation () von React nur die Weitergabe an Ereignisse stoppt wird auch in React angehängt, und stopQropagation () von JQuery stoppt die Weitergabe an mit React angehängte Ereignisse nicht.

Gibt es eine Möglichkeit, stopPropagation () für diese Ereignisse funktionsfähig zu machen? Ich habe eine einfache JSFiddle geschrieben , um diese Verhaltensweisen zu demonstrieren:

/** @jsx React.DOM */
var Propagation = React.createClass({
    alert: function(){
        alert('React Alert');
    },
    stopPropagation: function(e){
        e.stopPropagation();
    },
    render: function(){
        return (
            <div>
                <div onClick={this.alert}>
                    <a href="#" onClick={this.stopPropagation}>React Stop Propagation on React Event</a>
                </div>
                <div className="alert">
                    <a href="#" onClick={this.stopPropagation}>React Stop Propagation on JQuery Event</a>
                </div>
                <div onClick={this.alert}>
                    <a href="#" className="stop-propagation">JQuery Stop Propagation on React Event</a>
                </div>
                <div className="alert">
                    <a href="#" className="stop-propagation">JQuery Stop Propagation on JQuery Event</a>
                </div>
            </div>
        );
    }
});

React.renderComponent(<Propagation />, document.body);

$(function(){    
    $(document).on('click', '.alert', function(e){
        alert('Jquery Alert');
    });

    $(document).on('click', '.stop-propagation', function(e){
        e.stopPropagation();
    });
});
lofiinterstate
quelle
9
Der eigentliche Ereignis-Listener von React befindet sich ebenfalls im Stammverzeichnis des Dokuments. Dies bedeutet, dass das Klickereignis bereits im Stammverzeichnis angezeigt wurde. Sie können event.nativeEvent.stopImmediatePropagationdamit verhindern, dass andere Ereignis-Listener ausgelöst werden, die Ausführungsreihenfolge kann jedoch nicht garantiert werden.
Ross Allen
3
Tatsächlich werden stopImmediatePropagationListener von Anspruchsereignissen in der Reihenfolge aufgerufen, in der sie gebunden waren. Wenn Ihr React JS vor Ihrer jQuery initialisiert wird (wie in Ihrer Geige), funktioniert das Stoppen der sofortigen Weitergabe.
Ross Allen
Reagieren, um zu verhindern, dass jQuery-Listener aufgerufen werden: jsfiddle.net/7LEDT/5 jQuery kann nicht verhindern, dass React aufgerufen wird, da jQuery-Listener später in Ihrer Geige gebunden werden.
Ross Allen
Eine Möglichkeit besteht darin, einen eigenen jQuery-Ereignishandler einzurichten componentDidMount, der jedoch andere React-Ereignishandler auf unerwartete Weise beeinträchtigen kann.
Douglas
Ich erkannte, dass das zweite Beispiel .stop-propagationunbedingt nicht funktionieren wird. In Ihrem Beispiel wird die Ereignisdelegierung verwendet, es wird jedoch versucht, die Weitergabe am Element zu stoppen. Der Listener muss an das Element selbst gebunden sein : $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. Diese Geige verhindert jede Ausbreitung, wie Sie es versucht haben: jsfiddle.net/7LEDT/6
Ross Allen

Antworten:

165

React verwendet die Ereignisdelegierung mit einem einzelnen Ereignis-Listener documentfür Ereignisse, die sprudeln, wie z. B. "Klicken" in diesem Beispiel. Dies bedeutet, dass das Stoppen der Weitergabe nicht möglich ist. Das reale Ereignis hat sich bereits verbreitet, wenn Sie in React mit ihm interagieren. stopPropagationDas synthetische Ereignis von on React ist möglich, da React die interne Ausbreitung synthetischer Ereignisse handhabt.

Arbeiten Sie JSFiddle mit den Korrekturen von unten.

React Stop Propagation auf jQuery-Ereignis

Verwenden Event.stopImmediatePropagationSie diese Option , um zu verhindern, dass Ihre anderen Listener (in diesem Fall jQuery) im Stammverzeichnis aufgerufen werden. Es wird in IE9 + und modernen Browsern unterstützt.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Vorsichtsmaßnahme: Zuhörer werden in der Reihenfolge angerufen, in der sie gebunden sind. Die Reaktion muss vor einem anderen Code (hier jQuery) initialisiert werden, damit dies funktioniert.

jQuery Stop Propagation bei Reaktionsereignis

Ihr jQuery-Code verwendet auch die Ereignisdelegierung. Das Aufrufen stopPropagationdes Handlers stoppt also nichts. Das Ereignis wurde bereits an weitergegeben document, und der Listener von React wird ausgelöst.

// Listener bound to `document`, event delegation
$(document).on('click', '.stop-propagation', function(e){
    e.stopPropagation();
});

Um eine Weitergabe über das Element hinaus zu verhindern, muss der Listener an das Element selbst gebunden sein:

// Listener bound to `.stop-propagation`, no delegation
$('.stop-propagation').on('click', function(e){
    e.stopPropagation();
});

Bearbeiten (14.01.2016): Es wurde klargestellt, dass die Delegierung notwendigerweise nur für Ereignisse verwendet wird, die in die Luft sprudeln. Weitere Informationen zur Ereignisbehandlung finden Sie in der Quelle von React mit beschreibenden Kommentaren: ReactBrowserEventEmitter.js .

Ross Allen
quelle
@ssorellen: Klarstellung: Bindet React seinen Ereignis-Listener documentoder die Root-React-Komponente? Die verlinkte Seite sagt nur "auf der obersten Ebene", was ich damit meine, dass es nicht das ist document(aber ich kann nicht herausfinden, ob dies überhaupt relevant ist).
user128216
2
@FighterJet Das hängt vom Ereignistyp ab. Für Ereignisse, die in die Luft sprudeln, wird die Delegierung auf oberster Ebene verwendet. Bei Ereignissen, die nicht sprudeln, lauscht React unbedingt auf verschiedenen DOM-Knoten. Eine ausführlichere Beschreibung ist in ReactBrowserEventEmitter.js enthalten: github.com/facebook/react/blob/…
Ross Allen
Ich entschuldige mich für die vorübergehende Ablehnung. Ich brauchte es für einen Fehlerbericht und habe es durch ein Upvote ersetzt :)
Glorfindel
Lesen Sie auch Jims Antwort, in der vorgeschlagen wird, den globalen Klick-Listener windowanstelle von document: stackoverflow.com/a/52879137/438273
jsejcksn
13

Bemerkenswert (aus dieser Ausgabe ) , dass , wenn Sie Ereignisse sind angebracht document, e.stopPropagation()ist nicht auf Hilfe gehen. Um dieses Problem zu umgehen, können Sie window.addEventListener()anstelle von verwenden und verhindern document.addEventListener, event.stopPropagation()dass das Ereignis in das Fenster übertragen wird.

Jim Nielsen
quelle
Vielen Dank! Genau das habe ich und diese Lösung funktioniert.
Anh Tran
10

Es ist immer noch ein interessanter Moment:

ev.preventDefault()
ev.stopPropagation();
ev.nativeEvent.stopImmediatePropagation();

Verwenden Sie diese Konstruktion, wenn Ihre Funktion von einem Tag umschlossen ist

römisch
quelle
1
event.nativeEventist die richtige Antwort; Ich konnte so verwenden composedPath():event.nativeEvent.composedPath()
Jimmy
Was meinen Sie wrapped by tag? Könnten Sie bitte ein Beispiel geben?
JimmyLv
@ JimmyLv Ich meine <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
Roman
7

Ich konnte dieses Problem beheben, indem ich meiner Komponente Folgendes hinzufügte:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}
Senornestor
quelle
2
Dadurch wird SynteticEvents vollständig gestoppt, da React die Ereignisdelegierung mit einem einzelnen Ereignis-Listener für Dokumente verwendet, die in die Luft sprudeln.
Vakhtang
5

Ich bin gestern auf dieses Problem gestoßen und habe eine reaktionsfreundliche Lösung erstellt.

Schauen Sie sich den React-Native-Listener an . Bisher funktioniert es sehr gut. Feedback erwünscht.

Erik R.
quelle
5
react-native-listenerVerwendungen, findDomNodedie veraltet sein werden github.com/yannickcr/eslint-plugin-react/issues/…
Bohdan Lyzanets
Abgestimmt, weil Antworten kein geeigneter Ort sind, um externe Lösungen zu vermarkten oder zu liefern, die eine vollständige Antwort nicht ergänzen.
Jimmy
2

Aus der React-Dokumentation : Die folgenden Ereignishandler werden durch ein Ereignis in der Bubbling-Phase ausgelöst . Fügen Sie Capture hinzu , um einen Ereignishandler für die Erfassungsphase zu registrieren . (Betonung hinzugefügt)

Wenn Sie einen Klickereignis-Listener in Ihrem React-Code haben und nicht möchten, dass er in die Luft sprudelt, möchten Sie meiner Meinung nach onClickCapturestattdessen anstelle von verwenden onClick. Dann würden Sie das Ereignis an den Handler übergeben und verhindern event.nativeEvent.stopPropagation(), dass das native Ereignis an einen Vanilla JS-Ereignis-Listener (oder an etwas, das nicht reagiert) sprudelt.

Neil Girardi
quelle
0

Update: Das kannst du jetzt <Elem onClick={ proxy => proxy.stopPropagation() } />

Eric Hodonsky
quelle
1
Siehe die Antwort von @ RossAllen. Dies verhindert nicht die Weitergabe an die nativen DOM-Listener eines Vorfahren (die von jQuery verwendet werden).
Ben Mosher
Dies ist der richtige Weg, wenn Sie eine Reaktionskomponente verwenden. Highjacking das Event ist keine gute Idee.
Eric Hodonsky
Wenn etwas anderes fehlschlägt, liegt es daran, dass Sie etwas anderes falsch machen
Eric Hodonsky
1
Ich bin damit einverstanden, dass dies der richtige Weg ist, wenn alle Ihre Zuhörer über React verbunden sind, aber das ist hier nicht die Frage.
Ben Mosher
Das bin nur ich ... Ich werde immer dazu ermutigen, den richtigen Weg richtig zu verwenden, anstatt jemanden mit einer Umgehung in die Irre zu führen, die ihm in Zukunft Kummer bereiten könnte, weil er eine „Lösung“ für ein Problem hat.
Eric Hodonsky
0

Eine schnelle Problemumgehung wird window.addEventListeneranstelle von verwendet document.addEventListener.

Yuki
quelle