事件冒泡 和 事件捕獲

背景知識

什么是事件?
直觀的說就是網頁上發生的事情,大部分是指用戶的鼠標動作和鍵盤動作,如點擊、移動鼠標、按下某個鍵。為什么說大部分呢,因為事件不單單只有這兩部分,還有其他的例如document的load和unloaded。只不過我們更加關注的是用戶的操作。

事件模型:規范事件的定義的一種標準

事件的三種模型:

  • 原始事件模型(DOM 0級事件模型)
  • IE事件模型
  • DOM2事件模型
DOM2事件模型:

此模型是W3C制定的標準模型,既然是標準,那大家都得按這個來,我們現在使用的現代瀏覽器(指IE6~8除外的瀏覽器)都已經遵循這個規范。W3C制定的事件模型中,“DOM2級事件”中規定的事件流同時支持了事件捕獲階段和事件冒泡階段,而作為開發者,我們可以選擇事件處理函數在哪一個階段被調用。
一次事件的發生包含三個過程:

(1)capturing phase:事件捕獲階段。
事件被從document一直向下傳播到目標元素,在這過程中依次檢查經過的節點是否注冊了該事件的監聽函數,若有則執行。

(2)target phase:事件處理階段。
事件到達目標元素,執行目標元素的事件處理函數.

(3)bubbling phase:事件冒泡階段。
事件從目標元素上升一直到達document,同樣依次檢查經過的節點是否注冊了該事件的監聽函數,有則執行。

所有的事件類型都會經歷captruing phase(事件捕獲),但是只有部分事件會經歷bubbling phase(事件冒泡)階段,例如submit事件就不會被冒泡。

1. 是什么?

<div id="outer">
    <p id="inner">Click me!</p>
</div>

上面的代碼當中一個div元素當中有一個p子元素,如果兩個元素都有一個click的處理函數,那么我們怎么才能知道哪一個函數會首先被觸發呢?

為了解決這個問題微軟和網景提出了兩種幾乎完全相反的概念。

**事件流 : ** 指的是頁面中接收事件的順序.

事件冒泡

微軟提出了名為事件冒泡(event bubbling)的事件流。
事件冒泡可以形象地比喻為把一顆石頭投入水中,泡泡會一直從水底冒出水面。

當一個DOM元素上的事件被觸發的時候(如:按鈕點擊事件),這個元素的所有父元素 中,如果也綁定有該相同事件,則也會被觸發, 觸發的順序就是先從 : 當前元素的事件 ==> 臨近父元素 ==> 父元素......,這一過程被稱為事件冒泡

因此上面的例子在事件冒泡的概念下發生click事件的順序應該是:
p -> div -> body -> html -> document
IE,火狐和chrome瀏覽器都是事件冒泡.

事件捕獲

網景提出另一種事件流名為事件捕獲(event capturing)。
當一個DOM元素上的事件被觸發的時候(如:按鈕點擊事件),這個元素的所有父元素 中,如果也綁定有該相同事件,則也會被觸發, 觸發的順序就是先從 : ....... 父元素 ==> 臨近父元素 ==> 當前元素的事件,這一過程被稱為事件捕獲
與事件冒泡相反,事件會從最外層開始發生,直到最具體的元素。
上面的例子在事件捕獲的概念下發生click事件的順序應該是:
document -> html -> body -> div -> p

圖解:


事件冒泡和捕獲具體如圖

2. 解決了什么問題?

這兩個概念都是為了解決頁面中事件流(事件發生順序)的問題。

http://www.imooc.com/article/9833

3. 執行原理

<ul>
   <li>
       <p>
          <a>  </div>
      </p>
   </li>
</ul>
事件冒泡和事件捕獲原型圖

事件捕獲階段:事件從最上一級標簽開始往下查找,直到捕獲到事件目標(target)。
事件冒泡階段:事件從事件目標(target)開始,往上冒泡直到頁面的最上一級標簽。

事件捕獲當你使用事件捕獲時,父級元素先觸發,子級元素后觸發,即div先觸發,p后觸發。
事件冒泡當你使用事件冒泡時,子級元素先觸發,父級元素后觸發,即p先觸發,div后觸發。

4. 如何使用?

說到事件的執行順序,那么我們需要知道一個給元素添加事件的方法, 即給某元素動態綁定事件
W3C為我們提供了addEventListener()函數用來為指定的dom元素動態綁定事件。

語法:
element.addEventListener( event ,  function ,  useCapture )

參數分析

提示: 使用 removeEventListener()方法來移除 addEventListener() 方法添加的事件句柄。

function sayHello() { 
console.log("hello");
}
var myDiv = document.getElementById("myDiv");
myDiv.addEventListener("click", sayHello);

這樣我們點擊id為myDiv的元素時,控制臺就會輸出"Hello"。
事件冒泡

有如下html代碼:


html代碼
運行結果

下面設置了四個函數用來進行事件綁定:


要綁定的函數

使用下面的代碼,我們可以獲取四個元素對應DOM


四個DOM元素

現在,我試著同時分別為grandpa和grandson綁定sleep和doingHomework事件:

綁定事件

這時我們點擊最外層的grandpa時,當然會觸發sleep函數,然而當我們點擊grandson時,控制臺的輸出如下:

控制臺查看結果

原因:這是因為grandson在grandpa之上,當點擊grandson時,同時也在grandpa上進行了點擊操作,所以在執行了doingHomework后,還會觸發grandpa的sleep函數。
這種當滿足條件后從子元素到父元素依次觸發其上事件的處理方式叫做事件冒泡

我們也為father和child分別綁定watchTV和playingCard函數

綁定事件
控制臺查看結果
事件冒泡()
grandpa.addEventListener("click", sleep);            
grandson.addEventListener("click", doingHomework);            
father.addEventListener("click", watchTV);            
child.addEventListener("click", playingCard);
四個元素都綁定相同事件,點擊grandson時的結果
點擊child元素的 結果

事件捕獲

事件捕獲與事件冒泡完全相反,先觸發祖先元素的事件,然后再逐級觸發子元素的事件。默認情況下,綁定事件時,采用事件冒泡原則,如果想要進行事件捕獲的話,需要設置一個參數 。

可以為addEventListener函數添加第三個參數useCapture,參數值是布爾值,默認是false。當useCapture為false時,事件處理采取事件冒泡的原則,當userCapture為true時,則采取事件捕獲的原則

綁定捕獲事件

這時,當點擊grandson時,就會先執行祖先元素的事件,再執行后代元素的事件了,控制臺的輸出如下圖所示:

捕獲事件的執行結果

雖然默認情況下,useCapture的值是false,但我推薦我們在綁定函數時把它明顯的寫出來以避免瀏覽器兼容性的問題。

事件冒泡與事件捕獲要是同時進行怎么辦

有思想的同學肯定會思考這樣一個問題,在上述綁定事件的代碼中,第三個參數不是全部設置的true,就是全部設置成false,那如果既有true,又有false,有的元素設置成按事件冒泡處理,有的元素設置成按事件捕獲處理,那怎么辦呢?
直接告訴大家答案,我們的瀏覽器更“喜愛”事件捕獲:

它會先把useCapture為false的元素綁定事件放到一邊,按照事件捕獲正常的順序執行useCapture為true的元素綁定事件,最后在按照事件冒泡順序執行useCapture為false。
現在我們作如下更改

既有事件冒泡又有事件捕獲

按照上述原則,當點擊grandson時,先執行useCapture為true的元素的綁定事件,又按照事件捕獲原則,先執行grandpa的事件,再執行child的事件。之后,再按照事件捕獲順序執行useCapture為false的事件,輸出結果如下:
細說addEventListener與事件捕獲、事件冒泡_

阻止事件冒泡和捕獲

我們可以利用時間對象event的stopPropagation()方法阻止事件的進一步傳播。
我們修改一下doingHomework函數:

為grandson的事件設置阻止事件對象
設置阻止事件后,點擊grandson的結果

發現事件執行到doingHomework就被阻斷了,其后不會在事件傳播到父元素。
值得注意的是,event.stopPropagation()函數并不會阻止其下函數內容的執行。

*如果你使用的是jquery的事件綁定,也可以直接在函數中使用return false來阻止事件的傳播(當然event.stopPropagation同樣有效),與event.stopPropagation不同的是,return false會阻止同函數下面的代碼執行 *

傳統綁定事件方式在一個支持W3C DOM的瀏覽器中,像這樣一般的綁定事件方式,是采用的事件冒泡方式。
ele.onclick = doSomething2

IE瀏覽器
如上面所說,IE只支持事件冒泡,不支持事件捕獲,它也不支持addEventListener函數,不會用第三個參數來表示是冒泡還是捕獲,它提供了另一個函數attachEvent。
ele.attachEvent("onclick", doSomething2);

不是所有的事件都能冒泡,例如:blur、focus、load、unload。

事件冒泡的好處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容