DOM監(jiān)聽事件詳解

DOM監(jiān)聽事件詳解

事件定義

? 事件并不是代碼世界里的專用詞,它僅僅是由簡單的:監(jiān)聽、變化、通知 三要素組成

? 在前端世界中,事件可以定義為:代碼監(jiān)聽(用戶),(用戶)操作產(chǎn)生變化、(程序員)得到通知。

DOM事件

如何監(jiān)聽事件

DOM level 0 事件監(jiān)聽方法: button.onclick = function(){}

這種方法是DOM level0就支持一種方法??梢杂米骱唵蔚谋O(jiān)聽。這個方法存在一個很大的問題。那就是如果一個元素綁定事件時,有可能覆蓋掉前面已經(jīng)綁定好的事件。

DOM level 2 事件監(jiān)聽方法: button.addEventListener('click', function(){})

這種方法和level 0的綁定方法是一致 ,但監(jiān)聽事件每次都會生產(chǎn)一個全新的匿名函數(shù),和前面的函數(shù)完全不同,不會覆蓋掉前面已經(jīng)綁定好的時間。

事件類型

Google: DOM events MDN

事件機制:冒泡 & 捕獲

監(jiān)聽事件中。子元素被點擊,意味著父元素也被點擊了。如果同時監(jiān)聽子元素和父元素,就會有個通知的先后順序

冒泡階段(默認使用)

事件從事件目標(target)開始,往上冒泡直到頁面的最上一級標簽。

簡單來說:就是child 先通知,parent后通知

如果想阻止事件冒泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)來阻止事件的冒泡傳播。

捕獲階段

相反的,事件從最上一級標簽開始往下查找,直到捕獲到事件目標(target)。

簡單來說:就是parent 先通知,child 后通知


可以通過改變addEventListener的第三個參數(shù)改變事件的執(zhí)行順序。(false為冒泡階段執(zhí)行,true為捕獲階段執(zhí)行,留空則為false)

事件傳入屬性

事件傳入屬性:event,它傳入了事件觸發(fā)的屬性


event.addEventListener('eventType',function(e){

    console.log(e)
}

如何阻止事件:

e.prevenDefault(): 阻止默認事件,可以在容器上阻止但不推薦

e.stopPropagation(): 阻止冒泡事件

使用原生 JS 實現(xiàn)事件委托

基礎:一個元素的父元素綁定了監(jiān)聽事件,而本身沒有綁定監(jiān)聽事件。如果該元素被點擊,也會觸發(fā)父元素的監(jiān)聽事件

讓我們舉個例子:

這是一個ul,里面有4個li

    <ul id="ul">
        <li id="li1">1</li>
        <li id="li2">2</li>
        <li id="li3">3</li>
        <li id="li4">4</li>
    </ul>

現(xiàn)在要給每個li綁定一個監(jiān)聽事件

li1.addEventListener('click', function() {})
li2.addEventListener('click', function() {})
li3.addEventListener('click', function() {})
li4.addEventListener('click', function() {})

如果執(zhí)行的函數(shù)都一樣,且li個數(shù)很多,這就顯得非常麻煩了。尤其是li個數(shù)不確定的時候,此時我們新增一個li,很顯然,新增的li并沒有綁定監(jiān)聽事件。

addButton.onclick = function(){
  var li = document.createElement('li')
  li.textContent = 'new'  
  document.querySelector('ul').appendChild(li)
}
//新增了li,但沒有自動綁定監(jiān)聽事件

但是,在此例子中,如果點擊了li,這個時候不也等于點擊了ul嗎?(參考上述基礎)

因此可以直接把點擊事件綁定在ul上

var ul = document.getElementById('ul')
ul.addEventListener('click', function() {})

那么是否就可以簡單的監(jiān)聽父元素從而達到監(jiān)聽子元素的目的呢?

這是不對的。如果給ul添加個padding。

可以看出,當點擊padding部分,也是會觸發(fā)事件的。原因是我們監(jiān)聽的是ul。

所以這種綁定法,是有bug的,這明顯不是我們想要的結(jié)果,于是我們可以給事件添加一個判斷:

判斷一下點擊的目標,如果點的是li就觸發(fā),不然就不觸發(fā)。

        ul.addEventListener('click', function(e) {
                    // 檢查事件源e.targe是否為Li
                    if (e.target && e.target.nodeName.toUpperCase == "LI") {
                          console.log("點擊成功");
                    }
                }

? 這里就要科普一下用到上面所說的事件傳入屬性,其中:

? target:觸發(fā)該事件時點擊的元素

? currentTarget:觸發(fā)該事件時監(jiān)聽的元素

那么,還有什么bug嗎?有的!

嘗試給li加個span試試:

    <ul id="ul">
        <li id="li1"><span>1</span></li>  //給第一個li加一個span
        <li id="li2">2</li>
        <li id="li3">3</li>
        <li id="li4">4</li>
    </ul>

此時發(fā)現(xiàn)點擊第一個li已經(jīng)不觸發(fā)綁定事件了。

雖然前面說道,監(jiān)聽父元素就能達到監(jiān)聽子元素的目的。

但是我們?yōu)榱诵迯?‘padding’ 的bug,添加了一個標簽判斷,導致 ‘span’ 不會觸發(fā)綁定事件

if (e.target && e.target.nodeName.toUpperCase == "LI") 

我們必須考慮,li是否還有后代元素。這時候我們就應該先判斷點擊的元素的祖先元素當中有沒有l(wèi)i,如果有l(wèi)i,那點擊的還是li。

所以,最后寫出的事件委托函數(shù)優(yōu)化如下

ul.addEventListener('click', function() {
                    let el = e.target  \\獲得實際點擊的元素
                    \\以下while判定點擊元素el的祖先元素是否有Li
                    while (el && !el.matches(selector)) {
                        el = el.parentNode
                        if (element === el) {
                            el = null
                        }
                    }
                    if (el) {
                        console.log('執(zhí)行回調(diào)函數(shù)')
                    }
                }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • (續(xù)jQuery基礎(1)) 第5章 DOM節(jié)點的復制與替換 (1)DOM拷貝clone() 克隆節(jié)點是DOM的常...
    凜0_0閱讀 1,499評論 0 8
  • 總結(jié): 鼠標事件 1.click與dbclick事件$ele.click()$ele.click(handler(...
    阿r阿r閱讀 1,714評論 2 10
  • 第1章 鼠標事件 1-1 jQuery鼠標事件之click與dbclick事件 用交互操作中,最簡單直接的操作就是...
    mo默22閱讀 1,343評論 0 6
  • 醒來,已然發(fā)現(xiàn)是9點,空間里好友發(fā)動態(tài)說??谖⒄穑瑬|北白雪飄飄,外面的世界好冷,可是為什么在室內(nèi),在被窩的自己還是...
    Mosquito_閱讀 615評論 4 3
  • 剛進入007-14班時,看到班群里每天那么多留言,我頓時懵了:每天24小時,睡覺8小時,上班加路途8小時,瑜伽康復...
    游游老師閱讀 234評論 1 7

友情鏈接更多精彩內(nèi)容