日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

JS事件流和事件委托

發布時間:2025/7/14 javascript 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JS事件流和事件委托 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在上一篇《JS知識點大雜燴》中說到了事件流但沒有詳細的介紹,這篇文章就來介紹一下事件流。

事件流一共由三個階段分別是:

1.捕獲階段 2.目標階段 3.冒泡階段 復制代碼

事件綁定大家都知道,有DOM0級(on+type)和DOM2級(addEventListener),我覺得說那么多概念不好理解,直接看代碼吧,為了方便我就直接使用id來獲取元素。

  • DOM0級
<div id="box1"></div> box1.onclick = function(){console.log('box1'); } 復制代碼

輸出了box1這個我們都知道,再來看一下。

<div id="box1"></div> box1.onclick = function(){console.log('box1'); } box1.onclick = function(){console.log('box1 two'); } 復制代碼

輸出了box1 two,因為DOM0級會覆蓋掉之前在同一元素上面的綁定,再來看一下。

<div id="box1"><div id="box2"><div id="box3"></div></div> </div> box1.onclick = function(){console.log('box1'); } box2.onclick = function(){console.log('box2'); } box3.onclick = function(){console.log('box3'); } 復制代碼

當我們點擊box1時都知道輸出box1,可是當我們點擊box3時彈出什么呢?

你可能會感覺奇怪,為什么我點擊的box3怎么其他的也會觸發?因為事件冒泡。那什么是事件冒泡呢?概念請自行百度,直接上圖。

這就叫做事件冒泡,一級一級往上冒直到window這里我沒有畫出來。DOM0級只支持冒泡階段。

  • DOM2級
<div id="box1"><div id="box2"><div id="box3"></div></div> </div> box1.addEventListener('click', function(){console.log('box1'); },false); box2.addEventListener('click', function(){console.log('box2'); },false); box3.addEventListener('click', function(){console.log('box3'); },false); 復制代碼

輸出跟上面是一樣的,因為我們綁定在了冒泡階段。(true捕獲,false冒泡)。 我們再來看看捕獲階段是怎么樣的

<div id="box1"><div id="box2"><div id="box3"></div></div> </div> box1.addEventListener('click', function(){console.log('box1'); },true); box2.addEventListener('click', function(){console.log('box2'); },true); box3.addEventListener('click', function(){console.log('box3'); },true); 復制代碼

我們點擊box3看到

你可能會發現順序反過來了,那這是為什么呢?因為事件捕獲,那什么是時間捕獲呢?概念請自行百度,直接上圖。

這就是捕獲階段,跟冒泡階段完全相反。

那冒泡跟捕獲的執行順序是什么樣的呢?我分別在每一個元素上綁定了兩個階段的同一事件,我們來看看觸發的順序。

<div id="box1"><div id="box2"><div id="box3"></div></div> </div> 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); 復制代碼

我們點擊box3看到,先捕獲后冒泡。

那是不是都這樣呢?我們稍微改動一下。

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的冒泡階段后面 復制代碼

看看觸發的順序是不是還一樣呢?

發現反過來了,其實這就叫做目標階段吧。在你觸發事件的目標元素身上不區分冒泡捕獲,按綁定的順序來執行。

我們用圖來看一下。

這樣是不是太簡單我們來一點復雜的。

<div id="box1"><div id="box2"><div id="box3"></div></div> </div> 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'); } 復制代碼

觸發順序是什么樣的?(我覺得你最好先自己把答案寫出來)

看看你答對了沒有

這樣會不會太簡單,換一下順序

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); 復制代碼

答對了嗎?

  • 再說一下冒泡和捕獲
<div id="box1"><div id="box2"><div id="box3"></div></div> </div> 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); 復制代碼

你說這樣點擊會輸出什么?你是不是猶豫了?說明你還是不懂冒泡和捕獲。

不要讓你看到的騙了你,冒泡是DOM結構的父子關系而不是看起來是不是包裹的關系。(答案同上面)。

  • 忘了IE了,說一下IE的事件機制。

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') }) 復制代碼

這個會輸出什么?(提示:不會覆蓋) 答案是:box1 box2 box3 哈哈,開玩笑啊。

是不是很奇怪,IE中該事件是先綁定的后輸出。IE6、7、8,不支持事件捕獲只支持事件冒泡。

  • 阻止事件冒泡兼容

我們先來說一下不支持冒泡的事件:blur、focus、mouseenter、mouseleave。(我就知道這些) 還是這個例子,我們看一下阻止冒泡。

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){ //IEevent.cancelBubble = true;} } 復制代碼

阻止默認事件兼容

function preventDef(e){var g = e || window.event;if(g.preventDefault){g.preventDefault();}else if(g.returnValue){g.returnValue = false;}return false; } 復制代碼

我們再來看看事件綁定的this指向

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]

我們發現,IE6、7、8 this指向window

  • 擴展 事件委托
<ul id="ul"><li>1</li><li>2</li><li>3</li> </ul> 復制代碼

如果我們要監聽每一個li的行為,你會不會這么做。

let li = document.getElementsByTagName("li"); for (var i = 0; i < li.length; i++) {li.onclick = ()=>{console.log(i);} } 復制代碼

評論區有位大佬指出了我的錯誤,因為這樣形成了閉包,得不到輸出的結果。因為當時只想演示事件委托沒有注意到這個情況,在此為讀者們表示深深的歉意。下面是更改之后的,之所以不把錯誤的修改是想告誡自己還有和我犯同樣錯誤的人。

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); } 復制代碼

這樣做是對的單不夠好,要是再加幾個li或有很多的100|1000個li你還這樣做是不是感覺就不好了。我們就需要為每一個li注冊事件,麻煩不說,注冊很多事件就不好。那么我們怎么辦的,使用事件委托,就是把你的事件委托給別人(父級),利用事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件。

我們來看一下。

ul.onclick = function (e){console.log(e.target); } 復制代碼

這就是事件委托。

  • 優點:
    • 可以大量節省內存占用,減少事件注冊。
    • 可以實現當新增子對象時,無需再對其進行事件綁定,對于動態內容部分尤為合適
  • 缺點:
    • 事件代理的常用應用應該僅限于上述需求,如果把所有事件都用事件代理,可能會出現事件誤判。即本不該被觸發的事件被綁定上了事件。

轉載于:https://juejin.im/post/5c1752326fb9a049bd4234e2

總結

以上是生活随笔為你收集整理的JS事件流和事件委托的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。