React 阻止事件冒泡失效、stopPropagation和stopImmediatePropagation分析,解決stopPropagation沒有阻止冒泡問題

前言

做項目過程中, 發現了一個問題,onClik阻止冒泡事件並沒有生效,僅僅只是阻止outClick,我的需求是隻觸發"inner dom click",(PS:不想知道過程的可以直接跳過看最後的結論)

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

下面代碼的輸出是

“root click”
“inner dom click”
“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)

解決過程與坑

一尋思想起來React組件類似onClick的事件是合成事件,本質上所有綁定是代理到document上的,所有綁定都會冒泡到document層去執行。類似下面:

// react所有合成事件, SyntheticEvent裏面執行回調函數
document.addEventListener('click', SyntheticEvent);

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

所以在React綁定的合成事件調用e.stopPropagation()阻止的只是React裏綁定的合成事件,比如我例子裏面的outClick就沒有觸發。而對於原生事件還是阻止不了

百度告訴我說e.nativeEvent.stopImmediatePropagation可以阻止冒泡到原生事件,我就在在innerClick事件里加上了這句,輸出變成了

“root click”
“inner dom click”

顯然還是不可以,因爲stopImmediatePropagation只是阻止同層級且綁定靠後的事件(具體參考MDN),非document層的原生事件還是沒有阻止,因爲React的事件都是綁定在document層,所以React要阻止冒泡事件只有使用原生的綁定去調用e.stopPropagation()


  refCb = (dom) => {
      //這裏我就沒有去解綁了
      dom && dom.addEventListener('click',this.innerClick)
  }
  <button ref={this.refCb} > 測試冒泡</button>

這樣就可以解決了

結論

  1. React組件綁定事件是合成事件,本質上是代理到document上,採用事件冒泡的形式冒泡到document上面,然後React將事件封裝給正式的函數處理運行和處理(可以理解成React所有事件都是綁定在document層)
  2. 對於React的合成事件對象e,e.stopPropagation()只能阻止React合成事件的冒泡,e.nativeEvent.stopImmediatePropagation只能用來阻止冒泡到直接綁定在document上的事件
  3. 要想阻止所有的冒泡事件,只能通過ref獲得dom節點監聽,用原生事件對象e的e.stopPropagation()去阻止冒泡
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章