【问题标题】:ReactJS SyntheticEvent stopPropagation() only works with React events?ReactJS SyntheticEvent stopPropagation() 仅适用于 React 事件?
【发布时间】:2014-08-16 10:15:26
【问题描述】:

我正在尝试在 ReactJS 组件中使用 event.stopPropagation() 来阻止单击事件冒泡并触发在遗留代码中附加到 JQuery 的单击事件,但似乎 React 的 stopPropagation() 仅停止传播到也附加在 React 中的事件,并且 JQuery 的 stopPropagation() 不会停止传播到 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();
    });
});

【问题讨论】:

  • React 的实际事件监听器也在文档的根目录,这意味着点击事件已经冒泡到根目录。您可以使用event.nativeEvent.stopImmediatePropagation 来阻止其他事件侦听器触发,但不保证执行顺序。
  • 其实stopImmediatePropagation声称事件监听器会按照绑定的顺序被调用。如果你的 React JS 在你的 jQuery 之前被初始化(就像它在你的小提琴中一样),停止立即传播将起作用。
  • React 阻止调用 jQuery 侦听器:jsfiddle.net/7LEDT/5 jQuery 无法阻止调用 React,因为 jQuery 侦听器稍后会在您的小提琴中绑定。
  • 一种选择是在 componentDidMount 中设置您自己的 jQuery 事件处理程序,但它可能会以意想不到的方式干扰其他 React 事件处理程序。
  • 我意识到.stop-propagation 上的第二个示例一定行不通。您的示例使用事件委托,但试图在元素处停止传播。侦听器需要绑定到元素本身:$('.stop-propagation').on('click', function(e) { e.stopPropagation(); });。这个小提琴可以防止像您尝试的所有传播:jsfiddle.net/7LEDT/6

标签: javascript jquery reactjs


【解决方案1】:

React 在document 上使用带有单个事件侦听器的事件委托来处理冒泡事件,例如本例中的“点击”,这意味着无法停止传播;当你在 React 中与它交互时,真实的事件已经传播了。 stopPropagation React 的合成事件是可能的,因为 React 在内部处理合成事件的传播。

使用JSFiddle 进行下面的修复。

在 jQuery 事件上反应停止传播

使用Event.stopImmediatePropagation 防止调用根上的其他(在本例中为jQuery)侦听器。它在 IE9+ 和现代浏览器中受支持。

stopPropagation: function(e){
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
},
  • 警告:侦听器按绑定顺序调用。必须在其他代码(此处为 jQuery)之前初始化 React 才能使其正常工作。

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 年 1 月 14 日): 澄清委派必须仅用于冒泡事件。有关事件处理的更多详细信息,React 的源有描述性 cmets:ReactBrowserEventEmitter.js

【讨论】:

  • @ssoellen:澄清:React 是否将其事件侦听器绑定在 document 或根 React 组件上? linked page 只是说“在顶层”,我认为它不是 document(但我不知道这是否相关)。
  • @FighterJet 这取决于事件类型。对于冒泡的事件,使用顶级委托。对于不冒泡的事件,React 必须监听各种 DOM 节点。 ReactBrowserEventEmitter.js 中包含更详尽的描述:github.com/facebook/react/blob/…
  • 我对临时投反对票表示歉意。我需要它来报告错误,我已经用赞成票代替了它:)
  • 还可以查看 Jim 的回答,它建议将全局点击侦听器添加到 window 而不是 documentstackoverflow.com/a/52879137/438273
【解决方案2】:

这仍然是一个有趣的时刻:

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

如果你的函数被标签包裹,请使用这个结构

【讨论】:

  • event.nativeEvent 是正确答案;我可以通过它使用composedPath()event.nativeEvent.composedPath()
  • wrapped by tag 是什么意思?你能举个例子吗?
  • @JimmyLv 我的意思是&lt;div onClick=(firstHandler)&gt; &lt;div onClick=(secHandler)&gt;active text&lt;/div&gt; &lt;/div&gt;
【解决方案3】:

值得注意(来自this issue),如果您将事件附加到documente.stopPropagation() 将无济于事。作为一种解决方法,您可以使用window.addEventListener() 代替document.addEventListener,然后event.stopPropagation() 将阻止事件传播到窗口。

【讨论】:

  • 非常感谢!这正是我所拥有的,并且此解决方案有效。
【解决方案4】:

我能够通过将以下内容添加到我的组件来解决此问题:

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

【讨论】:

  • 这将完全停止 SynteticEvents,因为 React 使用事件委托和文档上的单个事件侦听器来处理冒泡事件。
【解决方案5】:

来自React documentation: 下面的事件处理程序由冒泡阶段的事件触发。要为捕获阶段注册事件处理程序,请追加捕获。 (强调)

如果您的 React 代码中有一个单击事件侦听器并且您不希望它冒泡,我认为您想要做的是使用 onClickCapture 而不是 onClick。然后您将事件传递给处理程序并执行event.nativeEvent.stopPropagation() 以防止本机事件冒泡到普通 JS 事件侦听器(或任何没有反应的东西)。

【讨论】:

  • 谢谢,截至 2021 年和 React 17,这对我有用。 onClickCapture={e => e.stopPropagation()} 就足够了,不必使用 event.nativeEvent 来停止传播到根。
【解决方案6】:

React 17 将事件委托给 root 而不是 document,这可能会解决问题。 更多详情here。 也可以参考my blog

【讨论】:

  • 几个月前,我在文档上有一个监听器,即使在调用 event.stopPropagation() 后仍会收到 mousedown 事件(我依赖于这种行为),但注意到在我更新后它不再被调用很多图书馆。我怀疑原因是 ReactJS 的更新,您的回答现在证实/解释了它。
【解决方案7】:

我昨天遇到了这个问题,所以我创建了一个对 React 友好的解决方案。

查看react-native-listener。到目前为止,它运行良好。感谢您的反馈。

【讨论】:

【解决方案8】:

更新:你现在可以&lt;Elem onClick={ proxy =&gt; proxy.stopPropagation() } /&gt;

【讨论】:

  • 见@RossAllen 的回答。这不会阻止传播到祖先的本机 DOM 侦听器(jQuery 使用)。
  • 如果您使用的是 react 组件,那么这是正确的方法。劫持事件不是一个好主意。
  • 如果其他事情失败了,那是因为你做错了其他事情
  • 我同意,如果您的所有听众都通过 React 附加,这是正确的方法,但这不是这里的问题。
  • 这只是我...我将始终鼓励正确使用正确的方法,而不是通过一种可能导致他们未来悲伤的工作而将某人误入歧途,因为他们有一个“解决方案”问题。
【解决方案9】:

一种快速的解决方法是使用window.addEventListener 而不是document.addEventListener

【讨论】:

    猜你喜欢
    • 2015-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 2019-07-01
    • 1970-01-01
    • 2013-06-24
    相关资源
    最近更新 更多