這幾天看到一個面試題,大概就是,讓你給1000個li都添加一個click事件,應該怎麼添加?大多數人第一開始的感受可能就是,每一個li上邊都添加一個唄,那要是這樣的話,估計面試的時候就會GG了,這裏就是撤出了咱們的事件冒泡和捕獲機制,以及事件委託機制,對於上邊這些,咱們慢慢來看.node
首先說一下事件冒泡和事件捕獲機制,事件冒泡是有微軟公司提出來的,事件捕獲是有網景公司提出來的,當時兩家是爭論的不可開交,後來w3c也沒辦法,就採起了折中的方式,事件產生後先捕獲後冒泡,面試
一般,在js中監聽事件的方法共有三種,分別是:瀏覽器
ele.addEventListener(type,listener,[useCapture]);//IE6~8不支持dom
ele.attachEvent('on'+type,listener);//IE6~10支持,IE11不支持函數
ele.onClick=function(){};//全部瀏覽器都支持post
w3c規範中定義了三個事件階段,依次是捕獲階段,目標階段,冒泡階段,而w3c指定的dom2級規定中,使用的是addEventListener來監聽事件的.因此咱們就以addEventListener來說解,首先事假冒泡就像你從往水中扔一塊石子,水中的氣泡從下邊往上冒同樣,意思爲觸發事件後從子元素王父元素方向觸發,而捕獲機制則正好相反,捕獲機制是從父元素往子元素方向進行事件觸發,而addEventListener函數中的第三位參數正是來決定是使用捕獲機制仍是冒泡機制的,當useCapture爲true是爲捕獲機制,當useCapture爲false時是冒泡機制,咱們看一下例子:spa
<div class="parent">
<div class="child">
</div>
</div>
<script>
var parent = document.getElementsByClassName('parent')[0];
var child = document.getElementsByClassName('child')[0]; parent.addEventListener('click',function(){ console.log("這裏是父元素"); },false); child.addEventListener('click',function(){ console.log("這裏是子元素"); },false); </script>
當咱們點擊子元素是顯示上圖,當咱們將false改成true後就會發現執行順序會反過來,這就是事件冒泡和捕獲的區別,他們兩個恰好相反,代理
那麼使用這種綁定機制咱們的弊端在於要去給每個對象綁定事件會是一個特別麻煩的事情,當咱們要刪除一個事件或者要改變一個事件的時候會特別的繁瑣,更重要的是,咱們增長了JavaScript和dom節點之間的關聯,並且一點出現循環引用,頗有可能形成內存泄露,這些都是它的弊端,code
那麼解決這種弊端的一種方法就是事件代理(event delegation),這個方法可讓你避免去給每個節點一一的添加事件,它的作法是將這些監聽事件去綁定到這些節點的父元素上,在父元素上的這個監聽函數自動去判斷是哪個子元素觸發的事件,從而能夠對觸發事件的子元素進行操做,這裏咱們給出的例子是davidwalsh所給出的一個例子:對象
如今咱們有一個父元素ul和幾個li子元素,
<ul id="parent-list"> <li id="post-1">Item 1</li> <li id="post-2">Item 2</li> <li id="post-3">Item 3</li> <li id="post-4">Item 4</li> <li id="post-5">Item 5</li> <li id="post-6">Item 6</li> </ul>
如今咱們要實現的是,當咱們點擊每個li節點的時候,都會輸出li節點中的內容,按照上邊的寫法,你能夠選中這些li,讓後給他們加上這些方法,而後等到不須要了再將他們移除,若是有100個li,1000個li呢,這將會成爲你的噩夢,較好的解決方法就是給父元素添加一個監聽事件,以後的問題即是怎麼去判斷出來時哪個li被點擊了? 咱們能夠在監聽事件中去判斷當前event的target來判斷是不是咱們要找的節點,這裏咱們有一個簡單的例子:
// 找到父元素,綁定一個監聽事件
document.getElementById("parent-list").addEventListener("click", function(e) {
// e.target是點擊的元素
// 若是它是li元素
if(e.target && e.target.nodeName == "LI") { // console.log("List item ", e.target.id.replace("post-", ""), " was clicked!"); } });
當ul中發生點擊事件後,由於addEventListener默認是冒泡事件,因此監聽事件會在底層事件冒泡過來時執行,在觸發了事件後,去檢測是不是咱們要尋找的目標元素,若是不是,就會忽略過去,那咱們不只僅能夠經過目標元素的標籤是否是咱們須要的目標元素,咱們還能夠根據目標元素的屬性或者類名來進行檢測,利用ele.maeches這個API來進行處理,
document.getElementById("myDiv").addEventListener("click",function(e) {
// e.target 就是當前被點擊的元素
if (e.target && e.target.matches("a.classA")) { console.log("Anchor element clicked!"); } });
所以咱們能夠看得出來,使用事件代理這種方式,可以給咱們帶來不少的便捷,能夠避免不少坑,使用事件代理是一種很強大的方法.