事件委託(事件代理)

  • 概念:

通俗的說是這個事件須要綁定在我身上的,綁定在別人身上,也能達到一樣的效果,也就是綁定在別人身上,我也能監聽到。javascript

  • 產生的緣由:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
<ul id="ul-test">
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
</ul>
</body>
<script type="text/javascript">
  var oUl=document.getElementById("ul-test");
  var oLi=oUl.getElementsByTagName("li");
  for(var i=0,len=oLi.length;i<len;i++){
    oLi[i].addEventListener("click",function(){
      alert(this.innerHTML)
    })
  }
</script>
</html>
複製代碼

問題在於:
1.for循環,循環的是li,10個li就循環10次,綁定10次事件,1000個就循環了1000次,綁定1000次事件!
2.若是li不是原本就在頁面上的,是將來元素,是頁面加載了,再經過js動態加載進來了,上面的寫法是無效的,點擊li是沒有反應的!html

應該怎麼解決以上問題?java

  • 解決的辦法就是經過事件委託:

var oUl=document.getElementById("ul-test");
oUl.addEventListener("click",function(ev){
  console.log(ev)
  var ev=ev||window.event;
  var target=ev.target||ev.srcElement;
  //若是點擊的最底層是li元素
  if(target.tagName.toLowerCase()==='li'){
    alert(target.innerHTML)
  }
})複製代碼

  •   原理:

  1. 事件的傳播有三個階段:

當一個事件發生之後,它會在不一樣的DOM節點之間傳播(propagation)。這種傳播分紅三個階段:瀏覽器

  • 第一階段:從window對象傳導到目標節點,稱爲「捕獲階段」(capture phase)。bash

  • 第二階段:在目標節點上觸發,稱爲「目標階段」(target phase)。函數

  • 第三階段:從目標節點傳導回window對象,稱爲「冒泡階段」(bubbling phase)。ui

這種三階段的傳播模型,會使得一個事件在多個節點上觸發。好比,假設點擊<div>之中嵌套一個<p>節點。
this

<div>
  <p>Click Me</p>
</div>複製代碼

若是對這兩個節點的click事件都設定監聽函數,則click事件會被觸發四次。
spa

var phases = {
  1: 'capture',
  2: 'target',
  3: 'bubble'
};

var div = document.querySelector('div');
var p = document.querySelector('p');

div.addEventListener('click', callback, true);
p.addEventListener('click', callback, true);
div.addEventListener('click', callback, false);
p.addEventListener('click', callback, false);

function callback(event) {
  var tag = event.currentTarget.tagName;
  var phase = phases[event.eventPhase];
  console.log("Tag: '" + tag + "'. EventPhase: '" + phase + "'");
}

// 點擊之後的結果
// Tag: 'DIV'. EventPhase: 'capture'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'P'. EventPhase: 'target'
// Tag: 'DIV'. EventPhase: 'bubble'複製代碼
  1. 捕獲階段:事件從<div><p>傳播時,觸發<div>click事件;
  2. 目標階段:事件從<div>到達<p>時,觸發<p>click事件;
  3. 目標階段:事件離開<p>時,觸發<p>click事件;
  4. 冒泡階段:事件從<p>傳回<div>時,再次觸發<div>click事件。

注意,用戶點擊網頁的時候,瀏覽器老是假定click事件的目標節點,就是點擊位置的嵌套最深的那個節點(嵌套在<div>節點的<p>節點)。因此,<p>節點的捕獲階段和冒泡階段,都會顯示爲target階段。
代理

事件傳播的最上層對象是window,接着依次是documenthtmldocument.documentElement)和bodydocument.body)。也就是說,若是<body>元素中有一個<div>元素,點擊該元素。事件的傳播順序,在捕獲階段依次爲windowdocumenthtmlbodydiv,在冒泡階段依次爲divbodyhtmldocumentwindow

定義:

因爲事件會在冒泡階段傳向父節點,所以能夠把子節點的監聽函數綁定在父節點上,用父節點的監聽函數統一處理多個子節點的事件,這就是事件代理。

var ul = document.querySelector('ul');

ul.addEventListener('click', function(event) {
  if (event.target.tagName.toLowerCase() === 'li') {
    // some code
  }
});複製代碼

上面這個函數事件監聽綁在ul身上,可是監聽到點擊li的事件,而後處理li點擊後的回調。

就算li是動態添加進來的依然可以監聽到。

總結:

因爲一個事件在傳播的過程當中有三個階段,由最外層的父節點一層層傳遞到最裏面的子節點,先是捕獲階段,到目標階段,再到冒泡階段。若是咱們想監聽到動態添加的子節點的事件,咱們能夠把事件綁在父節點上,利用event.target,篩選到子節點,處理事件相應的回調!!!

相關文章
相關標籤/搜索