从一个简单需求讲起
当我们自定义了一个弹出组件时,一个常见的需求就是要点击除了这块弹出层之外的空白区域将这个弹出层关掉。你可能会说,这还不简单,利用事件冒泡机制,在document
上加一个监听事件用来关闭这个弹出层,然后在弹出层和呼出弹出层的点击事件上阻止冒泡不就好了嘛!
在React中阻止事件冒泡的正确姿势
嗯,如果在普通html页面中的确是可以这样的,但在react
中项目中,你会发现点你触发目标区域的点击事件时无法阻止冒泡,因为你都找不到event.stopPropagation()
方法。后来查阅文章得知:
React组件中的点击事件都委托给了
document
,这样可以提高效率,所以直接在组件的点击事件里阻止冒泡是没有意义的,为了印证这点,可以看下面的示例代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class MyPomponent extends PureComponent {
constructor(props){
super(props)
}
componentDidMount() {
document.addEventListener('click', (e) => {
console.log(2);
})
document.body.addEventListener('click', (e) => {
console.log(3);
})
}
testClick(e) {
console.log(1);
}
render() {
return (
<div onClick={(e) => this.testClick(e)}>测试组件</div>
)
}
}
// 会依次打印 3 1 2既然既不能直接阻止目标事件的冒泡,那利用
e.stopImmediatePropagation()
事件可不可以呢,因为它不仅能阻止事件冒泡,还能阻止默认事件。事实上还是不行,因为把上面的testClick
的e
打印出来你就会发现根本就没有stopImmediatePropagation
方法,当然也没用stopPropagation
方法,后面查询资料得知,这些原生方法被放到nativeEvent
属性中去了,所以在目标事件中阻止冒泡的代码是:1
2
3
4// 阻止冒泡
stopBubble(event) {
event.nativeEvent.stopImmediatePropagation() //阻止冒泡
}
最后一步
好了,上面成功阻止了目标事件的冒泡之后,剩下的就是对document
添加一个click`
事件的监听,在回调中操作state
关闭我们的目标元素。实现代码如下:1
2
3
4
5
6
7
8componentDidMount() {
// 点击其他地方隐藏输入框
document.addEventListener('click', (e) => {
this.setState({
showInput: false,
})
})
}
当然这只适用于现代浏览器,要兼容IE的话加一下点击事件的浏览器兼容就好了,这里就不赘述了。
总结
其实这本来是个很简单的功能实现,但是因为React
对点击事件做了事件代理,所以跟普通页面不太一样,这是需要注意的地方,以上就是我日常开发中一个小坑的总结,如有理解不对之处欢迎指正,最后感谢参考文章中的资料。