js事件冒泡和事件捕獲

前段時間開發一個小項目的時候遇到一個問題,即給一個元素以及它的父元素綁定了click事件,這個時候我點擊這個元素時,父級元素的clik事件也會觸發,這顯然不符合要求。查閱了一些資料以後才知道原來這就是事件冒泡,下面是我對事件冒泡和事件捕獲的理解。javascript

事件冒泡:事件從目標子元素向父級元素逐級傳遞(目標div -> 父元素 —> body ->html ->document)
事件捕獲:事件從最頂級元素向目標子元素逐級傳遞(document -> html -> body -> 父級元素 -> 目標div)css

我理解的事件捕獲和事件冒泡其實就是瀏覽器事件觸發的兩個階段。
在不支持w3c標準的瀏覽器(IE9如下)中,咱們用attachEvent(event, fn)方法綁定一個事件時,fn函數只能在冒泡階段執行且只能在冒泡階段執行。
在支持w3c標準的瀏覽器中,咱們用addEventListener(event, fn, useCapture)方法綁定一個事件時,其中的useCapture參數即用來指定fn函數是在捕獲階段執行仍是在冒泡階段執行,默認false爲冒泡執行,ture爲捕獲執行。一般爲了兼容老版本的IE瀏覽器(IE如下),咱們不設置useCapture參數,或設置成falsehtml

下面是示例代碼java

html代碼:node

<style type="text/css">
    .parent {height: 120px; width: 120px; background-color: #999; padding: 25px;}
    .child {height: 60px; width: 60px; background-color: #666}
    
  </style>

  <body>
      <div class="parent" id="parent">
      <div class="child" id="child"></div> 
    </div>
  </body>

事件冒泡js代碼:瀏覽器

var parent = document.getElementById('parent')
var child = document.getElementById('child')

// click事件冒泡時觸發(useCapture參數爲false和不加參數時的默認行爲)
parent.addEventListener('click', function(e) {
  console.log('parent')
}, false)

child.addEventListener('click', function(e) {
  console.log('child')
  // e.stopPropagation()    // 阻止事件冒泡
}, false)

這個時候點擊child元素,控制檯會依次彈出child parent,所以事件觸發的順序是child ->parent, 固然若是咱們但願點擊child元素以後不觸發parent元素的click事件,也能夠用stopPropagation()來阻止事件的冒泡。函數

事件捕獲js代碼:性能

// click事件捕獲時觸發
parent.addEventListener('click', function(e) {
  console.log('parent')
}, true)

child.addEventListener('click', function(e) {
  console.log('child')
}, true)

這個時候點擊child元素,控制檯會依次彈出parent child,所以事件觸發的順序是parent->child.net

說了那麼多,事件冒泡究竟有什麼用呢? 下面咱們就利用事件冒泡原理來實現事件委託code

事件委託:

什麼是事件委託? 事件委託就是本來你要給一個元素綁定某個事件,可是你不直接綁定在這個元素上,而是綁定在與它相關的元素上,可是效果與綁定在這個元素上同樣。 這樣作有什麼好處呢? 下面咱們作個小例子,當鼠標移動到li上時改變它的背景顏色,鼠標離開時背景顏色恢復

html代碼:

<body>
    <ul id="parent">
       <li>item1</li>
       <li>item2</li>
       <li>item3</li>
       <li>item4</li>
       <li>item5</li>
    </ul>
</body>

js代碼:

var oParent = document.getElementById("parent");
var oItem = oParent.getElementsByTagName("li");

// 給ul綁定鼠標移入事件
oParent.addEventListener('mouseover', function(e) {
  if(e.target.nodeName.toLowerCase() == "li"){
    e.target.style.background = "red";
  }
})

// 給ul綁定鼠標移出事件
oParent.addEventListener('mouseout', function(e) {
  if(e.target.nodeName.toLowerCase() == "li"){
    e.target.style.background = "";
  }
})

固然你會說我也能夠利用循環遍歷爲每一個li都綁定鼠標移入移出事件,固然這是能夠的,可是這樣你的代碼就多了一個循環操做,若是循環多了以後對性能影響就不可忽略了。
還有就是若是你又動態的增長了一個<li>標籤,那麼你又不得不爲這個li標籤單獨綁定事件,而採用事件委託這些煩惱均可以省略。

本文參考文章:
js之事件冒泡詳解 http://www.jb51.net/article/4...
事件委託及其原理 http://www.cnblogs.com/dearxi...