js事件流機制冒泡和捕獲

JavaScript與HTML之間的交互是經過事件實現的。事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。html

事件流

從頁面中接收事件的順序稱爲事件流。git

IE --> 事件冒泡流github

Netscape --> 事件捕獲流瀏覽器

查看源碼:DOM2事件-捕獲-冒泡dom

事件冒泡

IE的事件流叫作事件冒泡(event bubbling),即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節點)接收,而後逐級向上傳播到較爲不具體的節點(文檔)。函數

咱們先來個簡單的例子,這是HTML結構性能

<!DOCTYPE html>

<html lang="en">

<head>

  <title>js事件流</title>

</head>

<body>

  <div id="div">

    我是div

  </div>

</body>

</html>

有一個div元素。若是咱們點擊<div>元素,那麼這個click事件的順序會怎麼呢?設計

咱們給幾個元素都添加監聽事件,3d

element.addEventListener(event, function, useCapture)
參數說明:code

  • event 字符串。指定事件名,好比 click、mouseenter、mouseleave
  • function 函數。指定要事件觸發時執行的函數。
  • useCapture 布爾值。指定事件是否在捕獲或冒泡階段執行。默認值是false,即事件在冒泡階段執行。true,在捕獲階段執行
var div = document.getElementById('div')

var body = document.body

var html = document.documentElement

div.addEventListener('click', function () {

  console.log('div標籤')

}, false)

body.addEventListener('click', function () {

  console.log('body ')

},  false)

html.addEventListener('click', function () {

  console.log('html')

},  false)

document.addEventListener('click', function () {

  console.log('document')

}, false)

而後點擊<div>元素,查看控制檯輸出,
事件冒泡流

由上面的輸出結果能夠看出,這個click事件會按照以下順序傳播:
<div> ---> <body>---> <html> --->document

也就是說,click事件首先發生在目標元素,而後,click事件沿着DOM樹向上傳播到document對象。這就是事件冒泡。

全部現代瀏覽器都支持事件冒泡。IE九、Firefox、Chrome和Safari則將事件一直冒泡到window對象。

若是,咱們在每一個DOM元素上都設置監聽事件,會獲得的事件的傳播順序是:
<div> ---><body> ---> <html> --->document ---> window

事件捕獲

Netscape Communicator團隊提出的另外一種事件流叫作事件捕獲(event capturing)。事件捕獲是不太具體的節點應該更早接收到事件,而最具體的節點應該最後接收到事件。事件捕獲的用意在於在事件到達預期目標以前捕獲它。

咱們仍是能夠看剛纔的例子,DOM結構不變,咱們修改下監聽事件

var div = document.getElementById('div')

var body = document.body

var html = document.documentElement

div.addEventListener('click', function () {

  console.log('div標籤')

}, true)

body.addEventListener('click', function () {

  console.log('body ')

},  true)

html.addEventListener('click', function () {

  console.log('html')

},  true)

document.addEventListener('click', function () {

  console.log('document')

}, true)

以後,仍是點擊<div>元素,獲得的結果
事件捕獲流

從上面例子能夠看出,事件走向: document ---><html> ---> <body> ---><div>

也就是說,在事件捕獲過程當中,document對象首先接收到click事件,而後事件沿着DOM樹依次向下,一直傳播到事件的實際目標,即<div>元素。這就是事件捕獲。與事件冒泡過程,截然相反。

儘管「DOM2級事件」規範要求事件應該從document對象開始傳播,可是現代瀏覽器大部分都是從window對象開始捕獲事件的。
若是,咱們在每一個DOM元素上都設置監聽事件,會獲得的事件的傳播順序是:
window ---> document ---><html> ---> <body> ---><div>

因爲在老版本的瀏覽器中不支持,所以事件捕獲用的人比較少,除非在特殊須要的時候才使用。

DOM事件流

「DOM2級事件」規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。

首先發生的是事件捕獲,爲截獲事件提供了機會。而後是實際的目標接收到事件。最後一個階段是冒泡階段,能夠在這個階段對事件做出響應。

DOM2級事件流

  • 事件捕獲階段:在DOM事件流中,實際的目標(<div>元素)在捕獲階段不會接收到事件。這意味着在捕獲階段,事件從document到<html>再到<body>後就中止了。

  • 處於目標階段:事件在<div>上發生,並在事件處理中被當作冒泡階段的一部分。

  • 事件冒泡階段:冒泡階段發生,事件又傳播迴文檔。

雖然,「DOM2級事件」規範明確要求捕獲階段不會涉及事件目標,可是IE9及以上的現代瀏覽器都會在捕獲階段觸發事件目標對象上的事件。結果,就是有兩個機會在目標對象上面操做事件,也就是說上圖中的步驟4,既能夠在捕獲階段發生,也能夠在冒泡階段發生。

IE8級更早版本不支持DOM事件流,現代瀏覽器都支持DOM事件流。

事件流的應用

事件流比較典型應用是事件委託。事件委託利用了事件冒泡,只指定一個事件處理程序,就能夠管理一類型的全部事件。

咱們查看一個經常使用例子,這是一個無序列表的DOM結構:

<ul>

    <li id="li1">我是第1個li</li>

    <li id="li2">我是第2個li</li>

    <li id="li3">我是第3個li</li>

  </ul>

咱們的需求是,點擊不一樣列,輸出不一樣的消息。

  • 第一種作法:給每一個<li>添加點擊事件,這樣能分別處理事件,展現不一樣的內容。
document.getElementById('li1').addEventListener('click', function(e) {

    console.log('我是第一個li')

}, false)

document.getElementById('li2').addEventListener('click', function(e) {

    console.log('我是第2個li')

}, false)

document.getElementById('li3').addEventListener('click', function(e) {

    console.log('我是第3個li')

}, false)

單擊每個<li>,會輸出對應的內容。

  • 第二種作法:給<li>元素的父元素<ul>添加一個處理事件,
document.querySelector('ul').addEventListener('click', function (e) {

  console.log(e.target.innerText)

}, false)

單擊每個<li>,會展現不一樣的<li>中文本元素內容。

在這段代碼中,咱們只爲<ul>元素添加了一個onclick事件處理程序。因爲全部<li>都是<ul>元素的子節點,並且它們的事件會冒泡,因此單擊事件最終會被這個函數處理。

以上兩種方式,第二種所具備的優點:

  1. 事前消耗更低。由於只取得了一個DOM元素,只添加了一個事件處理程序。

  2. 佔用的內存更少。每一個函數都是對象,都會佔用內存。

  3. 性能更優。 內存中的對象越多,性能就越差。

  4. 若是之後要增減<li>元素,也不用修改事件方法,能夠獲取相同的處理結果。

因此,比較推薦使用第二種方式。

最適合採用事件委託技術的事件包括clickmousedownmouseupkeydownkeyupkeypress。雖然mouseovermouseout事件也冒泡,但要適當處理它們並不容易,並且常常要計算元素的位置。

參考資料:

JavaScript高級程序設計(第三版)- 第13章 事件

查看源碼:DOM2事件-捕獲-冒泡

相關文章
相關標籤/搜索