又被事件冒泡坑了一把,此次要完全弄懂瀏覽器的事件流

打算封裝一個彈窗組件,作的時候忘記了考慮事件冒泡的因素,結果被坑得不要不要的。爲了解決本身的問題,去查閱了很多資料,把事件流相關的知識都給總結一下。css

事件冒泡:一個簡單,可是坑了我無數回的知識點!html

JavaScript與HTML的交互經過事件來實現。而瀏覽器的事件流是一個很是重要的概念。不去討論那些古老的瀏覽器有事件捕獲與事件冒泡的爭議,只須要知道在DOM2中規定的事件流包括了三個部分,事件捕獲階段、處於目標階段、事件冒泡階段。jquery

1、事件捕獲

事件捕獲,是從document元素開始,越高級的父級元素越早接收到事件,所以這是一個從上而下的過程。git

仍是那句話,沒在實際應用中踩過坑,可能你真的不算掌握了這個知識點。那麼,事件捕獲有什麼常見的坑呢?github

1. 坑一:新插入的子元素沒有綁定點擊事件

可能很多同窗在初學js的時候遇到過這樣一個坑。一個ul元素中初始的狀態有4個li元素,咱們能夠循環給li元素添加click事件,執行咱們想要的動做。這個例子的坑就在於,新添加的li元素不會有咱們綁定的click事件。瀏覽器

<ul class="container">

    <!-- 先循環給原有的4個li綁定click事件 -->
    <li class="item"></li>
    <li class="item"></li>
    <li class="item"></li>
    <li class="item"></li>
    
    <!-- 這是新添加的li元素,可是該元素沒綁定click事件 -->
    <li class="item new"></li>
</ul>

是的,就是這麼坑,爲了解決這個問題,咱們就要利用事件捕獲的原理。spa

$('ul.container').click(function(event) {
    var target = event.target;
    if (target.className == 'item') {
        // dosomething
    }
})

在上面的解決方案中,我並非直接給li綁定事件,而是給全部li的父級ul綁定事件。根據事件捕獲的原理,事件會自上而下傳遞給li,咱們只須要經過一些簡單的條件判斷來肯定咱們的目標元素便可,如上例中的判斷li的className。code

這個方式就是大名鼎鼎的事件委託。htm

事件委託是一個很重要並且在實際中會經常用到的知識點。事件

2. 坑二: 若是目標元素有子元素,那麼怎麼辦?

當咱們在使用className判斷目標元素時,會遇到這樣的狀況。

...
<li class="item">
    <div class="title">xxx</title>
    <p class="desc">xxxxxxs</p>
</li>
...

當咱們試圖使用事件委託但願給全部的li添加元素時,在利用className判斷的過程當中發現,目標event.target元素竟然是li.item的子元素,這個時候就沒辦法準確的將事件綁定到li上了,這個時候咱們應該怎麼辦?

這種狀況,咱們要作的,就是阻止事件捕獲的傳遞,那麼,要如何阻止呢?

我知道的一種方式,利用css,給li全部子元素添加以下css屬性便可

li.item >  * {
  pointer-events: none;
}

按道理來講,應該是有js方式的,但是查了不少文章都沒有說起,所以就暫時這樣吧,到時候遇到了在補充。

在jquery中,已經幫助咱們實現了事件委託,而且幫咱們解決掉了這些坑。咱們只須要按照必定語法使用便可,而咱們不用再本身去進行條件判斷,好比咱們要給全部的li.item元素綁定事件,寫法以下

// on中的第二參數就是咱們的目標元素的選擇器
$('ul.container').on('click', 'li.item', function(event) {
    // dosomething
})

2、事件冒泡

說完了事件捕獲,而後來講說事件冒泡這個坑。所謂事件冒泡,就是讓DOM樹最底層的目標元素最早接收到事件,而後往上傳遞,這是一個自下而上的過程。

咱們經常會遇到一種彈窗樣式,彈窗出來時,內容在中間,而後會有一層半透明的遮罩將頁面內容與彈窗區分開。彈窗內容會有一些按鈕綁定點擊事件,好比確認與取消。而在半透明遮罩上,可能也會綁定一個點擊事件,當點擊時,將彈窗隱藏。若是我因爲經驗不足,將該遮罩層設置成了彈窗按鈕的父級,那麼就會遇到事件冒泡帶來的麻煩。

也就是說,在以下例子中 container是全屏遮罩,button是彈窗裏面的點擊按鈕。他們都同時綁定了click事件,執行不一樣的動做。可是在實際執行的時候,當我點擊了button,那麼button和container的click事件都會執行,自下而上按順序執行

<div className="container">
    <div className="button">click</div>
</div>

我爲這個事情寫了一個小例子,你們能夠動手感覺一下這個坑。點擊空白會生成一個彈窗

http://yangbo5207.github.io/s...

好吧解決問題的方法很簡單,就是阻止冒泡事件。

$xxx.click(function(e) {
    e.stopPropagation();
    
    // ie
    e.cancelBubble = true;
})

理解了整個事件流,咱們能夠感覺事件在DOM中的傳遞過程與方向,而且利用他來解決咱們的問題和各類坑,雖然是一個簡單的知識點,可是卻很是容易被忽略,所以建議你們找機會將它掌握牢固。

而還有一個坑,可能你們在實際中會不多遇到,這個坑就是,某些事件類型天生就不支持事件冒泡!

blur: 在元素失去焦點時觸發,該事件不支持冒泡
focus: 在元素得到焦點時觸發,該事件不支持冒泡
mouseenter: 當鼠標移入元素時觸發,該事件不支持冒泡
mouseleave: 當鼠標移出元素時觸發,該事件不支持冒泡
... ...

以及IE6 IE7 IE8中 的 change、select、submit、reset 事件事實上都沒有參照規範定義產生事件冒泡。

當你在須要的冒泡的時候,綁定了這些事件,而你剛好不知道竟然還有事件天生就不支持冒泡的,那麼你可能就悲劇了。因此這個點只要在腦殼裏面有個印象就行了,我只記得我曾經遇到過這樣一個坑,但暫時想不起來應用場景了 - -!

我所知道的,遇到過的關於事件流的坑就這麼多了,想來應該也算是很是完整的一個總結了,值得你們點一下推薦和收藏的哦!

clipboard.png

相關文章
相關標籤/搜索