国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

javaScript 事件綁定、事件冒泡、事件捕獲和事件執(zhí)行順序整理總結(jié)

2019-11-20 08:47:17
字體:
供稿:網(wǎng)友

抽空學(xué)習(xí)了下javascript和jquery的事件設(shè)計(jì),收獲頗大,總結(jié)此貼,和大家分享。

(一)事件綁定的幾種方式

javascript給DOM綁定事件處理函數(shù)總的來說有2種方式:在html文檔中綁定、在js代碼中綁定。下面的方式1、方式2屬于在html中綁定事件,方式3、方式4和方式5屬于在js代碼中綁定事件,其中方法5是最推薦的做法。

方式1:

HTML的DOM元素支持onclick、onblur等以on開頭屬性,我們可以直接在這些屬性值中編寫javascript代碼。當(dāng)點(diǎn)擊div的時候,下面的代碼會彈出div的ID:

<div id="outestA" onclick="var id = this.id;alert(id);return false;"></div>

這種做法很顯然不好,因?yàn)榇a都是放在字符串里的,不能格式化和排版,當(dāng)代碼很多的時候很難看懂。這里有一點(diǎn)值得說明:onclick屬性中的this代表的是當(dāng)前被點(diǎn)擊的DOM對象,所以我們可以通過this.id獲取DOM元素的id屬性值。

方式2:

當(dāng)代碼比較多的時候,我們可以在onclick等屬性中指定函數(shù)名。

<script> function buttonHandler(thisDom) { alert(this.id);//undefined alert(thisDom.id);//outestA return false; }</script><div id="outestA" onclick="return buttonHandler(this);"></div>

跟上面的做法相比,這種做法略好一些。值得一提的是:事件處理函數(shù)中的this代表的是window對象,所以我們在onclick屬性值中,通過this將dom對象作為參數(shù)傳遞。

方式3:在JS代碼中通過dom元素的onclick等屬性

var dom = document.getElementById("outestA");dom.onclick = function(){alert("1=" + this.id);};dom.onclick = function(){alert("2=" + this.id);};

這種做法this代表當(dāng)前的DOM對象。還有一點(diǎn):這種做法只能綁定一個事件處理函數(shù),后面的會覆蓋前面的。

方式4:IE下使用attachEvent/detachEvent函數(shù)進(jìn)行事件綁定和取消。

attachEvent/detachEvent兼容性不好,IE6~IE11都支持該函數(shù),但是FF和Chrome瀏覽器都不支持該方法。而且attachEvent/detachEvent不是W3C標(biāo)準(zhǔn)的做法,所以不推薦使用。在IE瀏覽器下,attachEvent有以下特點(diǎn)。

a) 事件處理函數(shù)中this代表的是window對象,不是dom對象。

var dom = document.getElementById("outestA"); dom.attachEvent('onclick',a);    function a() {    alert(this.id);//undefined }

b) 同一個事件處理函數(shù)只能綁定一次。

var dom = document.getElementById("outestA"); dom.attachEvent('onclick',a); dom.attachEvent('onclick',a);  function a() {   alert(this.id);}

雖然使用attachEvent綁定了2次,但是函數(shù)a只會調(diào)用一次。

c)不同的函數(shù)對象,可以重復(fù)綁定,不會覆蓋。

var dom = document.getElementById("outestA"); dom.attachEvent('onclick',function(){alert(1);}); dom.attachEvent('onclick',function(){alert(1);}); // 當(dāng)outestA的click事件發(fā)生時,會彈出2個對話框 

匿名函數(shù)和匿名函數(shù)是互相不相同的,即使代碼完全一樣。所以如果我們想用detachEvent取消attachEvent綁定的事件處理函數(shù),那么綁定事件的時候不能使用匿名函數(shù),必須要將事件處事函數(shù)單獨(dú)寫成一個函數(shù),否則無法取消。

方式5:使用W3C標(biāo)準(zhǔn)的addEventListener和removeEventListener。

這2個函數(shù)是W3C標(biāo)準(zhǔn)規(guī)定的,F(xiàn)F和Chrome瀏覽器都支持,IE6/IE7/IE8都不支持這2個函數(shù)。不過從IE9開始就支持了這2個標(biāo)準(zhǔn)的API。

// type:事件類型,不含"on",比如"click"、"mouseover"、"keydown";// 而attachEvent的事件名稱,含含"on",比如"onclick"、"onmouseover"、"onkeydown";// listener:事件處理函數(shù)// useCapture是事件冒泡,還是事件捕獲,默認(rèn)false,代表事件冒泡類型addEventListener(type, listener, useCapture); 

a) 事件處理函數(shù)中this代表的是dom對象,不是window,這個特性與attachEvent不同。

var dom = document.getElementById("outestA"); dom.addEventListener('click', a, false);    function a() {    alert(this.id);//outestA }

b) 同一個事件處理函數(shù)可以綁定2次,一次用于事件捕獲,一次用于事件冒泡。

var dom = document.getElementById("outestA"); dom.addEventListener('click', a, false); dom.addEventListener('click', a, true);    function a() {    alert(this.id);//outestA }// 當(dāng)點(diǎn)擊outestA的時候,函數(shù)a會調(diào)用2次

如果綁定的是同一個事件處理函數(shù),并且都是事件冒泡類型或者事件捕獲類型,那么只能綁定一次。

var dom = document.getElementById("outestA"); dom.addEventListener('click', a, false); dom.addEventListener('click', a, false);    function a() {    alert(this.id);//outestA }// 當(dāng)點(diǎn)擊outestA的時候,函數(shù)a只會調(diào)用1次

c) 不同的事件處理函數(shù)可以重復(fù)綁定,這個特性與attachEvent一致。

(二)事件處理函數(shù)的執(zhí)行順序

方式1、方式2和方式3都不能實(shí)現(xiàn)事件的重復(fù)綁定,所以自然也就不存在執(zhí)行順序的問題。方式4和方式5可以重復(fù)綁定特性,所以需要了解下執(zhí)行順序的問題。如果你寫出依賴于執(zhí)行順序的代碼,可以斷定你的設(shè)計(jì)存在問題。所以下面的順序問題,僅作為興趣探討,沒有什么實(shí)際意義。直接上結(jié)論:addEventListener和attachEvent表現(xiàn)一致,如果給同一個事件綁定多個處理函數(shù),先綁定的先執(zhí)行。下面的代碼我在IE11、FF17和Chrome39都測試過。

<script> window.onload = function(){ <span style="white-space:pre"> </span>var outA = document.getElementById("outA");  outA.addEventListener('click',function(){alert(1);},false); outA.addEventListener('click',function(){alert(2);},true); outA.addEventListener('click',function(){alert(3);},true); outA.addEventListener('click',function(){alert(4);},true); };</script><body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> </div></body>

當(dāng)點(diǎn)擊outA的時候,會依次打印出1、2、3、4。這里特別需要注意:我們給outA綁定了多個onclick事件處理函數(shù),也是直接點(diǎn)擊outA觸發(fā)的事件,所以不涉及事件冒泡和事件捕獲的問題,即addEventListener的第三個參數(shù)在這種場景下,沒有什么用處。如果是通過事件冒泡或者是事件捕獲觸發(fā)outA的click事件,那么函數(shù)的執(zhí)行順序會有變化。

(三) 事件冒泡和事件捕獲

事件冒泡和事件捕獲很好理解,只不過是對同一件事情的不同看法,只不過這2種看法都很有道理。
我們知道HTML中的元素是可以嵌套的,形成類似于樹的層次關(guān)系。比如下面的代碼:

<div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div>  </div></div>

如果點(diǎn)擊了最內(nèi)側(cè)的outC,那么外側(cè)的outB和outC算不算被點(diǎn)擊了呢?很顯然算,不然就沒有必要區(qū)分事件冒泡和事件捕獲了,這一點(diǎn)各個瀏覽器廠家也沒有什么疑義。假如outA、outB、outC都注冊了click類型事件處理函數(shù),當(dāng)點(diǎn)擊outC的時候,觸發(fā)順序是A-->B-->C,還是C-->B-->A呢?如果瀏覽器采用的是事件冒泡,那么觸發(fā)順序是C-->B-->A,由內(nèi)而外,像氣泡一樣,從水底浮向水面;如果采用的是事件捕獲,那么觸發(fā)順序是A-->B-->C,從上到下,像石頭一樣,從水面落入水底。

事件冒泡見下圖:


事件捕獲見下圖:

一般來說事件冒泡機(jī)制,用的更多一些,所以在IE8以及之前,IE只支持事件冒泡。IE9+/FF/Chrome這2種模型都支持,可以通過addEventListener((type, listener, useCapture)的useCapture來設(shè)定,useCapture=false代表著事件冒泡,useCapture=true代表著采用事件捕獲。

<script> window.onload = function(){ var outA = document.getElementById("outA");  var outB = document.getElementById("outB");  var outC = document.getElementById("outC");   // 使用事件冒泡 outA.addEventListener('click',function(){alert(1);},false); outB.addEventListener('click',function(){alert(2);},false); outC.addEventListener('click',function(){alert(3);},false); }; </script><body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;">  <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div>  </div> </div></body>

使用的是事件冒泡,當(dāng)點(diǎn)擊outC的時候,打印順序是3-->2-->1。如果將false改成true使用事件捕獲,打印順序是1-->2-->3。

(四) DOM事件流

DOM事件流我也不知道怎么解釋,個人感覺就是事件冒泡和事件捕獲的結(jié)合體,直接看圖吧。

DOM事件流:將事件分為三個階段:捕獲階段、目標(biāo)階段、冒泡階段。先調(diào)用捕獲階段的處理函數(shù),其次調(diào)用目標(biāo)階段的處理函數(shù),最后調(diào)用冒泡階段的處理函數(shù)。這個過程很類似于Struts2框中的action和Interceptor。當(dāng)發(fā)出一個URL請求的時候,先調(diào)用前置攔截器,其次調(diào)用action,最后調(diào)用后置攔截器。

<script> window.onload = function(){ var outA = document.getElementById("outA");  var outB = document.getElementById("outB");  var outC = document.getElementById("outC");   // 目標(biāo)(自身觸發(fā)事件,是冒泡還是捕獲無所謂) outC.addEventListener('click',function(){alert("target");},true);  // 事件冒泡 outA.addEventListener('click',function(){alert("bubble1");},false); outB.addEventListener('click',function(){alert("bubble2");},false);  // 事件捕獲 outA.addEventListener('click',function(){alert("capture1");},true); outB.addEventListener('click',function(){alert("capture2");},true);  }; </script><body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;">  <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div>  </div> </div></body>

當(dāng)點(diǎn)擊outC的時候,依次打印出capture1-->capture2-->target-->bubble2-->bubble1。到這里是不是可以理解addEventListener(type,handler,useCapture)這個API中第三個參數(shù)useCapture的含義呢?useCapture=false意味著:將事件處理函數(shù)加入到冒泡階段,在冒泡階段會被調(diào)用;useCapture=true意味著:將事件處理函數(shù)加入到捕獲階段,在捕獲階段會被調(diào)用。從DOM事件流模型可以看出,捕獲階段的事件處理函數(shù),一定比冒泡階段的事件處理函數(shù)先執(zhí)行。

(五) 再談事件函數(shù)執(zhí)行先后順序

在DOM事件流中提到過:

// 目標(biāo)(自身觸發(fā)事件,是冒泡還是捕獲無所謂)
outC.addEventListener('click',function(){alert("target");},true);

我們在outC上觸發(fā)onclick事件(這個是目標(biāo)對象),如果我們在outC上同時綁定捕獲階段/冒泡階段事件處理函數(shù)會怎么樣呢?

<script> window.onload = function(){ var outA = document.getElementById("outA");  var outB = document.getElementById("outB");  var outC = document.getElementById("outC");   // 目標(biāo)(自身觸發(fā)事件,是冒泡還是捕獲無所謂) outC.addEventListener('click',function(){alert("target2");},true); outC.addEventListener('click',function(){alert("target1");},true);  // 事件冒泡 outA.addEventListener('click',function(){alert("bubble1");},false); outB.addEventListener('click',function(){alert("bubble2");},false);  // 事件捕獲 outA.addEventListener('click',function(){alert("capture1");},true); outB.addEventListener('click',function(){alert("capture2");},true);   }; </script><body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;">  <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div>  </div> </div></body>

點(diǎn)擊outC的時候,打印順序是:capture1-->capture2-->target2-->target1-->bubble2-->bubble1。由于outC是我們觸發(fā)事件的目標(biāo)對象,在outC上注冊的事件處理函數(shù),屬于DOM事件流中的目標(biāo)階段。目標(biāo)階段函數(shù)的執(zhí)行順序:先注冊的先執(zhí)行,后注冊的后執(zhí)行。這就是上面我們說的,在目標(biāo)對象上綁定的函數(shù)是采用捕獲,還是采用冒泡,都沒有什么關(guān)系,因?yàn)槊芭莺筒东@只是對父元素上的函數(shù)執(zhí)行順序有影響,對自己沒有什么影響。如果不信,可以將下面的代碼放進(jìn)去驗(yàn)證。

// 目標(biāo)(自身觸發(fā)事件,是冒泡還是捕獲無所謂)outC.addEventListener('click',function(){alert("target1");},false);outC.addEventListener('click',function(){alert("target2");},true);outC.addEventListener('click',function(){alert("target3");},true);outC.addEventListener('click',function(){alert("target4");},false);

至此我們可以給出事件函數(shù)執(zhí)行順序的結(jié)論了:捕獲階段的處理函數(shù)最先執(zhí)行,其次是目標(biāo)階段的處理函數(shù),最后是冒泡階段的處理函數(shù)。目標(biāo)階段的處理函數(shù),先注冊的先執(zhí)行,后注冊的后執(zhí)行。

(六) 阻止事件冒泡和捕獲

默認(rèn)情況下,多個事件處理函數(shù)會按照DOM事件流模型中的順序執(zhí)行。如果子元素上發(fā)生某個事件,不需要執(zhí)行父元素上注冊的事件處理函數(shù),那么我們可以停止捕獲和冒泡,避免沒有意義的函數(shù)調(diào)用。前面提到的5種事件綁定方式,都可以實(shí)現(xiàn)阻止事件的傳播。由于第5種方式,是最推薦的做法。所以我們基于第5種方式,看看如何阻止事件的傳播行為。IE8以及以前可以通過 window.event.cancelBubble=true阻止事件的繼續(xù)傳播;IE9+/FF/Chrome通過event.stopPropagation()阻止事件的繼續(xù)傳播。

<script> window.onload = function(){ var outA = document.getElementById("outA");  var outB = document.getElementById("outB");  var outC = document.getElementById("outC");   // 目標(biāo) outC.addEventListener('click',function(event){  alert("target");  event.stopPropagation(); },false); // 事件冒泡 outA.addEventListener('click',function(){alert("bubble");},false); // 事件捕獲 outA.addEventListener('click',function(){alert("capture");},true);   }; </script><body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;">  <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div>  </div> </div></body>

當(dāng)點(diǎn)擊outC的時候,之后打印出capture-->target,不會打印出bubble。因?yàn)楫?dāng)事件傳播到outC上的處理函數(shù)時,通過stopPropagation阻止了事件的繼續(xù)傳播,所以不會繼續(xù)傳播到冒泡階段。

最后再看一段更有意思的代碼:

<script> window.onload = function(){ var outA = document.getElementById("outA");  var outB = document.getElementById("outB");  var outC = document.getElementById("outC");   // 目標(biāo) outC.addEventListener('click',function(event){alert("target");},false); // 事件冒泡 outA.addEventListener('click',function(){alert("bubble");},false); // 事件捕獲 outA.addEventListener('click',function(){alert("capture");event.stopPropagation();},true);   }; </script><body> <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;">  <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div>  </div> </div></body>

執(zhí)行結(jié)果是只打印capture,不會打印target和bubble。神奇吧,我們點(diǎn)擊了outC,但是卻沒有觸發(fā)outC上的事件處理函數(shù),而是觸發(fā)了outA上的事件處理函數(shù)。原因不做解釋,如果你還不明白,可以再讀一遍本文章。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 黄陵县| 枣强县| 石林| 徐水县| 个旧市| 乳山市| 东安县| 望都县| 陈巴尔虎旗| 山阳县| 砚山县| 兰州市| 托克逊县| 太原市| 丽江市| 资兴市| 阳谷县| 通渭县| 平原县| 张掖市| 金坛市| 英山县| 浮梁县| 米泉市| 若尔盖县| 曲周县| 建瓯市| 昭苏县| 衡阳县| 嵊州市| 麦盖提县| 应城市| 建宁县| 冀州市| 大关县| 昆明市| 南阳市| 达拉特旗| 花莲县| 嵊州市| 景德镇市|