React 阻止事件冒泡失效、stopPropagation和stopImmediatePropagation分析,解决stopPropagation没有阻止冒泡问题

15 篇文章 0 订阅

其他前端有趣的例子和坑合集:https://github.com/wqhui/blog
代码链接:https://codepen.io/Lik_Lit/pen/OJLROMW

前言

做开发中,React 和 JS 原生事件监听addEventListener混用了,因此发现了一个现象:React的阻止冒泡并没有阻止原生JS监听的事件触发。
例子中:onClik阻止冒泡事件并没有生效,仅仅只是阻止outClick,我的需求是只触发inner dom click

代码链接:https://codepen.io/Lik_Lit/pen/OJLROMW

例子中有4个click监听:

  1. React 组件内button元素监听innerClick ,也是点击事件的触发者
  2. React 组件内button父元素监听outClick
  3. React 组件渲染根节点的元素监听root click,注意是原生的监听
  4. document的监听document click,注意是原生的监听
const domContainer = document.querySelector('#root')

// 绑定在外层的点击 非documemt层的原生事件
domContainer.addEventListener('click', e => console.log('root click'))

class InnerDom extends React.Component {
  componentDidMount () {
    // documemt层的原生事件
    document.addEventListener('click', () => {
      console.log('document click')
    })
  }

  outClick = (e) => {
    console.log('out dom click')
  }

  innerClick = (e) => {
    e.stopPropagation();
    console.log('inner dom click')
  }

  render () {
    return <div onClick={this.outClick}>
      <button onClick={this.innerClick}> 测试冒泡</button>
    </div>
  }
}

ReactDOM.render(<InnerDom />, domContainer)

猜猜输出是什么?

“root click”
“inner dom click”
“document click”

解决过程与坑

原来 React 的组件不是挂载到对应元素上的,而且被统一管理起来的合成事件,本质上所有的事件绑定是代理到document上的,然后再触发的时候再根据不同元素去分发执行。类似下面:

// react所有合成事件, SyntheticEvent里面执行回调函数
document.addEventListener('click', SyntheticEvent);

// 浏览器原生
document.addEventListener('click', () => {
   alert('document click');
})

所以在 React 绑定的合成事件调用e.stopPropagation()阻止的只是React的合成事件(eg:<div onClick={()=>{}}/>),而对于原生事件还是阻止不了

百度告诉我说e.nativeEvent.stopImmediatePropagation可以阻止冒泡到原生事件,我就在在innerClick事件里加上了这句,输出变成了

“root click”
“inner dom click”

可以看到还是不行,因为stopImmediatePropagation只是阻止同层级且绑定靠后的事件(具体参考MDN),这里只阻止了document层的事件,同时也验证了上面说的本质上所有的事件绑定是代理到document上的的说法。

那非document层的原生事件我们要怎么阻止呢?我这边只能想到笨方法:React要阻止冒泡到原生事件只有使用原生的绑定去调用e.stopPropagation()


  refCb = (dom) => {
      //这里我就没有去解绑了
      dom && dom.addEventListener('click',this.innerClick)
  }
  <button ref={this.refCb} > 测试冒泡</button>

这样就可以解决了

结论

  1. React 中JSX写法绑定的事件都是合成事件,它们本质上是代理到document的事件,这就相当于事件委托,当某个事件触发时,该事件会冒泡到document上面,React监听到这次事件开始统一分发合成事件给监听的回调函数处理(可以理解成React所有事件都是绑定在document层,React V17之后是绑定到root节点层)
  2. 对于React的合成事件对象e,e.stopPropagation()只能阻止React合成事件的冒泡,e.nativeEvent.stopImmediatePropagation 只能用来阻止冒泡到直接绑定在document上的事件
  3. 如果代码中存在JSX写法绑定的事件和addEventListener事件,要想阻止底层事件冒泡到二者,只有通过原生事件对象e.stopPropagation()去阻止冒泡
  • 4
    点赞
  • 3
    收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页
评论 6

打赏作者

Qssn丶

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值