關於js的冒泡--新手踩坑案例

fxyf1991發表於2018-01-08

先通過一段html程式碼瞭解各個div的從屬關係

<div id='wrapper' class="wrapper">
  <button id="clickMe">點我</button>
  <div id='popover' class="popover">
    浮層
  </div>
</div>
複製程式碼

微信圖片_20180105161821.png

要實現的功能是

  1. 通過button按鈕點選後開啟浮層

採用對button新增監聽事件,預設隱藏浮層,在使用者點選後展示浮層。

  1. 點選頁面空白區域關閉浮層 實現過程如下所示

1. 監聽body

首先考慮對body物件新增監聽,內容為點選後隱藏浮層。發現無效後通過對body新增邊框看到body高度是根據其內容變化的,所以只佔螢幕上方很小一部分。


2. 監聽document

  • 同樣無效。經分析,在冒泡階段先觸發按鈕事件,但因同時存在對document的監聽,緊接著觸發document監聽,所以仍然無法顯示圖層。

  • 進一步思考後,對wrapper新增e.stopPropagation,即停止傳播,由此可阻斷冒泡,即可實現上述功能。

clickMe.addEventListener('click', function(e){
  popover.style.display = 'block'
})
wrapper.addEventListener('click', function(e){
  e.stopPropagation()
})
document.addEventListener('click', function(){
  popover.style.display = 'none'
})
複製程式碼

3. 以上雖然可實現需求,但新增的監聽器過多,考慮減少記憶體的使用,可進行優化:

將對document的監聽全部新增到click事件的內部,並使用one方法只監聽一次點選事件。

$(clickMe).on('click', function() {
  $(popover).show()
  $(document).one('click', function() {
    $(popover).hide()
  })
})
$(wrapper).on('click', function(e){
  e.stopPropagation()
})
複製程式碼

需注意,在3中若刪除阻斷,仍會出現bug,若執意刪除,則需更改程式碼如下:

$(clickMe).on('click', function() {
  $(popover).show()
  setTimeout(function() {
    $(document).one('click', function() {
      $(popover).hide()
    })
  }, 0)
})
複製程式碼

上述程式碼中,將document監聽放在setTimeout函式中,這裡的setTimeout作用在於讓其中的內容儘快執行而不是立即執行,若不新增則在button被點選之後會立即呼叫show和監聽,此時會立即對popover繫結hide,在冒泡階段便會依序執行show和hide,造成bug。

相關文章