ReactJS SyntheticEvent stopPropagation () работает только с событиями React?

126

Я пытаюсь использовать event.stopPropagation () в компоненте ReactJS, чтобы остановить событие щелчка от всплытия и запуска события щелчка, которое было прикреплено с JQuery в устаревшем коде, но похоже, что stopPropagation () React только останавливает распространение на события также прикреплен к React, а stopPropagation () JQuery не останавливает распространение событий, связанных с React.

Есть ли способ заставить stopPropagation () работать с этими событиями? Я написал простой JSFiddle, чтобы продемонстрировать это поведение:

/** @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
источник
9
Фактический прослушиватель событий React также находится в корне документа, что означает, что событие щелчка уже перешло в корень. Вы можете использовать, event.nativeEvent.stopImmediatePropagationчтобы предотвратить запуск других прослушивателей событий, но порядок выполнения не гарантируется.
Росс Аллен
3
Фактически, stopImmediatePropagationпрослушиватели событий утверждений будут вызываться в том порядке, в котором они были связаны. Если ваш React JS инициализирован до вашего jQuery (как в вашей скрипке), сработает остановка немедленного распространения.
Росс Аллен
Реагируйте, запрещая вызов слушателей jQuery: jsfiddle.net/7LEDT/5 Для jQuery невозможно предотвратить вызов React, потому что слушатели jQuery связаны позже в вашей скрипке.
Росс Аллен
Один из вариантов - настроить собственный обработчик событий jQuery componentDidMount, но он может неожиданным образом мешать работе других обработчиков событий React.
Дуглас
Я понял, что второй пример по .stop-propagationобязательно не сработает. В вашем примере используется делегирование событий, но выполняется попытка остановить распространение элемента. Слушатель должен быть привязан к самому элементу: $('.stop-propagation').on('click', function(e) { e.stopPropagation(); });. Эта скрипка предотвращает любое распространение, как вы пытались: jsfiddle.net/7LEDT/6
Росс Аллен

Ответы:

165

React использует делегирование событий с одним включенным прослушивателем documentсобытий для событий, которые всплывают, например «щелчок» в этом примере, что означает, что остановить распространение невозможно; реальное событие уже распространено к тому моменту, когда вы взаимодействуете с ним в React. stopPropagationна синтетическом событии React возможно, потому что React обрабатывает распространение синтетических событий внутри себя.

Рабочий JSFiddle с исправлениями снизу.

React Stop Propagation on jQuery Event

Используйте, Event.stopImmediatePropagationчтобы предотвратить вызов других ваших слушателей (в данном случае jQuery) в корневом каталоге. Он поддерживается в IE9 + и современных браузерах.

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • Предостережение: слушатели вызываются в том порядке, в котором они связаны. Чтобы это работало, React должен быть инициализирован перед другим кодом (здесь jQuery).

jQuery Остановить распространение при событии React

В вашем коде jQuery также используется делегирование событий, что означает, что вызов stopPropagationобработчика ничего не останавливает; событие уже передано document, и будет запущен слушатель React.

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

Чтобы предотвратить распространение за пределы элемента, слушатель должен быть привязан к самому элементу:

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

Изменить (2016/01/14): Уточнено, что делегирование обязательно используется только для событий, которые всплывают. Для получения дополнительной информации об обработке событий в источнике React есть описательные комментарии: ReactBrowserEventEmitter.js .

Росс Аллен
источник
@ssorellen: Уточнение: связывает ли React свой прослушиватель событий documentили корневой компонент React? На связанной странице просто написано «на верхнем уровне», что, как я понимаю, означает, что это не тот document(но я не могу понять, актуально ли это вообще).
user128216
2
@FighterJet Это зависит от типа события. Для всплывающих событий используется делегирование верхнего уровня. Для событий, которые не всплывают, React обязательно прослушивает различные узлы DOM. Более подробное описание включено в ReactBrowserEventEmitter.js: github.com/facebook/react/blob/…
Росс Аллен,
Приношу свои извинения за временный отрицательный голос. Мне он был нужен для отчета об ошибке, и я заменил его на голосование :)
Глорфиндель
Также ознакомьтесь с ответом Джима, в котором предлагается добавить глобальный прослушиватель кликов windowвместо document: stackoverflow.com/a/52879137/438273
jsejcksn
13

Стоит отметить (из этой проблемы ), что если вы прикрепляете события к document, e.stopPropagation()это не поможет. В качестве обходного пути вы можете использовать window.addEventListener()вместо document.addEventListener, тогда event.stopPropagation()событие не будет распространяться в окно.

Джим Нильсен
источник
Большое спасибо! Это именно то, что у меня есть, и это решение работает.
Ань Тран,
10

Это все еще один интересный момент:

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

Используйте эту конструкцию, если ваша функция обернута тегом

Римский
источник
1
event.nativeEventправильный ответ; Я смог использовать composedPath()через это:event.nativeEvent.composedPath()
jimmont
Что ты имеешь в виду wrapped by tag? не могли бы вы привести пример?
JimmyLv
@JimmyLv Я имею в виду <div onClick=(firstHandler)> <div onClick=(secHandler)>active text</div> </div>
Роман
7

Я смог решить эту проблему, добавив в свой компонент следующее:

componentDidMount() {
  ReactDOM.findDOMNode(this).addEventListener('click', (event) => {
    event.stopPropagation();
  }, false);
}
senornestor
источник
2
Это полностью остановит SynteticEvents, потому что React использует делегирование событий с одним прослушивателем событий в документе для событий, которые всплывают.
Вахтанг
5

Вчера я столкнулся с этой проблемой, поэтому создал удобное для React решение.

Проверьте response-native-listener . Пока он работает очень хорошо. Обратная связь приветствуется.

Эрик Р.
источник
5
react-native-listenerиспользование, findDomNodeкоторое будет устаревшим github.com/yannickcr/eslint-plugin-react/issues/…
Богдан Лизанец
Проголосовали против, потому что ответы не подходят для рынка или предоставления внешних решений, которые не дополняют полный ответ.
Джиммонт
2

Из документации React : Обработчики событий ниже запускаются событием в фазе восходящей цепочки . Чтобы зарегистрировать обработчик событий для фазы захвата, добавьте Capture . (курсив мой)

Если у вас есть прослушиватель событий щелчка в вашем коде React, и вы не хотите, чтобы он всплывал, я думаю, что вы хотите использовать onClickCaptureвместо onClick. Затем вы передадите событие обработчику и сделаете так, event.nativeEvent.stopPropagation()чтобы нативное событие не всплывало до ванильного прослушивателя событий JS (или чего-либо, что не реагирует).

Нил Жирарди
источник
0

Обновление: теперь вы можете <Elem onClick={ proxy => proxy.stopPropagation() } />

Эрик Годонски
источник
1
См. Ответ @ RossAllen. Это не предотвратит распространение на родные прослушиватели DOM предка (которые использует jQuery).
Бен Мошер
Что ж, это правильный путь, если вы используете компонент реакции. Угнать событие - не лучшая идея.
Эрик Годонски
Если что-то еще не
удается
1
Я согласен, что это правильный способ, если все ваши слушатели подключены через React, но вопрос не в этом.
Бен Мошер
Это только я ... Я всегда буду поощрять правильное использование правильного пути вместо того, чтобы вводить кого-то в заблуждение работой, которая может причинить им горе в будущем, потому что у них есть «решение» проблемы.
Eric Hodonsky
0

Быстрый обходной путь - использование window.addEventListenerвместо document.addEventListener.

Yuki
источник