ReactJS SyntheticEvent stopPropagation () funziona solo con gli eventi React?


126

Sto provando a utilizzare event.stopPropagation () all'interno di un componente ReactJS per impedire a un evento click di ribollire e innescare un evento click che era allegato a JQuery nel codice legacy, ma sembra che stopPropagation () di React interrompa solo la propagazione agli eventi anche allegato in React, e stopPropagation () di JQuery non interrompe la propagazione agli eventi allegati con React.

C'è un modo per far funzionare stopPropagation () in questi eventi? Ho scritto un semplice JSFiddle per dimostrare questi comportamenti:

/** @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();
    });
});

9
L'effettivo listener di eventi di React è anche alla radice del documento, il che significa che l'evento click è già stato gorgogliato alla radice. È possibile utilizzare event.nativeEvent.stopImmediatePropagationper impedire l'attivazione di altri listener di eventi, ma l'ordine di esecuzione non è garantito.
Ross Allen

3
In realtà, i stopImmediatePropagationlistener di eventi attestazioni verranno chiamati nell'ordine in cui sono stati associati. Se il tuo React JS viene inizializzato prima di jQuery (come nel tuo violino), l'arresto della propagazione immediata funzionerà.
Ross Allen

Reagisci bloccando la chiamata dei listener jQuery: jsfiddle.net/7LEDT/5 Non è possibile per jQuery impedire che React venga chiamato perché i listener jQuery vengono associati più tardi nel tuo violino.
Ross Allen

Un'opzione potrebbe essere quella di impostare il proprio gestore di eventi jQuery componentDidMount, ma potrebbe interferire con altri gestori di eventi React in modi inaspettati.
Douglas

Mi sono reso conto che il secondo esempio su .stop-propagationnecessariamente non funzionerà. Il tuo esempio utilizza la delega degli eventi ma sta tentando di interrompere la propagazione nell'elemento. L'ascoltatore ha bisogno di essere vincolato all'elemento stesso: $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. Questo violino impedisce tutta la propagazione come stavi provando: jsfiddle.net/7LEDT/6
Ross Allen,

Risposte:


165

React utilizza la delega degli eventi con un singolo listener di eventi attivato documentper gli eventi che si muovono in bolla, come "clic" in questo esempio, il che significa che non è possibile interrompere la propagazione; l'evento reale si è già propagato nel momento in cui interagisci con esso in React. stopPropagationsull'evento sintetico di React è possibile perché React gestisce internamente la propagazione degli eventi sintetici.

Lavorare con JSFiddle con le correzioni dal basso.

React Stop Propagation on jQuery Event

Da utilizzare Event.stopImmediatePropagationper impedire che gli altri listener (jQuery in questo caso) sulla radice vengano chiamati. È supportato in IE9 + e nei browser moderni.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Avvertenza: gli ascoltatori vengono chiamati nell'ordine in cui sono vincolati. React deve essere inizializzato prima dell'altro codice (jQuery qui) affinché funzioni.

jQuery Arresta la propagazione in caso di reazione

Il tuo codice jQuery utilizza anche la delega degli eventi, il che significa che chiamare stopPropagationil gestore non interrompe nulla; l'evento si è già propagato documente l'ascoltatore di React verrà attivato.

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

Per impedire la propagazione oltre l'elemento, l'ascoltatore deve essere associato all'elemento stesso:

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

Modifica (2016/01/14): chiarito che la delega è necessariamente utilizzata solo per gli eventi di quella bolla. Per maggiori dettagli sulla gestione degli eventi, la fonte di React ha commenti descrittivi: ReactBrowserEventEmitter.js .


@ssorellen: Chiarimento: React collega il suo ascoltatore di eventi documento il componente React radice? La pagina collegata dice semplicemente "al livello più alto", il che intendo significare che non è il document(ma non riesco a capire se questo sia rilevante).
user128216

2
@FighterJet Dipende dal tipo di evento. Per gli eventi con bolle, viene utilizzata la delega di primo livello. Per gli eventi che non fanno bolle, React ascolta necessariamente su vari nodi DOM. Una descrizione più approfondita è inclusa in ReactBrowserEventEmitter.js: github.com/facebook/react/blob/…
Ross Allen

Le mie scuse per il voto negativo temporaneo. Ne avevo bisogno per una segnalazione di bug e l'ho sostituito con un voto positivo :)
Glorfindel

Verificate anche la risposta di Jim, che suggerisce di aggiungere il clic ascoltatore globale window, invece di document: stackoverflow.com/a/52879137/438273
jsejcksn

13

Vale la pena notare (da questo numero ) che se stai allegando eventi a document, e.stopPropagation()non ti aiuterà. Come soluzione alternativa, puoi usare window.addEventListener()invece di document.addEventListener, quindi event.stopPropagation()interromperà la propagazione dell'evento alla finestra.


Grazie mille! Questo è esattamente quello che ho e questa soluzione funziona.
Anh Tran

10

È ancora un momento interessante:

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

Usa questa costruzione, se la tua funzione è racchiusa in tag


1
event.nativeEventè la risposta corretta; Sono stato in grado di utilizzare composedPath():event.nativeEvent.composedPath()
jimmont

Cosa intendi wrapped by tag? potresti fornire un esempio?
JimmyLv

@JimmyLv Voglio dire <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
Roman

7

Sono stato in grado di risolvere questo problema aggiungendo quanto segue al mio componente:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}

2
Questo interromperà completamente SynteticEvents, perché React usa la delega degli eventi con un singolo listener di eventi sul documento per gli eventi che compongono la bolla.
Vakhtang


2

Dalla documentazione di React : I gestori di eventi di seguito vengono attivati da un evento nella fase di bubbling . Per registrare un gestore eventi per la fase di acquisizione, aggiungi Capture . (enfasi aggiunta)

Se hai un listener di eventi click nel tuo codice React e non vuoi che si diffonda, penso che quello che vuoi fare sia usare onClickCaptureinvece di onClick. Quindi passeresti l'evento al gestore e faresti event.nativeEvent.stopPropagation()per impedire all'evento nativo di ribollire a un listener di eventi JS vanilla (o qualsiasi cosa che non reagisca).


0

Aggiornamento: ora puoi <Elem onClick={ proxy => proxy.stopPropagation() } />


1
Vedi la risposta di @ RossAllen. Ciò non impedirà la propagazione ai listener DOM nativi di un antenato (che utilizza jQuery).
Ben Mosher

Bene, questo è il modo giusto se stai usando un componente di reazione. Fare il furto dell'evento non è una buona idea.
Eric Hodonsky

Se qualcos'altro non funziona, è perché stai facendo qualcos'altro di sbagliato
Eric Hodonsky

1
Sono d'accordo che questo è il modo corretto se tutti i tuoi ascoltatori sono collegati tramite React, ma non è questo il problema qui.
Ben Mosher,

Sono solo io ... incoraggerò sempre l'uso corretto del modo corretto invece di portare qualcuno fuori strada con un lavoro che potrebbe causare loro dolore in futuro perché hanno una "soluzione" a un problema.
Eric Hodonsky

0

Una soluzione rapida è usare window.addEventListenerinvece di document.addEventListener.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.