事件流javascript
若是單擊了頁面中的某個按鈕,同時也單擊了按鈕的容器元素,甚至單擊了整個頁面。java
IE提出的是冒泡流,網景提出的是捕獲流。node
<div id="content">content <div id="btn">button</div> </div> <script type="text/javascript"> var content = document.getElementById("content"); var btn = document.getElementById('btn'); btn.onclick = function(){ alert("btn"); }; content.onclick = function(){ alert("content"); }; document.onclick = function(){ alert("document"); } </script> //點擊容器 #btn ,則彈出的順序是:btn > content > document //點擊容器 #content,則彈出的順序是:content > document //點擊容器 document,則彈出的是 document
JS事件流原理圖 :app
一、一個完整的事件流是從 window 開始,最後回到 window 的一個過程測試
二、事件流被分爲三個階段:(1-5)捕獲過程、(5-6)目標過程、(6-10)冒泡過程this
<div id="wrapDiv">wrapDiv <p id="innerP">innerP <span id="textSpan">textSpan</span> </p> </div> <script> var wrapDiv = document.getElementById("wrapDiv"); var innerP = document.getElementById("innerP"); var textSpan = document.getElementById("textSpan"); // 捕獲階段綁定事件 window.addEventListener("click", function(e){ console.log("window 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.addEventListener("click", function(e){ console.log("document 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.documentElement.addEventListener("click", function(e){ console.log("documentElement 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.body.addEventListener("click", function(e){ console.log("body 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); wrapDiv.addEventListener("click", function(e){ console.log("wrapDiv 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); innerP.addEventListener("click", function(e){ console.log("innerP 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); textSpan.addEventListener("click", function(e){ console.log("textSpan 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); // 冒泡階段綁定的事件 window.addEventListener("click", function(e){ console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.addEventListener("click", function(e){ console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.documentElement.addEventListener("click", function(e){ console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.body.addEventListener("click", function(e){ console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); wrapDiv.addEventListener("click", function(e){ console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); innerP.addEventListener("click", function(e){ console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); textSpan.addEventListener("click", function(e){ console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); </script>
若是點擊textSpan元素,控制檯打印以下圖:spa
如上圖所示,事件傳播的過程是先捕獲,再冒泡。code
那麼,若是不使用addEventListener方法綁定的事件(如onclick),會發生在哪一個階段?blog
<div id="wrapDiv">wrapDiv <p id="innerP">innerP <span id="textSpan">textSpan</span> </p> </div> <script> var wrapDiv = document.getElementById("wrapDiv"); var innerP = document.getElementById("innerP"); var textSpan = document.getElementById("textSpan"); // 測試直接綁定的事件到底發生在哪一個階段 wrapDiv.onclick = function(){ console.log("wrapDiv onclick 測試直接綁定的事件到底發生在哪一個階段") }; // 捕獲階段綁定事件 window.addEventListener("click", function(e){ console.log("window 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.addEventListener("click", function(e){ console.log("document 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.documentElement.addEventListener("click", function(e){ console.log("documentElement 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); document.body.addEventListener("click", function(e){ console.log("body 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); wrapDiv.addEventListener("click", function(e){ console.log("wrapDiv 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); innerP.addEventListener("click", function(e){ console.log("innerP 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); textSpan.addEventListener("click", function(e){ console.log("textSpan 捕獲", e.target.nodeName, e.currentTarget.nodeName); }, true); // 冒泡階段綁定的事件 window.addEventListener("click", function(e){ console.log("window 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.addEventListener("click", function(e){ console.log("document 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.documentElement.addEventListener("click", function(e){ console.log("documentElement 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); document.body.addEventListener("click", function(e){ console.log("body 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); wrapDiv.addEventListener("click", function(e){ console.log("wrapDiv 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); innerP.addEventListener("click", function(e){ console.log("innerP 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); textSpan.addEventListener("click", function(e){ console.log("textSpan 冒泡", e.target.nodeName, e.currentTarget.nodeName); }, false); </script>
點擊textSpan元素,以下圖控制檯輸出:事件
一、全部在目標元素上綁定的事件,都會發生在目標階段
二、在綁定捕獲代碼以前寫了綁定的冒泡階段的代碼,因此在目標元素上就不會遵照先捕獲後冒泡這一規則,而是先綁定的事件先發生。
3、因爲wrapDiv不是目標元素,因此它上面綁定的事件會遵照先捕獲後冒泡的規則。因此用onclick直接綁定的事件發生在了冒泡階段。
事件委託
對「事件處理程序過多」問題的解決方案就是事件委託
事件委託利用了事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。
例如,click事件會一直冒泡到document層次。咱們能夠爲整個頁面指定一個onclick事件處理程序,而沒必要給每一個可單擊的元素分別添加事件處理程序。
//點擊li元素,輸出li當中的顏色 <ul id="box"> <li>red</li> <li>yellow</li> <li>blue</li> <li>green</li> <li>black</li> <li>white</li> </ul> //通常會這樣寫 <script> var colorBox = document.getElementById('box') var colors = colorBox.getElementsByTagName('li') for(var i=0;i<colors.length;i++){ colors[i].addEventListener('click',function(e){ var li = e.target; console.log(li.innerHTML) },false) } </script> //使用事件委託去寫 <script> var colorBox = document.getElementById('box') colorBox.addEventListener('click',function(e){ var li = e.target; if(li.nodeName.toLowerCase() === 'li'){ console.log(li.innerHTML) } },false) </script>
事件委託還有一個好處就是添加進來的元素也能綁定事件
//點擊li元素,輸出li當中的顏色 <ul id="box"> <li>red</li> <li>yellow</li> <li>blue</li> <li>green</li> <li>black</li> <li>white</li> </ul> <button onclick="add()">add</button> <script> var colorBox = document.getElementById('box') colorBox.addEventListener('click',function(e){ var li = e.target || e.srcElement; //兼容處理 if(li.nodeName.toLowerCase() === 'li'){ console.log(li.innerHTML) } },false) function add(){ var liNode = document.createElement('li') var textNode = document.createTextNode('this is add text.') liNode.appendChild(textNode); document.getElementById('box').appendChild(liNode) } </script>