JS Event事件流(冒泡機制、捕獲機制、事件綁定)

1.事件流javascript

事件流:從頁面中接收事件的順序。也就是說當一個事件產生時,這個事件的傳播過程,就是事件流。css

  • IE的事件流

  IE中的事件流叫事件冒泡;事件冒泡:事件開始時由最具體的元素接收,而後逐級向上傳播到較爲不具體的節點(文檔)。對於html來講,就是當一個元素產生了一個事件,它會把這個事件傳遞給它的父元素,父元素接收到了以後,還要繼續傳遞給它的上一級元素,就這樣一直傳播到document對象(親測如今的瀏覽器到window對象,只有IE8及下不這樣);html

再多說一句,如今的瀏覽器默認是採用的是事件冒泡;在DOM0級方法綁定事件只能是事件冒泡,不能設置;在DOM2級你能夠設置是用事件冒泡仍是事件捕獲(下面說);java

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
<style type="text/css">
#child{background: red;width: 50px;height: 50px;}
#father{width: 100px;height: 100px;background: green;}
#grandparent{
width: 150px;height: 150px;background: black;margin: 100px auto 0;}
</style>
</head>
<body>
<div id="grandparent">
<div id="father">
<div id="child"></div>
</div>
</div>
</body>
<script type="text/javascript">
var grandparent=document.getElementById("grandparent");
var parent=document.getElementById("father");
var child=document.getElementById('child');
var body=document.body;
var html=document.getElementsByTagName("html")[0];
child.onclick=function(){
console.log("我是兒子");
}
parent.onclick=function(){
console.log("我是父親");
}
grandparent.onclick=function(){
console.log("我是爺爺");
}
window.onclick=function(){
console.log("我是window");
}
document.onclick=function(){
console.log("我是document");
}
html.onclick=function(){
console.log("我是html");
}
body.onclick=function(){
console.log("我是body");
}
</script>chrome

</html>瀏覽器

當我點擊紅色部分函數

 

會打印這樣:測試

 

我測試了(PS:我用的都是最新版的)chrome,firefox,opera,IE11,IE10,IE9都是這個結果,也就是說如今都冒泡到window對象,不只僅是到document對象,可是IE8及以前的就冒泡到document就結束了;this

這就是事件冒泡,它會把你這個click事件,一級一級的向上傳遞,若是相應的元素也綁定click事件處理程序(這裏強調是click事件,若是你是給綁定了其它事件,那沒用),那麼它的這個事件處理程序也會執行,也就產生了上面的結果了;spa

形象的就是跟水裏的魚吐泡泡似的,慢慢的向上傳遞;

  • 事件捕獲

  事件捕獲是網景(Netscape)提出來的,事件捕獲是不太具體的元素應該更早接受到事件,而最具體的節點應該最後接收到事件。他們的用意是在事件到達目標以前就捕獲它;也就是跟冒泡的過程正好相反,以html的click事件爲例,document對象(DOM級規範要求從document開始傳播,可是如今的瀏覽器是從window對象開始的)最早接收到click事件的而後事件沿着DOM樹依次向下傳播,一直傳播到事件的實際目標;我測試了一下(我用的都是最新的瀏覽器),chrome,opera,firefox,IE11到IE9都支持事件捕獲。

代碼等着我在下面講DOM事件流再一塊說明;

  • DOM事件流

  DOM2級中規定了事件流要包括三個階段:事件捕獲階段、處於目標階段、事件冒泡階段。這是W3C採用了他們兩家的事件監聽機制。(說點題外話,w3c中的不少標準就是這樣,瀏覽器廠商有不少本身的私有解決問題方式,好用的就被W3c採納了)DOM2級還規定,實際發生事件的元素在捕獲階段不能接收到事件。咱們就以上面的事件冒泡時的代碼說明這個過程:按照標準是這樣的,當一個元素產生了事件,事件是從document到html再到body再到DIV爺爺再到DIV爸爸,這時候捕獲階段就應該中止了,再進入下一個階段「處於目標階段」,而後是從DIV爸爸到DIV爺爺再到body再到html再到document,這就是事件冒泡階段;實際上咱們把處於目標階段即第二階段看做是冒泡階段的一部分,即冒泡的開始;其實是怎麼樣的呢?先上代碼,仍是前面的代碼只是改了一下js代碼:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
<style type="text/css">
#child{ background: red; width:50px; height:50px; }
#father{ width:100px; height:100px; background:green; }
#grandparent{ width:150px; height:150px; background:black; margin:100px auto 0; }
</style>
</head>
<body>
<div id="grandparent">
<div id="father">
<div id="child"></div></div>
</div>
</body>
<script type="text/javascript">
var grandparent = document.getElementById("grandparent");
var parent = document.getElementById("father");
var child = document.getElementById('child');
var html = document.getElementsByTagName("html")[0];
var body = document.body;
grandparent.addEventListener("click",function ()
{console.log("I am capturing grandparent"); },true);
grandparent.addEventListener("click",function () {console.log("I am grandparent"); },false);
parent.addEventListener("click",function() {console.log("I am parent"); },false);
parent.addEventListener("click",function() { console.log("I am capturing parent"); },true);
child.addEventListener("click",function() { console.log("I am capturing child"); },true);
child.addEventListener("click",function() { console.log("I am child"); },false);
body.addEventListener("click",function() { console.log("I am body"); },false);
body.addEventListener("click",function() { console.log("I am capturing body"); },true);
html.addEventListener("click",function() { console.log("I am capturing html"); },true);
html.addEventListener("click",function() { console.log("I am html"); },false);
document.addEventListener("click",function() { console.log("I am capturing document"); },true);
document.addEventListener("click",function() { console.log("I am document"); },false);
window.addEventListener("click",function() {console.log("I am window"); },false);
window.addEventListener("click",function() {console.log("I am capturing window"); },true);
</script></html>

代碼有點多見諒了!也是爲了最能說明問題!

打印是這樣的:

 

這是我點擊最裏面DIV兒子元素所發生的情形,能夠看出捕獲階段也能觸發目標元素上的事件,而不只僅是在冒泡階段;而且仍是從window開始,到最後再以window對象結束,瀏覽器廠商就是任性,不把W3c看在眼裏。你的標準我想實現就實現不想就不實現;

當我把DIV爺爺的事件綁定方式換成DOM0級的方式,其餘的保持不變,即

grandparent.onclick = function() {    console.log("我是在哪一個階段發生呢?")  }

是這樣打印的

再次說明了我上面在IE事件流中強調的,用DOM0級綁定事件時,事件只發生冒泡的階段;

 

二、Event屬性和方法:

1. type:事件的類型,如onlick中的click;

2. srcElement/target:事件源,就是發生事件的元素;

3. button:聲明被按下的鼠標鍵,整數,1表明左鍵,2表明右鍵,4表明中鍵,若是按下多個鍵,酒把這些值加起來,因此3就表明左右鍵同時按下;(firefox中 0表明左鍵,1表明中間鍵,2表明右鍵)

4. clientX/clientY:事件發生的時候, 鼠標相對於瀏覽器窗口可視文檔區域的左上角的位置;(在DOM標準中,這兩個屬性值都不考慮文檔的滾動狀況,也就是說,不管文檔滾動到哪裏,只要事件發生在窗口左上角,clientX和clientY都是 0, 因此在IE中,要想獲得事件發生的座標相對於文檔開頭的位置,要加上
document.body.scrollLeft和 document.body.scrollTop)

5. offsetX,offsetY/layerX,layerY:事件發生的時候, 鼠標相對於源元素左上角的位置

6. x,y/pageX,pageY:檢索相對於 父元素鼠標水平座標的整數

7. altKey,ctrlKey,shiftKey等:返回一個布爾值;

8. keyCode:返回keydown和keyup事件發生的時候按鍵的代碼,以及keypress 事件的Unicode字符;(firefox2不支持 event.keycode,能夠用 event.which替代 )

9. fromElement,toElement:前者是指代mouseover事件中鼠標移動過的文檔元素,後者指代mouseout事件中鼠標移動到的文檔元素;

10. cancelBubble:一個布爾屬性,把它設置爲true的時候,將中止事件進一步起泡到包容層次的元素;(e.cancelBubble = true; 至關於 e.stopPropagation();)

11. returnValue:一個布爾屬性,設置爲false的時候能夠組織瀏覽器執行默認的事件動做;(e.returnValue = false; 至關於 e.preventDefault();)

12. attachEvent(),detachEvent()/addEventListener(),removeEventListener:爲制定 DOM對象事件類型註冊多個事件處理函數的方法,它們有兩個參數,第一個是事件類型,第二個是事件處理函數。在
attachEvent()事件執行的時候,this關鍵字指向的是window對象,而不是發生事件的那個元素;
IE:obj.attachEvent(事件名稱,事件函數);
        一、沒有捕獲;
        二、事件名稱有on
        三、函數執行的順序:標準IE--正序,非標準IE--倒序;
標準:obj.addEventListener(事件名稱,事件函數,是否捕獲);
        一、有捕獲
         二、事件名稱沒有on
        三、事件執行的順序是正序的;
       四、this 觸發該事件的對象;
//是否捕獲?:默認是false
 false:冒泡
var dv1=document.getElementById("d1");
dv1.addEventListener("click",function(){alert(1);},false);/*告訴div1,若是有一個 出去的事件觸發了你,你就去執行函數體,alert(1);*/
 true:捕獲
  dv1.addEventListener("click",function(){alert(3);},true);/*告訴div1,若是有一個進來的事件觸發了你,你就去執行函數體,alert(3);*/
 
事件的取消:
 
(1)、普通綁定方式:
function fn1(){alert(1);}     function fn2(){alert(1);}
document.onClick=fn1;----->取消事件綁定: document.Onclick=null;
(2) 、attachEvent(事件名稱,事件函數) 綁定方式:
document.attachEvent("onclick",fn1);document.attachEvent("onclick",fn2);----->取消事件綁定:document. detachEvent("onclick",fn1);
(3)、addEventListener(事件名稱,事件函數,是否捕獲);
document.addEventListener("click",fn1,flase);document.addEventListener("click",fn1,true);document.addEventListener("click",fn2,flase);
----->取消事件綁定:document. removeEventListener("click",fn1,false);
        


13. screenX、screenY: 鼠標指針相對於顯示器左上角的位置,若是你想打開新的窗口,這兩個屬性很重要;


一些說明:

1.  event表明事件的狀態,例如觸發event對象的元素、鼠標的位置及狀態、按下的鍵等等;

2.  event對象只在事件發生的過程當中纔有效。
firefox裏的event跟IE裏的不一樣,IE裏的是全局變量,隨時可用;firefox裏的要用參數引導才能用,是運行時的臨時變量。
在IE/Opera中是window.event,在Firefox中是event;而事件的對象,在IE中是 window.event.srcElement,在Firefox中是event.target,Opera中二者均可用。

3.  下面兩句效果相同
var evt = (evt) ? evt : ((window.event) ? window.event : null);
var evt = evt || window.event; // firefox下window.event爲null, IE下event爲null

4.  IE中事件的起泡
IE中事件能夠沿着包容層次一點點起泡到上層,也就是說,下層的DOM節點定義的事件處理函數,到了上層的節點若是還有和下層相同事件類型的事件處理函數,那麼上層的事件處理函數也會執行。例如, div 標籤包含了 a ,若是這兩個標籤都有onclick事件的處理函數,那麼執行的狀況就是先執行標籤 a 的onclick事件處理函數,再執行 div 的事件處理函數。 若是但願的事件處理函數執行完畢以後,不但願執行上層的 div 的onclick的事件處理函數了,那麼就把cancelBubble設置爲true便可。
相關文章
相關標籤/搜索