React - 阻止子组件触发父组件事件

126

我有一个场景,当父元素被点击时,它会翻转并展示一个不同颜色的子元素。但不幸的是,当用户单击其中一种颜色时,也会触发父元素的“单击”事件。

如何在单击子元素时停止触发父元素上的事件?

我在思考可能的解决方案:

  1. CSS?
    在单击子元素时将pointer-events: none类添加到父元素。然而,这意味着稍后需要清除父元素的pointer-events类。

  2. 使用 Ref?
    记录父React元素的ref,并在单击子元素时将event.target与引用进行比较?我不喜欢这个方法,因为我不喜欢全局ref

希望能提供更好的思路和解决方案。问题是: 如何在单击子元素时停止触发父元素上的事件?

7个回答

244
您可以使用 stopPropagation

stopPropagation - 阻止事件进一步传播到冒泡阶段。

var App = React.createClass({
  handleParentClick: function (e) { 
    console.log('parent');
  },

  handleChildClick: function (e) {
    e.stopPropagation();
    console.log('child');
  },

  render: function() {
    return <div>
      <p onClick={this.handleParentClick}>
        <span onClick={this.handleChildClick}>Click</span>
      </p>
    </div>;
  }
});

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>


9
React Native 中内部元素是一个按钮时无法正常工作。 - Baidyanath Ghosh
如果您无法将E传递给函数,该怎么办? - Mars2024

29
我在React中遇到了同样的问题,并通过这个解决方案解决了它。
if (e.currentTarget != e.target) return;
...

这对我有帮助,但你是不是想要做一个“软”不等于?e.currentTarget !== e.target 对我有效。 - Grant Humphries
兄弟,即使子元素没有点击事件,我的也会传播,为什么会这样? - Suleman Mughal

8
另一种解决方法是在父元素上附加以下事件回调函数:
if(event.target == event.currentTarget){
  event.stopPropagation()
  ....
}

这样你就可以拦截源自所附节点的事件,并将未关联的事件转发到下一个节点。

3

我想在props上调用函数,但同时又希望阻止事件从子组件向父组件传播,以下是如何处理的:

class LabelCancelable extends Component {

  handleChildClick(e) {
    e.stopPropagation()
  }
  closeClicked(e, props) {
    e.stopPropagation();
    props.onCloseClicked()
  }

  render() {
    const {displayLabel} = this.props;
    return (
      <span className={ "label-wrapper d-inline-block pr-2 pl-2 mr-2 mb-2" } onClick={ this.handleChildClick }>
          <button type="button" className="close cursor-pointer ml-2 float-right" aria-label="Close"
              onClick={(e) => this.closeClicked(e, this.props) }>
              <span aria-hidden="true">&times;</span>
          </button>
          <span className="label-text fs-12">
            { displayLabel }
          </span>
      </span>
    );
  }
}

export default LabelCancelable;

2

我认为这个解决方案是最干净的。谢谢JohnsonFashanu

onClick={e => e.currentTarget === e.target && doSomething(e)}

这里是更详细解释的尝试: 当你的鼠标进入父元素时,currentTarget 被设置(事件),然后当它进入子元素时,目标就会改变。如果你不进行检查,由于鼠标离开事件没有触发,父元素的 onClick 就会触发。

2

我刚刚停止了对'child' div上的事件的传递,这样它就能继续传递给子元素。你可以在想要停止点击事件的地方放置这个:

<div onClick={doThisClickEvent}> 
 <div onClick={(e) => e.stopPropagation()}>
  <div>
   <p>This now wont trigger click event</p>
  </div>
 </div>
</div>



0
我在使用Material-UI DataGrid时遇到了问题,并通过以下方式解决:
event.defaultMuiPrevented = true;

e.g:

<DataGrid
  onCellDoubleClick={(params, event) => {
    if (!event.ctrlKey) {
      event.defaultMuiPrevented = true;
    }
  }}
  {...data}
/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接