ReactJS SyntheticEvent stopPropagation () działa tylko ze zdarzeniami React?

126

Próbuję użyć zdarzenia event.stopPropagation () w komponencie ReactJS, aby zatrzymać propagację zdarzenia kliknięcia i wyzwolenie zdarzenia kliknięcia, które zostało dołączone do JQuery w starszym kodzie, ale wygląda na to, że stopPropagation () Reacta zatrzymuje tylko propagację do zdarzeń również dołączony w Reakcie, a stopPropagation () JQuery nie zatrzymuje propagacji do zdarzeń dołączonych do React.

Czy istnieje sposób, aby funkcja stopPropagation () działała w przypadku tych zdarzeń? Napisałem prosty JSFiddle, aby zademonstrować następujące zachowania:

/** @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
źródło
9
Rzeczywisty detektor zdarzeń Reacta również znajduje się w katalogu głównym dokumentu, co oznacza, że ​​zdarzenie kliknięcia zostało już przesłane do katalogu głównego. Można użyć, event.nativeEvent.stopImmediatePropagationaby zapobiec uruchamianiu innych detektorów zdarzeń, ale kolejność wykonywania nie jest gwarantowana.
Ross Allen
3
W rzeczywistości stopImmediatePropagationdetektory zdarzeń roszczeń będą wywoływane w kolejności, w jakiej zostały powiązane. Jeśli twój React JS został zainicjowany przed twoim jQuery (tak jak na twoich skrzypcach), zatrzymanie natychmiastowej propagacji zadziała.
Ross Allen,
Reaguj, aby uniemożliwić wywoływanie słuchaczy jQuery : jsfiddle.net/7LEDT/5 Nie jest możliwe, aby jQuery uniemożliwił wywołanie Reacta, ponieważ słuchacze jQuery są związani później w twoich skrzypcach.
Ross Allen
Jedną z opcji byłoby skonfigurowanie własnego modułu obsługi zdarzeń jQuery w programie componentDidMount, ale może to kolidować z innymi programami obsługi zdarzeń React w nieoczekiwany sposób.
Douglas,
Zdałem sobie sprawę, że drugi przykład na pewno .stop-propagationnie zadziała. Twój przykład używa delegowania zdarzeń, ale próbuje zatrzymać propagację w elemencie. Słuchacz musi być związany z samego elementu: $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. To skrzypce zapobiega wszelkiej propagacji, tak jak próbowałeś: jsfiddle.net/7LEDT/6
Ross Allen

Odpowiedzi:

165

React używa delegowania zdarzeń z włączonym jednym nasłuchiwaniem documentdla zdarzeń, które są w dymku, jak na przykład „kliknięcie” w tym przykładzie, co oznacza, że ​​zatrzymanie propagacji nie jest możliwe; rzeczywiste zdarzenie zostało już rozpropagowane do czasu interakcji z nim w Reakcie. stopPropagationna syntetycznym zdarzeniu Reacta jest możliwe, ponieważ React obsługuje wewnętrzną propagację zdarzeń syntetycznych.

Działa JSFiddle z poprawkami od dołu.

React Stop Propagation on jQuery Event

Służy Event.stopImmediatePropagationdo zapobiegania wywoływaniu innych słuchaczy (w tym przypadku jQuery) w katalogu głównym. Jest obsługiwany w IE9 + i nowoczesnych przeglądarkach.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Uwaga: słuchacze są wywoływani w kolejności, w jakiej są związani. Aby to zadziałało, React musi zostać zainicjowany przed innym kodem (tutaj jQuery).

jQuery Stop Propagation on React Event

Twój kod jQuery również używa delegowania zdarzeń, co oznacza, że ​​wywołanie stopPropagationprocedury obsługi niczego nie zatrzymuje; zdarzenie zostało już rozpropagowane do document, a detektor Reacta zostanie uruchomiony.

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

Aby zapobiec propagacji poza element, odbiornik musi być powiązany z samym elementem:

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

Edycja (2016/01/14): Wyjaśniono, że delegacja jest koniecznie używana tylko w przypadku wydarzeń w dymku. Aby uzyskać więcej informacji na temat obsługi zdarzeń, źródło Reacta zawiera opisowe komentarze: ReactBrowserEventEmitter.js .

Ross Allen
źródło
@ssorellen: Wyjaśnienie: czy React wiąże swój detektor zdarzeń documentlub główny komponent React? Strona, do której prowadzi link, mówi tylko „na najwyższym poziomie”, co rozumiem, że nie jest to document(ale nie mogę się dowiedzieć, czy jest to w ogóle istotne).
user128216
2
@FighterJet To zależy od rodzaju wydarzenia. W przypadku wydarzeń, które są bąbelkowe, jest używane delegowanie najwyższego poziomu. W przypadku zdarzeń, które nie pojawiają się, React koniecznie nasłuchuje na różnych węzłach DOM. Bardziej szczegółowy opis znajduje się w ReactBrowserEventEmitter.js: github.com/facebook/react/blob/ ...
Ross Allen
Przepraszam za tymczasowy głos przeciw. Potrzebowałem go do zgłoszenia błędu i zastąpiłem go głosem pozytywnym :)
Glorfindel
Sprawdź również odpowiedź Jima, która sugeruje dodanie globalnego odbiornika kliknięć windowzamiast document: stackoverflow.com/a/52879137/438273
jsejcksn
13

Warto zauważyć (na podstawie tego wydania ), że jeśli dołączasz wydarzenia do document, e.stopPropagation()nie pomoże. Aby obejść ten problem, możesz użyć window.addEventListener()zamiast document.addEventListener, a następnie event.stopPropagation()zatrzyma propagację zdarzenia do okna.

Jim Nielsen
źródło
Dziękuję Ci bardzo! Dokładnie to mam i to rozwiązanie działa.
Anh Tran
10

To wciąż jeden interesujący moment:

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

Użyj tej konstrukcji, jeśli twoja funkcja jest opakowana tagiem

rzymski
źródło
1
event.nativeEventto poprawna odpowiedź; Udało mi się skorzystać composedPath()za jego pośrednictwem:event.nativeEvent.composedPath()
jimmont
Co masz na myśli wrapped by tag? czy możesz podać przykład?
JimmyLv
@JimmyLv Mam na myśli <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
Roman
7

Udało mi się rozwiązać ten problem, dodając następujące elementy do mojego komponentu:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}
senornestor
źródło
2
Spowoduje to całkowite zatrzymanie zdarzeń SynteticEvents, ponieważ React używa delegowania zdarzeń z pojedynczym nasłuchiwaniem zdarzeń w dokumencie dla zdarzeń w bańce.
Vakhtang
5

Wczoraj napotkałem ten problem, więc stworzyłem rozwiązanie przyjazne Reactowi.

Wypróbuj reagować na natywne-nasłuchiwanie . Jak dotąd działa bardzo dobrze. Opinie mile widziane.

Erik R.
źródło
5
react-native-listenerzastosowania, findDomNodektóre zostaną wycofane github.com/yannickcr/eslint-plugin-react/issues/…
Bohdan Lyzanets
Negocjowane, ponieważ odpowiedzi nie są odpowiednim miejscem na rynek lub dostarczają zewnętrznych rozwiązań, które nie uzupełniają pełnej odpowiedzi.
Jimmont
2

Z dokumentacji Reacta : poniższe programy obsługi zdarzeń są wyzwalane przez zdarzenie w fazie propagacji . Aby zarejestrować procedurę obsługi zdarzeń dla fazy przechwytywania, dołącz Capture . (podkreślenie dodane)

Jeśli masz odbiornik zdarzeń kliknięcia w swoim kodzie Reacta i nie chcesz, aby był widoczny, myślę, że onClickCapturezamiast tego chcesz go użyć onClick. Następnie przekazałbyś zdarzenie do modułu obsługi i zrobiłbyś, event.nativeEvent.stopPropagation()aby natywne zdarzenie nie przepływało do zwykłego nasłuchiwania zdarzeń JS (lub czegokolwiek, co nie reaguje).

Neil Girardi
źródło
0

Aktualizacja: możesz teraz <Elem onClick={ proxy => proxy.stopPropagation() } />

Eric Hodonsky
źródło
1
Zobacz odpowiedź @ RossAllen. Nie zapobiegnie to propagacji do natywnych odbiorników DOM przodka (których używa jQuery).
Ben Mosher,
Cóż, to jest właściwy sposób, jeśli używasz komponentu reagującego. Highjacking nie jest dobrym pomysłem.
Eric Hodonsky
Jeśli coś innego zawodzi, to dlatego, że robisz coś innego źle
Eric Hodonsky
1
Zgadzam się, że jest to właściwy sposób, jeśli wszyscy twoi słuchacze są podłączeni przez React, ale nie o to tutaj chodzi.
Ben Mosher
To tylko ja ... Zawsze będę zachęcać do właściwego używania właściwego sposobu, zamiast sprowadzać kogoś na manowce pracą, która może przysporzyć mu smutku w przyszłości, ponieważ ma „rozwiązanie” problemu.
Eric Hodonsky
0

Szybkim obejściem jest użycie window.addEventListenerzamiast document.addEventListener.

Yuki
źródło