Fork me on GitHub

在React中实现点击空白区域关闭指定元素的实现

从一个简单需求讲起

当我们自定义了一个弹出组件时,一个常见的需求就是要点击除了这块弹出层之外的空白区域将这个弹出层关掉。你可能会说,这还不简单,利用事件冒泡机制,在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
    22
    class 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()事件可不可以呢,因为它不仅能阻止事件冒泡,还能阻止默认事件。事实上还是不行,因为把上面的testClicke打印出来你就会发现根本就没有stopImmediatePropagation方法,当然也没用stopPropagation方法,后面查询资料得知,这些原生方法被放到nativeEvent属性中去了,所以在目标事件中阻止冒泡的代码是:

    1
    2
    3
    4
    // 阻止冒泡
    stopBubble(event) {
    event.nativeEvent.stopImmediatePropagation() //阻止冒泡
    }

最后一步

好了,上面成功阻止了目标事件的冒泡之后,剩下的就是对document添加一个click`事件的监听,在回调中操作state关闭我们的目标元素。实现代码如下:

1
2
3
4
5
6
7
8
componentDidMount() {
// 点击其他地方隐藏输入框
document.addEventListener('click', (e) => {
this.setState({
showInput: false,
})
})
}

当然这只适用于现代浏览器,要兼容IE的话加一下点击事件的浏览器兼容就好了,这里就不赘述了。

总结

其实这本来是个很简单的功能实现,但是因为React对点击事件做了事件代理,所以跟普通页面不太一样,这是需要注意的地方,以上就是我日常开发中一个小坑的总结,如有理解不对之处欢迎指正,最后感谢参考文章中的资料。

参考