理解DOM事件處理程序和事件委托

前言

JavaScript和HTML之間的交互是通過(guò)事件實(shí)現(xiàn)的??梢杂?strong>偵聽(tīng)器(或處理程序)來(lái)預(yù)訂事件。這種模式是設(shè)計(jì)模式中的觀察者模式(Observer Pattern)。使頁(yè)面行為和視圖之間形成松散耦合。

一、基本概念:事件處理程序

響應(yīng)某個(gè)事件的函數(shù)叫做事件處理程序(或事件偵聽(tīng)器),以on開(kāi)頭。

例如:onclick、onload等。

HTML事件處理程序

<input onclick="xx"/>

缺點(diǎn):

  1. 存在時(shí)差問(wèn)題。用戶可能會(huì)在HTML元素一出現(xiàn)頁(yè)面上就觸發(fā)響應(yīng)的事件,但當(dāng)時(shí)的事件處理程序有可能尚不具備執(zhí)行條件。

    解決方案:將事件處理程序封裝在一個(gè)try-catch塊中。

  2. 這樣擴(kuò)展事件處理程序的作用域鏈在不同的瀏覽器中會(huì)導(dǎo)致不同的結(jié)果。

  3. HTML與JavaScript代碼緊密耦合。

DOM0

以這種方式添加事件處理程序會(huì)在事件流的冒泡階段被處理。

element.onlick = function () {}

優(yōu)點(diǎn):

  1. 簡(jiǎn)單
  2. 具有跨瀏覽器的優(yōu)勢(shì)

刪除通過(guò)DOM0級(jí)方法指定的事件處理程序:

element.onlick = null;

DOM1

規(guī)定如何映射基于XML的文檔結(jié)構(gòu)

沒(méi)有涉及和事件相關(guān)的,在這里不贅述

DOM2

  1. 擴(kuò)充了眾多新類(lèi)型和新接口的定義
  • DOM視圖:定義了跟蹤不同文檔視圖的接口
  • DOM事件:定義了事件和事件處理的接口
  • DOM樣式:增加了對(duì)CSS的支持
  • DOM遍歷和范圍:遍歷和操作DOM的接口
  1. 通過(guò) addEventListener() 處理指定事件處理程序
element.addEventListener('click', function () {}, false)

false:在冒泡階段觸發(fā) (大多數(shù)情況下)

true:在捕獲階段觸發(fā)

優(yōu)點(diǎn):可以添加多個(gè)事件處理程序

  1. 通過(guò) removeEventListener() 刪除事件處理程序
element.remoteEventListener('click', function () {}, false)

注:移除時(shí)傳入的參數(shù)與添加處理程序時(shí)使用的參數(shù)相同。意味著通過(guò)addEventListener()添加的匿名函數(shù)無(wú)法被移除。

DOM3

  1. 添加了更多事件類(lèi)型:
    • UI事件
    • 鼠標(biāo)事件
    • 滾輪事件
    • 鍵盤(pán)事件
    • 焦點(diǎn)事件
    • 文本事件
    • 合成事件
    • 變動(dòng)事件
    • 變動(dòng)名稱(chēng)事件
  2. 引入了以統(tǒng)一方式加載和保存文檔的方法:在DOM加載和保存模塊中定義
  3. 新增了驗(yàn)證文檔的方法:在DOM驗(yàn)證模塊中定義
  4. 支持XML1.0規(guī)范
  5. DOM3添加事件處理程序
element.addEventListener('keyup', function () {}, false)

二、DOM事件模型

  • 捕獲
  • 冒泡

三、DOM事件流

事件流描述的是從頁(yè)面中接收事件的順序。即瀏覽器在當(dāng)前頁(yè)面,用戶點(diǎn)擊了鼠標(biāo)左鍵,是怎么傳到頁(yè)面上的,又是如何響應(yīng)的呢?

DOM2級(jí)事件規(guī)定的事件流分三個(gè)階段:

  1. 捕獲階段:從上向下
  2. 目標(biāo)階段:事件通過(guò)捕獲到達(dá)目標(biāo)元素
  3. 冒泡階段:從目標(biāo)元素向上傳到window對(duì)象

四、描述DOM事件捕獲/冒泡的具體流程

事件捕獲會(huì)按照如下順序傳播:

  1. window
  2. document
  3. <html>
  4. <body>
  5. div 父級(jí)元素->子級(jí)元素
  6. 目標(biāo)元素

注:冒泡流程與之相反

五、事件對(duì)象的常見(jiàn)應(yīng)用

事件對(duì)象event包含所有與事件相關(guān)的信息,包括:導(dǎo)致事件的元素、事件的類(lèi)型以及其他信息。

5.1 DOM中的事件對(duì)象有關(guān)的屬性和方法

  1. event.preventDefault()

    類(lèi)型:Function

    說(shuō)明:阻止特定事件的默認(rèn)行為

    例子:給a標(biāo)簽綁定click事件,在響應(yīng)函數(shù)中設(shè)置該方法會(huì)阻止鏈接默認(rèn)跳轉(zhuǎn)的行為

  2. event.stopPropagation()

    類(lèi)型:Function

    說(shuō)明:立即阻止事件進(jìn)一步冒泡

    例子:父級(jí)元素和子元素分別綁定一個(gè)事件。我們希望子元素做一件事,父元素做一件事,即要使子元素單擊,父元素不響應(yīng)。

  3. event.stopImmediatePropagation()

    類(lèi)型:Function

    說(shuō)明:阻止事件進(jìn)一步冒泡,同時(shí)阻止任何事件處理程序被調(diào)用。事件響應(yīng)優(yōu)先級(jí)(DOM3級(jí)事件中新增)

    例子:按鈕綁定了兩個(gè)click事件,通過(guò)優(yōu)先級(jí)的方式,第一個(gè)響應(yīng)函數(shù)是a,第二個(gè)是b。以此注冊(cè)a,b兩個(gè)click事件。想a點(diǎn)擊時(shí)不要響應(yīng)b了,即在a響應(yīng)函數(shù)中定義這個(gè)方法,可以成功阻止b的執(zhí)行。

  4. event.currentTarget/event.target(重點(diǎn))

    類(lèi)型:Element

    常見(jiàn)問(wèn)題:一個(gè)for循環(huán),給DOM注冊(cè)N多事件,即一個(gè)父元素有很多子元素,不想讓一個(gè)for循環(huán)綁定click事件到每一個(gè)子元素,該如何優(yōu)化?jQuery事件委托

    事件委托(事件代理):子元素的事件代理都轉(zhuǎn)移到父級(jí)元素,綁定一次事件就可以了。響應(yīng)函數(shù)中要判斷當(dāng)前到底哪個(gè)元素被點(diǎn)擊了。

    target:事件真正的目標(biāo),表示當(dāng)前被點(diǎn)擊的元素 (IE:SourceElement)

    currentTarget:當(dāng)前正在處理事件的元素

    注:在事件處理程序內(nèi)部,對(duì)象this始終等于currentTarget的值,而target則只包含事件的實(shí)際目標(biāo)。

5.2 事件委托

5.2.1 簡(jiǎn)述事件委托

關(guān)鍵:檢查事件是否來(lái)自你所預(yù)期的元素。

優(yōu)點(diǎn):

  • 可以大量節(jié)省內(nèi)存占用,減少事件注冊(cè)
  • 可以實(shí)現(xiàn)當(dāng)新增子對(duì)象時(shí)無(wú)需再次修改其綁定事件,對(duì)于動(dòng)態(tài)內(nèi)容部分尤為合適

5.2.2 代碼實(shí)現(xiàn)

基于第八部分做的封裝實(shí)現(xiàn)

var list = document.getElementById("myLinks");
EventUtil.addHander(list, "click", function (event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);

    switch (target.id) {
        case "sayHello":
            document.title = "Change the document's title to Hello World";
            break;
        case "doSearch":
            location.;
            break;
        case "sayGoodbye":
            alert("Bye~");
            break;
    }
});

六、自定義事件(模擬事件)

場(chǎng)景:有一個(gè)button,增加事件,在別的地方不用回調(diào)處理。

var eve=new Event('custome');
ev.addEventListener('custome', function(){
    console.log('custome');
});
ev.dispatchEvent(eve);

CustomEvent :除了可以指定事件名,還可以指定參數(shù)

Event:只能指定事件名

七、捕獲流程/自定義事件

<div id="ev">
    <style>
        #ev{
            width: 300px;
            height: 100px;
            background: yellow;
            color: #fff;
            text-align: center;
            line-height: 100px;
        }
    </style>
    目標(biāo)元素
    <script type="text/javascript">
        var ev=document.getElementById('ev');
        //給window注冊(cè)捕獲事件
        window.addEventListener('click',function(){
            console.log('window capture');
        },true);
       
        document.addEventListener('click',function(){
            console.log('document capture');
        },true);
        
        document.documentElement.addEventListener('click',function(){
            console.log('body capture');      
        },true);
        
        document.body.addEventListener('click',function(){
           console.log('body capture'); 
        },true);
        
        ev.addEventListener('click',function(){
            console.log('ev capture');
        },true);  
        
        //自定義事件
        var eve=new Event('test');
        ev.addEventListener('test',function(){
            console.log('test dispatch');
        })       
        //延時(shí)器模擬某個(gè)按鈕的動(dòng)作
        setTimeout(function(){
            ev.dispatchEvent(eve);  
        },1000);
    </script>
</div>

注:

  1. 響應(yīng)的順序和定義的順序無(wú)關(guān)

  2. 把true改成false 則為冒泡

八、手寫(xiě)DOM事件處理跨瀏覽器(兼容IE)封裝

var eventUtil = { /*DOM2級(jí)事件處理程序*/
    //給一個(gè)元素添加事件
    addHander: function (element, type, hander) {
        if (element.addEventListener) {
            element.addEventListener(type, hander, false); /*非IE*/
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, hander); /*IE*/
        } else {
            element["on" + type] = hander;
        }
    },
    //給一個(gè)元素刪除事件
    removeHander: function (element, type, hander) {
        if (element.removeEventListener) {
            element.removeEventListener(type, hander, false); /*非IE*/
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, hander); /*IE*/
        } else {
            element["on" + type] = null;
        }
    },
    //獲取兼容所有瀏覽器的一個(gè)對(duì)象
    getEvent: function (event) {
        return event ? event : window.event;
    },
    //獲取事件類(lèi)型
    getType: function (event) { //此項(xiàng)不存在瀏覽器兼容問(wèn)題
        return event.type;
    },
    //事件來(lái)自哪個(gè)元素
    getElement: function (event) {
        return event.target || event.srcElement;
    },
    //阻止事件默認(rèn)行為
    preventDefault: function (event) {
        if (event.preventDefault) {
            event.preventDefault(); /*非IE*/
        } else {
            event.returnValue = false; /*IE*/
        }
    },
    //阻止事件冒泡
    stopPropagation: function (event) {
        if (event.stopPropagation) {
            event.stopPropagation(); /*非IE*/
        } else {
            event.cancelBubble = true; /*IE*/
        }
    }
}

以上,如有錯(cuò)誤希望大家指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • ??JavaScript 與 HTML 之間的交互是通過(guò)事件實(shí)現(xiàn)的。 ??事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,686評(píng)論 1 11
  • 事件流 JavaScript與HTML之間的交互是通過(guò)事件實(shí)現(xiàn)的。事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特定的交互...
    DHFE閱讀 910評(píng)論 0 3
  • JavaScript 程序采用了異步事件驅(qū)動(dòng)編程模型。在這種程序設(shè)計(jì)風(fēng)格下,當(dāng)文檔、瀏覽器、元素或與之相關(guān)的對(duì)象發(fā)...
    劼哥stone閱讀 1,331評(píng)論 3 11
  • 一、事件流 1.1 事件流 事件流:從頁(yè)面中接受事件的順序 事件冒泡:即事件開(kāi)始時(shí)由最具體的元素(文檔中嵌套層次最...
    范小飯_閱讀 1,163評(píng)論 1 9
  • JavaScript 與 HTML 之間的交互是通過(guò)事件實(shí)現(xiàn)的。事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特定的交互瞬...
    LemonnYan閱讀 737評(píng)論 0 4

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