摘要:那什么是事件冒泡呢概念請(qǐng)自行百度,直接上圖。,不支持事件捕獲只支持事件冒泡。即本不該被觸發(fā)的事件被綁定上了事件。
在上一篇《JS知識(shí)點(diǎn)大雜燴》中說到了事件流但沒有詳細(xì)的介紹,這篇文章就來介紹一下事件流。
事件流一共由三個(gè)階段分別是:
1.捕獲階段 2.目標(biāo)階段 3.冒泡階段
事件綁定大家都知道,有DOM0級(jí)(on+type)和DOM2級(jí)(addEventListener),我覺得說那么多概念不好理解,直接看代碼吧,為了方便我就直接使用id來獲取元素。
DOM0級(jí)
box1.onclick = function(){ console.log("box1"); }
輸出了box1這個(gè)我們都知道,再來看一下。
box1.onclick = function(){ console.log("box1"); } box1.onclick = function(){ console.log("box1 two"); }
輸出了box1 two,因?yàn)镈OM0級(jí)會(huì)覆蓋掉之前在同一元素上面的綁定,再來看一下。
box1.onclick = function(){ console.log("box1"); } box2.onclick = function(){ console.log("box2"); } box3.onclick = function(){ console.log("box3"); }
當(dāng)我們點(diǎn)擊box1時(shí)都知道輸出box1,可是當(dāng)我們點(diǎn)擊box3時(shí)彈出什么呢?
你可能會(huì)感覺奇怪,為什么我點(diǎn)擊的box3怎么其他的也會(huì)觸發(fā)?因?yàn)?strong>事件冒泡。那什么是事件冒泡呢?概念請(qǐng)自行百度,直接上圖。
這就叫做事件冒泡,一級(jí)一級(jí)往上冒直到window這里我沒有畫出來。DOM0級(jí)只支持冒泡階段。
DOM2級(jí)
box1.addEventListener("click", function(){ console.log("box1"); },false); box2.addEventListener("click", function(){ console.log("box2"); },false); box3.addEventListener("click", function(){ console.log("box3"); },false);
輸出跟上面是一樣的,因?yàn)槲覀兘壎ㄔ诹嗣芭蓦A段。(true捕獲,false冒泡)。
我們?cè)賮砜纯床东@階段是怎么樣的
box1.addEventListener("click", function(){ console.log("box1"); },true); box2.addEventListener("click", function(){ console.log("box2"); },true); box3.addEventListener("click", function(){ console.log("box3"); },true);
我們點(diǎn)擊box3看到
你可能會(huì)發(fā)現(xiàn)順序反過來了,那這是為什么呢?因?yàn)?strong>事件捕獲,那什么是時(shí)間捕獲呢?概念請(qǐng)自行百度,直接上圖。
這就是捕獲階段,跟冒泡階段完全相反。
那冒泡跟捕獲的執(zhí)行順序是什么樣的呢?我分別在每一個(gè)元素上綁定了兩個(gè)階段的同一事件,我們來看看觸發(fā)的順序。
box1.addEventListener("click", function(){ console.log("box1 捕獲階段"); },true); box2.addEventListener("click", function(){ console.log("box2 捕獲階段"); },true); box3.addEventListener("click", function(){ console.log("box3 捕獲階段"); },true); box1.addEventListener("click", function(){ console.log("box1 冒泡階段"); },false); box2.addEventListener("click", function(){ console.log("box2 冒泡階段"); },false); box3.addEventListener("click", function(){ console.log("box3 冒泡階段"); },false);
我們點(diǎn)擊box3看到,先捕獲后冒泡。
那是不是都這樣呢?我們稍微改動(dòng)一下。
box1.addEventListener("click", function(){ console.log("box1 捕獲階段"); },true); box2.addEventListener("click", function(){ console.log("box2 捕獲階段"); },true); box1.addEventListener("click", function(){ console.log("box1 冒泡階段"); },false); box2.addEventListener("click", function(){ console.log("box2 冒泡階段"); },false); box3.addEventListener("click", function(){ console.log("box3 冒泡階段"); },false); box3.addEventListener("click", function(){ console.log("box3 捕獲階段"); },true); // 將box3的捕獲階段放到box3的冒泡階段后面
看看觸發(fā)的順序是不是還一樣呢?
發(fā)現(xiàn)反過來了,其實(shí)這就叫做目標(biāo)階段吧。在你觸發(fā)事件的目標(biāo)元素身上不區(qū)分冒泡捕獲,按綁定的順序來執(zhí)行。
我們用圖來看一下。
這樣是不是太簡(jiǎn)單我們來一點(diǎn)復(fù)雜的。
box1.addEventListener("click", function(){ console.log("box1 捕獲階段"); },true); box2.addEventListener("click", function(){ console.log("box2 捕獲階段"); },true); box3.addEventListener("click", function(){ console.log("box3 捕獲階段"); },true); box1.addEventListener("click", function(){ console.log("box1 冒泡階段"); },false); box2.addEventListener("click", function(){ console.log("box2 冒泡階段"); },false); box3.addEventListener("click", function(){ console.log("box3 冒泡階段"); },false); box1.onclick = function () { console.log("box1 51561"); } box2.onclick = function () { console.log("box2"); } box3.onclick = function () { console.log("box3"); } box1.onclick = function () { console.log("box1"); }
觸發(fā)順序是什么樣的?(我覺得你最好先自己把答案寫出來)
看看你答對(duì)了沒有
這樣會(huì)不會(huì)太簡(jiǎn)單,換一下順序
box1.onclick = function () { console.log("box1 51561"); } box2.onclick = function () { console.log("box2"); } box3.onclick = function () { console.log("box3"); } box1.onclick = function () { console.log("box1"); } box1.addEventListener("click", function(){ console.log("box1 捕獲階段"); },true); box2.addEventListener("click", function(){ console.log("box2 捕獲階段"); },true); box1.addEventListener("click", function(){ console.log("box1 冒泡階段"); },false); box2.addEventListener("click", function(){ console.log("box2 冒泡階段"); },false); box3.addEventListener("click", function(){ console.log("box3 冒泡階段"); },false); box3.addEventListener("click", function(){ console.log("box3 捕獲階段"); },true);
答對(duì)了嗎?
再說一下冒泡和捕獲
box1.onclick = function () { console.log("box1 51561"); } box2.onclick = function () { console.log("box2"); } box3.onclick = function () { console.log("box3"); } box1.onclick = function () { console.log("box1"); } box1.addEventListener("click", function(){ console.log("box1 捕獲階段"); },true); box2.addEventListener("click", function(){ console.log("box2 捕獲階段"); },true); box1.addEventListener("click", function(){ console.log("box1 冒泡階段"); },false); box2.addEventListener("click", function(){ console.log("box2 冒泡階段"); },false); box3.addEventListener("click", function(){ console.log("box3 冒泡階段"); },false); box3.addEventListener("click", function(){ console.log("box3 捕獲階段"); },true);
你說這樣點(diǎn)擊會(huì)輸出什么?你是不是猶豫了?說明你還是不懂冒泡和捕獲。
不要讓你看到的騙了你,冒泡是DOM結(jié)構(gòu)的父子關(guān)系而不是看起來是不是包裹的關(guān)系。(答案同上面)。
忘了IE了,說一下IE的事件機(jī)制。
IE上面不支持addEventListener但是它有attachEvent
box1.onclick = function () { console.log("box1 51561"); } box2.onclick = function () { console.log("box2"); } box3.onclick = function () { console.log("box3"); } box1.onclick = function () { console.log("box1"); } box1.attachEvent("onclick", function (){ console.log("box1 attachEvent") }) box2.attachEvent("onclick", function (){ console.log("box2 attachEvent") }) box3.attachEvent("onclick", function (){ console.log("box3 attachEvent") })
box1.attachEvent("onclick", function (){ console.log("box1") }) box1.attachEvent("onclick", function (){ console.log("box2") }) box1.attachEvent("onclick", function (){ console.log("box3") })
這個(gè)會(huì)輸出什么?(提示:不會(huì)覆蓋)
答案是:box1 box2 box3
哈哈,開玩笑啊。
是不是很奇怪,IE中該事件是先綁定的后輸出。IE6、7、8,不支持事件捕獲只支持事件冒泡。
阻止事件冒泡兼容
我們先來說一下不支持冒泡的事件:blur、focus、mouseenter、mouseleave。(我就知道這些)
還是這個(gè)例子,我們看一下阻止冒泡。
box1.onclick = function (){ console.log("box1") } box2.onclick = function (){ console.log("box2") } box3.onclick = function (e){ e.stopPropagation(); console.log("box3") }
只輸出了box3.
雖然阻止了冒泡但在IE8及以下是不好使的,我們看一下兼容的寫法。
function stopPropagate(e){ var event = e || window.event; if(event.stopPropagation){ event.stopPropagation(); }else if(event.cancelBubble){ //IE event.cancelBubble = true; } }
阻止默認(rèn)事件兼容
function preventDef(e){ var g = e || window.event; if(g.preventDefault){ g.preventDefault(); }else if(g.returnValue){ g.returnValue = false; } return false; }
我們?cè)賮砜纯词录壎ǖ膖his指向
box1.onclick = function (){ console.log("onclick", this); } box1.addEventListener("click",function () { console.log("addEventListener", this); }, false)
IE6、7、8,事件綁定的this指向
box1.attachEvent("onclick",function () { console.log("attachEvent", this); })
attachEvent [object Window]
我們發(fā)現(xiàn),IE6、7、8 this指向window
擴(kuò)展 事件委托
如果我們要監(jiān)聽每一個(gè)li的行為,你會(huì)不會(huì)這么做。
let li = document.getElementsByTagName("li"); for (var i = 0; i < li.length; i++) { li.onclick = ()=>{ console.log(i); } }
評(píng)論區(qū)有位大佬指出了我的錯(cuò)誤,因?yàn)檫@樣形成了閉包,得不到輸出的結(jié)果。因?yàn)楫?dāng)時(shí)只想演示事件委托沒有注意到這個(gè)情況,在此為讀者們表示深深的歉意。下面是更改之后的,之所以不把錯(cuò)誤的修改是想告誡自己還有和我犯同樣錯(cuò)誤的人。
let li = document.getElementsByTagName("li"); //ES6 for (let i = 0; i < li.length; i++) { li[i].onclick = ()=>{ console.log(i); } } //IIFE(1) for (var i = 0; i < li.length; i++) { (function(j){ li[j].onclick = ()=>{ console.log(j); }})(i) } //IIFE(2) for (var i = 0; i < li.length; i++) { li[i].onclick = (function(j){ return ()=>{ console.log(j); } })(i); }
這樣做是對(duì)的單不夠好,要是再加幾個(gè)li或有很多的100|1000個(gè)li你還這樣做是不是感覺就不好了。我們就需要為每一個(gè)li注冊(cè)事件,麻煩不說,注冊(cè)很多事件就不好。那么我們?cè)趺崔k的,使用事件委托,就是把你的事件委托給別人(父級(jí)),利用事件冒泡,只指定一個(gè)事件處理程序,就可以管理某一類型的所有事件。
我們來看一下。
ul.onclick = function (e){ console.log(e.target); }
這就是事件委托。
優(yōu)點(diǎn):
可以大量節(jié)省內(nèi)存占用,減少事件注冊(cè)。
可以實(shí)現(xiàn)當(dāng)新增子對(duì)象時(shí),無需再對(duì)其進(jìn)行事件綁定,對(duì)于動(dòng)態(tài)內(nèi)容部分尤為合適
缺點(diǎn):
事件代理的常用應(yīng)用應(yīng)該僅限于上述需求,如果把所有事件都用事件代理,可能會(huì)出現(xiàn)事件誤判。即本不該被觸發(fā)的事件被綁定上了事件。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100567.html
摘要:處于目標(biāo)階段事件在上發(fā)生并處理。冒泡階段事件又傳播回文檔。不支持捕獲事件的??偨Y(jié)二使用事件委托新添加的元素還會(huì)有之前的事件。事件對(duì)象在觸發(fā)某個(gè)事件時(shí),會(huì)產(chǎn)生一個(gè)事件對(duì)象。 js 是采用異步事件驅(qū)動(dòng)的機(jī)制來響應(yīng)用戶操作的,也就是說當(dāng)用戶對(duì)某個(gè)html元素進(jìn)行操作的時(shí)候,會(huì)產(chǎn)生一個(gè)事件,該事件會(huì)驅(qū)動(dòng)某些函數(shù)來處理。事件源:產(chǎn)生事件的地方(html元素,窗口,其他等等);事件:鼠標(biāo)事件,鍵盤...
摘要:響應(yīng)某個(gè)事件的函數(shù)就叫事件處理程序或事件偵聽器。為事件指定事件處理程序的方法主要有種。事件處理程序事件直接加在元素上。事件委托利用冒泡的原理,把事件加到父元素或祖先元素上,觸發(fā)執(zhí)行效果,解決事件處理程序過多問題。事件委托優(yōu)點(diǎn)提高性能。 JavaScript簡(jiǎn)單入門可以看看我丑丑的Github博客JavaScript簡(jiǎn)單入門 事件 JavaScript與HTML之間的交互是通過事件實(shí)現(xiàn)的...
摘要:事件流描述的是從頁面中接受事件的順序。事件流中的事件流是事件冒泡流。順序是從外向里級(jí)事件規(guī)定的事件流包括三個(gè)階段事件捕獲階段處于目標(biāo)階段和事件冒泡階段,其中到是處于目標(biāo)階段,如圖所示。添加的事件會(huì)被逆序執(zhí)行。 HTML和js之間的交互是通過事件實(shí)現(xiàn)的。 事件流描述的是從頁面中接受事件的順序。 事件流 IE中的事件流是事件冒泡流。順序是從里向外 eg:div-body-html-...
閱讀 3344·2021-11-19 11:36
閱讀 2949·2021-09-27 13:34
閱讀 2008·2021-09-22 15:17
閱讀 2418·2019-08-30 13:49
閱讀 768·2019-08-26 13:58
閱讀 1368·2019-08-26 10:47
閱讀 2549·2019-08-23 18:05
閱讀 611·2019-08-23 14:25