移除元素所有事件监听_DOM 事件模型或 DOM 事件机制
DOM 事件模型
DOM 的事件操作(監(jiān)聽和觸發(fā)),都定義在EventTarget接口。所有節(jié)點對象都部署了這個接口,其他一些需要事件通信的瀏覽器內(nèi)置對象(比如,XMLHttpRequest、AudioNode、AudioContext)也部署了這個接口。
該接口主要提供三個實例方法。
- addEventListener:綁定事件的監(jiān)聽函數(shù)
- removeEventListener:移除事件的監(jiān)聽函數(shù)
- dispatchEvent:觸發(fā)事件
事件模型
一個事件發(fā)生后,會在子元素及父元素之間進行傳播(propagation),這種傳播分為三個階段。
(這種三階段的傳播模型,使得同一個事件會在多個節(jié)點上觸發(fā)。)
通俗一點來說就是一個事件被觸發(fā)時,瀏覽器會自動從用戶操作標(biāo)簽外的最上級標(biāo)簽逐漸向里檢查是否有相同事件,如果有則觸發(fā),如果沒有則繼續(xù)向下檢查知道用戶操作的標(biāo)簽,這過程稱為捕獲,此時瀏覽器會繼續(xù)由用戶操作標(biāo)簽繼續(xù)向是上級標(biāo)簽檢查,如果有相同事件則觸發(fā),如果沒有則繼續(xù)向上檢查直到最上級元素為止,此過程稱為冒泡。(有監(jiān)聽函數(shù)就執(zhí)行,并提供事件信息,沒有就跳過)
事件傳播的最上層對象是window,上例的事件傳播順序,在捕獲階段依次為window、document、html、body、父節(jié)點、目標(biāo)節(jié)點,在冒泡階段依次為目標(biāo)節(jié)點、父節(jié)點、body、html、document、window。
DOM事件傳播的三個階段:捕獲階段,目標(biāo)階段,冒泡階段
點擊事件
代碼:
<div class="grandfather"><div class="father"><div class="son"></div>word</div> </div>即.grandfather>.father>.son
給三個div分別添加事件的監(jiān)聽fnYe/fnBa/fnEr
提問1:點擊了誰?
點擊文字,算不算點擊兒子?
點擊文字,算不算點擊爸爸?
點擊文字,算不算點擊爺爺?
答案:都算
提問2:調(diào)用循序
點擊文字,最先調(diào)用fnYe/fnBa/fnEr中的那一個函數(shù)?
答案:都行
IE5認為先調(diào)用fnEr,網(wǎng)景認為先調(diào)用fnYe,最后遇到了W3C
2002年,w3c發(fā)布標(biāo)準文檔名為DOM Level 2 Events Specification
規(guī)定瀏覽器應(yīng)該同時支持兩種調(diào)用順序
首先按照grandfather->father->son
然后按照son->father->grandfather
術(shù)語:
從外向內(nèi)找監(jiān)聽函數(shù),叫做事件捕捉從內(nèi)向外找監(jiān)聽函數(shù),叫做事件冒泡
那豈不是fnYe/fnBa/fnEr都調(diào)用兩次,非也!
開發(fā)者可以自己決定把fnYe放在捕捉階段還是放在冒泡階段
addEventListener事件綁定API
IE5*:baba.attachEvent('onclick',fn)//冒泡
網(wǎng)景:baba.addEventListener('click',fn)//捕獲
W3C:baba.addEventListener('click',fn,bool)
如果bool不傳或為falsy
就讓fn走冒泡,即當(dāng)瀏覽器在冒泡階段發(fā)現(xiàn)baba有fn監(jiān)聽函數(shù),就會調(diào)用fn,并提供時間信息。
如果bool為true
就讓fn走捕獲,即當(dāng)瀏覽器在捕獲階段發(fā)現(xiàn)baba有fn監(jiān)聽函數(shù),就會調(diào)用fn,并且提供事件信息。
代碼演示:
<!DOCTYPE html> <html> <head><meta charset="utf-8"><title>JS Bin</title> </head> <body> <div class="level1 x"><div class="level2 x"><div class="level3 x"><div class="level4 x"><div class="level5 x"><div class="level6 x"><div class="level7 x"></div></div></div></div></div></div> </div></body> </html>CSS:
* {box-sizing: border-box; } div[class^=level] {border: 1px solid;border-radius: 50%;display: inline-flex; } .level1 {padding: 10px;background: purple; } .level2 {padding: 10px;background: blue; } .level3 {padding: 10px;background: cyan; } .level4 {padding: 10px;background: green; } .level5 {padding: 10px;background: yellow; } .level6 {padding: 10px;background: orange; } .level7 {width: 50px;height: 50px;border: 1px solid;background: red;border-radius: 50%; } .x{background: transparent;//把元素的變?yōu)橥该?}Javascript代碼:
const level1 = document.querySelector('.level1') const level2 = document.querySelector('.level2') const level3 = document.querySelector('.level3') const level4 = document.querySelector('.level4') const level5 = document.querySelector('.level5') const level6 = document.querySelector('.level6') const level7 = document.querySelector('.level7')let n = 1level1.addEventListener('click', (e)=>{const t = e.currentTarget//e只有在點擊得一瞬間才會出現(xiàn),所以要用t來記錄一下。setTimeout(()=>{ t.classList.remove('x')},n*1000)n+=1//因為如果每個時間都為1000那么就相當(dāng)于在8點同時設(shè)置很多的鬧鐘,。知識 }) level2.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{ t.classList.remove('x')},n*1000)n+=1 }) level3.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{ t.classList.remove('x')},n*1000)n+=1 }) level4.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{ t.classList.remove('x')},n*1000)n+=1 }) level5.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{ t.classList.remove('x')},n*1000)n+=1 }) level6.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{ t.classList.remove('x')},n*1000)n+=1 }) level7.addEventListener('click', (e)=>{const t = e.currentTargetsetTimeout(()=>{ t.classList.remove('x')},n*1000)n+=1 })簡化:
const level1 = document.querySelector('.level1') const level2 = document.querySelector('.level2') const level3 = document.querySelector('.level3') const level4 = document.querySelector('.level4') const level5 = document.querySelector('.level5') const level6 = document.querySelector('.level6') const level7 = document.querySelector('.level7')let n = 1const fm = (e)=>{const t = e.currentTargetsetTimeout(()=>{ t.classList.remove('x')},n*1000)n+=1 }const fa = (e)=>{const t =e.currentTargetsetTimeout(()=>{t.classList.add('x')},n*1000)n+=1}level1.addEventListener('click',fm,true) level1.addEventListener('click',fa) level2.addEventListener('click',fm,true) level2.addEventListener('click',fa) level3.addEventListener('click',fm,true) level3.addEventListener('click',fa) level4.addEventListener('click',fm,true) level4.addEventListener('click',fa) level5.addEventListener('click',fm,true) level5.addEventListener('click',fa) level6.addEventListener('click',fm,true) level6.addEventListener('click',fa) level7.addEventListener('click',fm,true) level7.addEventListener('click',fa)知識復(fù)習(xí):
classList:
定義和用法
classList 屬性返回元素的類名,作為 DOMTokenList 對象。
該屬性用于在元素中添加,移除及切換 CSS 類。
classList 屬性是只讀的,但你可以使用 add() 和 remove() 方法修改它。
HTML DOM classList 屬性?www.runoob.comcurrentTarget 事件屬性
定義和用法
currentTarget 事件屬性返回其監(jiān)聽器觸發(fā)事件的節(jié)點,即當(dāng)前處理該事件的元素、文檔或窗口。
在捕獲和起泡階段,該屬性是非常有用的,因為在這兩個節(jié)點,它不同于 target 屬性。
總結(jié):
兩個疑問:
兒子被點擊,算不算點擊老子?
那么先調(diào)用老子得函數(shù)還是先調(diào)用兒子的函數(shù)?
捕獲冒泡
捕獲說先調(diào)用爸爸的監(jiān)聽函數(shù)
冒泡說先調(diào)用兒子的監(jiān)聽函數(shù)
W3C時間模型
先捕獲(先爸爸=>兒子)再冒泡(再兒子=>爸爸)
注意e對象被傳給所有的監(jiān)聽函數(shù)
事件結(jié)束后,e對象就不存在了
target v.s. currentTarget的區(qū)別
區(qū)別:
e.target - 用戶操作的元素e.currentTarget-程序員監(jiān)聽的元素
this是e.currentTarget,我個人不推薦使用它
舉例:
div>span{文字},用戶點擊文字e.target就是span
e.currentTarget就是div
一個特例
背景:
只有一個div被監(jiān)聽(不考慮父子同時被監(jiān)聽)
fn分別再捕獲階段和冒泡階段監(jiān)聽click事件
用戶點擊的元素就是開發(fā)者監(jiān)聽的
代碼:
div.addEventListenter('click',f1)
div.addEventListenter('click',f2,true)
請問,f1先執(zhí)行還是f2先執(zhí)行?
如果把兩個調(diào)換位置?
總結(jié):誰先監(jiān)聽誰先執(zhí)行。
level7.addEventListener('click',()=>{console.log(2) },true)//捕獲 level7.addEventListener('click',()=>{console.log(1) })//冒泡e.stopPropagation():取消冒泡
e.stopPropagation()可打斷冒泡,瀏覽器不再向上走
一般用于封裝某些獨立組件
注意:捕獲不可以取消但是冒泡可以
不可以取消冒泡
有些事件不可以取消冒泡
可以查閱MDN英文版冒泡
比如scroll:
Bubbles:冒泡
Cancelable:是否取消冒泡
如何禁用滾動
取消特定元素的wheel和touchstart的默認動作
JS Bin?js.jirengu.com瀏覽器自帶事件
來自MDN:
事件參考?developer.mozilla.org自定義事件:代碼
JS Bin?js.jirengu.com事件委托:
我委托一個元素幫我監(jiān)聽我本該監(jiān)聽的東西,比如onclick
場景1:
要給100個按鈕添加點擊事件,咋辦?
答:監(jiān)聽這個100個按鈕的祖先,等冒泡的時候判斷target是不是這100個按鈕中的一個
代碼:
JS Bin?js.jirengu.com場景2:
你要監(jiān)聽目前不存在的元素的點擊事件?
答:監(jiān)聽祖先,等點擊的時候看看是不是監(jiān)聽的元素即可。
優(yōu)點:省監(jiān)聽數(shù)(內(nèi)存),可以動態(tài)監(jiān)聽元素
代碼:
JS Bin?js.jirengu.com封裝一個事件委托
只要實行一個函數(shù)就可以實現(xiàn)事件委托
要求:
寫出這樣一個函數(shù)on('click','#testDiv','li',fn)
當(dāng)用戶點擊#testDiv里面的li元素時,調(diào)用fn函數(shù)
要求用到事件委托
答案1:判斷target是否匹配'li'
答案2:target/target的爸爸/target的爺爺
代碼:
JS Bin?js.jirengu.com錯的但是面試可以用:
答:給一個元素加一個監(jiān)聽,看當(dāng)前的target是否滿足監(jiān)聽函數(shù)(函數(shù)2)中函數(shù)2的條件如果滿足調(diào)用,不滿足放過。但是是錯的!
代碼:
setTimeout(()=>{const button = document.createElement('button')const span = document.createElement('span')span.textContent='click 1'button.appendChild(span)div1.appendChild(button) },1000)on('click','#div1','button',()=>{//'#div'是選擇器不是元素console.log('button 被點擊啦') }) function on(eventType,element,selector,fn){if(!(element instanceof Element)){element = document.querySelector(element)}element.addEventListener(eventType,(e)=>{const t= e.target//被點擊的元素是span不是button啦if(t.matches(selector)){//matches用來判斷一個元素是否匹配一個選擇器,selector是不是一個選擇器 span不匹配buttonfn(e)} }) }總結(jié)
以上是生活随笔為你收集整理的移除元素所有事件监听_DOM 事件模型或 DOM 事件机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python爬虫urllib 数据处理_
- 下一篇: windows 2008r2文件服务器部