溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

JavaScript中如何實(shí)現(xiàn)事件冒泡與時(shí)間捕獲

發(fā)布時(shí)間:2021-12-24 09:38:11 來(lái)源:億速云 閱讀:109 作者:小新 欄目:web開(kāi)發(fā)

小編給大家分享一下JavaScript中如何實(shí)現(xiàn)事件冒泡與時(shí)間捕獲,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!

一、事件冒泡與事件捕獲

當(dāng)我們?cè)赪eb頁(yè)面單擊某一個(gè)元素的時(shí)候,比如某個(gè)p元素。仔細(xì)想想,我們單擊的不僅僅是這一個(gè)p元素,一同被單擊的還有以該p為圓心的同心圓元素,比如元素的父,外層body、body的父元素html還有外層的document。事件在這些嵌套的元素之間的傳播稱(chēng)為事件流。

  • 1、事件冒泡

  • 2、事件捕獲

1、事件冒泡

IE的事件流稱(chēng)為事件冒泡,事件從最具體的元素開(kāi)始,逐級(jí)向上傳播。我們使用DOM0添加的事件處理程序就是在事件冒泡階段被處理的。例如:

<html>
  <head>
    <script type="text/javascript">

      window.onload = bubblingHandle;      function bubblingHandle() {
          //內(nèi)層p處理程序
          document.getElementById("inner").onmousedown     = function() {
              alert("inner p");
          }          //外層p處理程序
          document.getElementById("outer").onmousedown = function() {
              alert("outerp");
          }

          document.onmousedown = function() {
              alert("document");
          }
      } 
      -->    </script>
  </head>
  <body>
    <p id="outer" style="background-color:black; padding: 15px;">
        <p id="inner" style="background-color:white; padding: 5px;"></p>
    </p>
  </body></html>

當(dāng)點(diǎn)擊內(nèi)層的白色p時(shí),會(huì)依次顯示:

inner p
outer p
document

事件捕獲

網(wǎng)景提出的事件流稱(chēng)為事件捕獲,其與IE幾乎相反。事件首先由最不具體的元素接收,然后逐級(jí)向具體節(jié)點(diǎn)傳播。

二、DOM0級(jí)事件處理

事件,由WEB頁(yè)面中發(fā)生的一些特定行為觸發(fā)。比如在某個(gè)頁(yè)面元素上按下鼠標(biāo)左鍵,按下鍵盤(pán)某個(gè)按鍵,某對(duì)象獲得或丟失焦點(diǎn)時(shí)均會(huì)觸發(fā)對(duì)應(yīng)的事件。JavaScript和HTML的交互就是通過(guò)事件來(lái)實(shí)現(xiàn)的。我們使用事件偵聽(tīng)器對(duì)事件進(jìn)行“注冊(cè)”,事件發(fā)生時(shí)便執(zhí)行相應(yīng)的代碼。

DOM0級(jí)事件處理程序以其簡(jiǎn)單、跨瀏覽器支持的特點(diǎn),至今仍為所有瀏覽器支持。

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

  • 事件處理程序中的this

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

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

通過(guò)DOM0級(jí)方法指定事件處理程序方法很簡(jiǎn)單,首先取得一個(gè)要操作元素的引用,然后接將一個(gè)函數(shù)賦值給該元素的對(duì)應(yīng)事件處理程序?qū)傩?/code>。(每個(gè)元素包括window和document都擁有自己的事件處理程序?qū)傩?/code>。)注意,這種方法添加的事件處理程序?qū)⒃谑录鞯拿芭蓦A段被處理。

有關(guān)事件處理程序?qū)傩?,有以下幾點(diǎn)需要說(shuō)明:

1、事件處理程序?qū)傩匀啃?xiě),以”on”開(kāi)頭,后面跟事件類(lèi)型:

onclick  //單擊鼠標(biāo)
onload  //圖像或頁(yè)面載入完成
onmouseover  //將鼠標(biāo)移動(dòng)到某元素上面
onmousemove  //移動(dòng)鼠標(biāo)
onfocus  //對(duì)象獲得焦點(diǎn)

2、每個(gè)元素如img、a、input、form包括window和document都擁有自己的事件處理程序?qū)傩浴H纾?/p>

document.getElementById("btn1").onclick  //btn1上單擊鼠標(biāo)
document.getElementById("img1").onmouseover  //鼠標(biāo)移動(dòng)到img1
document.getElementById("img1").onmerror  //img1圖像無(wú)法載入

接下來(lái),給事件處理程序?qū)傩再x值即可完成事件處理程序方法的指定。例如,當(dāng)鼠標(biāo)移動(dòng)到”img1”上時(shí),彈出對(duì)話(huà)框”This is a nice pic!”:

var pic1 = document.getElementById("img1");
pic1.onmouseover = function() {
    alert("This is a nice pic!");
};

特別注意:如果以上代碼處于文檔的底部,在頁(yè)面剛剛加載時(shí),我們將鼠標(biāo)移動(dòng)到img1上面。有可能由于代碼尚未執(zhí)行,不會(huì)彈出我們?cè)O(shè)定的對(duì)話(huà)框!如今,這個(gè)延遲已經(jīng)十分短暫。

事件處理程序中的this

通過(guò)DOM0級(jí)方法指定的事件處理程序,屬于元素方法。因此,我們?cè)谑录幚沓绦蛑械膖his引用的是該元素!通過(guò)以下例子來(lái)說(shuō)明:

<input id="btn1" type="button" value="Click Me" />
...//省略
<script type="text/javascript">
  <!--
  var btn1 = document.getElementById("btn1");
  btn1.onclick = function() {
      alert(this.id + "\n" + this.type + "\n" + this.value);
  };
  -->
</script>

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

要?jiǎng)h除事件處理程序,只需要將對(duì)應(yīng)的事件處理程序?qū)傩栽O(shè)置為null即可:

pic1.onmouseover = null;

三、DOM2級(jí)事件處理

1、addEventListener與removeEventListener

目前,幾乎所有的瀏覽器都支持DOM0事件模型,但鼓勵(lì)開(kāi)發(fā)人員使用新的DOM2模型。DOM2模型與DOM0有兩個(gè)顯著區(qū)別:

  • 1、DOM2不依賴(lài)事件處理程序?qū)傩?/p>

  • 2、可以同時(shí)對(duì)對(duì)象的同一事件注冊(cè)多個(gè)處理程序,它們按照注冊(cè)順序依次執(zhí)行。

DOM2定義了2個(gè)方法:

addEventListener()  //指定事件處理程序
removeEventListener()  //刪除事件處理程序

所有DOM節(jié)點(diǎn)有包含這兩個(gè)方法,這兩個(gè)方法用法如下,它們都接收3個(gè)參數(shù),第1個(gè)為要處理事件名(不含on),第2個(gè)事件處理函數(shù),第3個(gè)布爾變量:

例如我們?yōu)榘粹obtn1的單擊事件添加2個(gè)事件處理程序,事件處理程序在事件冒泡階段被處理:

<input id="btn1" type="button" value="Click Me" />
...
<script type="text/javascript">
  <!--
  var btn1 = document.getElementById("btn1");
  var handle1 = function() {
      alert("handle1!");
  }
  var handle2 = function() {
      alert("handle2!");
  }
  btn1.addEventListener("click", handle1, false);
  btn1.addEventListener("click", handle2, false);
  -->
</script>

當(dāng)單擊btn1按鈕時(shí),會(huì)依次彈出對(duì)話(huà)框:

handle1!
handle2!

我們可以用removeEventListener()方法來(lái)刪除我們剛才指定的事件處理程序,注意參數(shù)要保持一致:

btn1.removeEventListener("click", handle2, false);

此時(shí)單擊btn1按鈕,只會(huì)顯示handle1!。

要特別注意的是,如果我們使用匿名函數(shù)指定事件處理程序,便無(wú)法使用removeEventListener()方法刪除事件處理程序:

 btn1.addEventListener("click", function(){
     alert("click!");
 }, false);

 btn1.removeEventListener("click", function(){
     alert("click!");
 }, false);  //無(wú)法取消!

這樣是無(wú)法取消以上指定的事件處理程序的!因?yàn)樯厦鎍ddEventListener和removeEventListener中的2個(gè)事件處理函數(shù)雖然代碼相同,實(shí)質(zhì)上是2個(gè)不同的函數(shù)引用。

另外,強(qiáng)調(diào)一點(diǎn),以上兩個(gè)函數(shù)的第一個(gè)參數(shù)(要處理的事件名)是沒(méi)有on前綴的。這一點(diǎn)和IE不同,后面會(huì)說(shuō)明。

tips: IE9, Firefox, Safari, Chrome以及Opera均支持DOM2級(jí)事件處理程序。

DOM2事件處理程序中的this

DOM2事件處理程序和DOM0相同,它們的this都在其依附的元素作用域中運(yùn)行。this的指代參考DOM0的示例。這里之所以要特別指出DOM2的this,是為了和IE事件處理程序進(jìn)行區(qū)分。IE中事件處理程序this與事件指定方式有關(guān)。

四、IE事件處理程序及跨瀏覽器支持

attachEvent()與detachEvent()

IE并沒(méi)有提供對(duì)W3C事件模型的支持,其實(shí)現(xiàn)了2個(gè)和DOM2模型類(lèi)似的方法:

attachEvent()
detachEvent()

這兩個(gè)方法只接收2個(gè)參數(shù):事件名稱(chēng)以及事件處理函數(shù)。由于IE8及更早版本只支持事件冒泡,這兩個(gè)方法添加的事件處理程序會(huì)在事件冒泡階段被執(zhí)行。

和DOM2不同的是:

  • 1、IE事件處理方法運(yùn)行作用域?yàn)槿肿饔糜?,this指代window;

  • 2、第一個(gè)參數(shù)事件名以on為前綴;

  • 3、當(dāng)為同一對(duì)象的相同事件指定多個(gè)處理程序時(shí),執(zhí)行順序和DOM2相反,IE中以添加它們的相反順序執(zhí)行。

例如:

<input id="btn1" type="button" value="Click Me" />
...
<script type="text/javascript">
  <!--
  var btn1 = document.getElementById("btn1");
  var handle1 = function() {
      alert("handle1!" + "\n" + (this === window));
  };
  var handle2 = function() {
      alert("handle2!"+ "\n" + (this === window));
  };
  btn1.attachEvent("onclick", handle1);
  btn1.attachEvent("onclick", handle2);
  -->
</script>

執(zhí)行結(jié)果:

handle2!
true

handle1!
true

跨瀏覽器支持

雖然可以使用屏蔽瀏覽器差異的JS庫(kù),實(shí)際上,我們自己編寫(xiě)一個(gè)跨瀏覽器兼容的事件處理代碼并不是一件困難的事情,同時(shí)更有利于我們對(duì)原生JavaScript的學(xué)習(xí)理解。我們使用一個(gè)習(xí)慣上稱(chēng)為EventUtil的對(duì)象來(lái)進(jìn)行跨瀏覽器事件處理:

var EventUtil = {
    addEventHandler : function(element, eventType,        handler) {
        if(element.addEventListener){
            element.addEventListener(eventType, handler, flase);
        } else if(element.attachEvent) {
            element.attachEvent("on" + eventType, handler);
        } else {
            element["on" + eventType] = handler;
        }
    },

    removeEventHandler : function(element, eventType, handler) {
        if(element.aremoveEventListener){
            element.addEventListener(eventType, handler, flase);
        } else if(element.detachEvent) {
            element.attachEvent("on" + eventType, handler);
        } else {
            element["on" + eventType] = null;
        }
    }
}

為了保證事件處理代碼能夠在大多數(shù)瀏覽器中一致地運(yùn)行,我們這里只關(guān)注冒泡階段。以上代碼使用瀏覽器能力檢測(cè),首先檢測(cè)是否支持DOM2級(jí)方法addEventListener和removeEventListener,如果支持則使用該方法;如果不支持該方法,檢測(cè)是否是IE8級(jí)更早版本的attachEvent或detachEvent方法,若支持則使用該方法;如果對(duì)以上2種方法都不支持,則使用DOM0級(jí)方法。要注意,DOM0級(jí)對(duì)每個(gè)事件只能指定一個(gè)事件處理程序。

以上對(duì)象使用示例如下:

var btn1 = document.getElementById("btn1");var handle1 = function() {
    alert("handle1!" + "\n" + (this === window));
};var handle2 = function() {
    alert("handle2!"+ "\n" + (this === window));
};
EventUtil.addEventHandler(btn1, "click", handler1);
EventUtil.addEventHandler(btn1, "click", handler2);

EventUtil.removeEventHandler(btn1, "click", handler2);

五、事件對(duì)象

在觸發(fā)某個(gè)事件時(shí),會(huì)產(chǎn)生一個(gè)event對(duì)象。該對(duì)象中包含與事件有關(guān)的信息。例如觸發(fā)事件的元素、事件的類(lèi)型、與特定事件相關(guān)的如鼠標(biāo)位置信息等。

  • 1、DOM事件對(duì)象

  • 2、IE事件對(duì)象與跨瀏覽器事件對(duì)象

1、DOM事件對(duì)象

不論使用DOM0級(jí)還是DOM2級(jí)方法指定事件處理程序,事件觸發(fā)時(shí)都會(huì)自動(dòng)將一個(gè)event對(duì)象傳入事件處理程序,例如:

var btn1 = document.getElementById("btn1");

btn1.onmouseover = function(evnt) {
    alert(evnt.type);
}var handle = function(evnt) {
    alert(evnt.type);
};
btn1.addEventListener("click", handle, false);

以上是一個(gè)簡(jiǎn)單的event對(duì)象的示例。event對(duì)象中的type屬性是一個(gè)只讀字符串屬性,其中包含著事件的類(lèi)型。例如我們上例中的click和onmouseover。event對(duì)象中包含有大量的有關(guān)事件的屬性和方法(例如event.stopPropagation()方法可用于停止事件在捕獲或者冒泡階段的繼續(xù)傳播,preventDefault()方法會(huì)取消阻止事件的行)在此就不一一列舉了。其中常用的如下:

屬性/方法值類(lèi)型讀寫(xiě)描述
currentTargetElementreadonly事件處理程序當(dāng)前正在處理的元素
targetElementreadonly事件的目標(biāo)
typeStringreadonly觸發(fā)事件的類(lèi)型
preventDefaultFunctionreadonly取消事件默認(rèn)行為,如鏈接的默認(rèn)行為就是被單擊時(shí)跳轉(zhuǎn)到href指定的url
stopPropagationFunctionreadonly取消事件進(jìn)一步冒泡或捕獲

2、IE事件對(duì)象與跨瀏覽器事件對(duì)象

IE事件對(duì)象

在IE中,當(dāng)使用DOM0級(jí)指定事件處理程序時(shí),event對(duì)象被認(rèn)為是window的一個(gè)屬性,例如獲取鼠標(biāo)點(diǎn)擊坐標(biāo)的代碼:

var mouseLoc = function() {
    var loc = "x: " + window.event.screenX + "\n" +              "y: " + window.event.screenY;
    alert(loc);
};

當(dāng)使用attachEvent()方法指定事件處理程序時(shí),event對(duì)象會(huì)被作為參數(shù)傳入事件處理程序,我們將以上的代碼重寫(xiě):

var mouseLoc = function(event) {
    var loc = "x: " + event.screenX + "\n" +              "y: " + event.screenY;
    alert(loc);
};
btn1.attachEvent("onclick",  mouseLoc);

IE中event對(duì)象的相關(guān)屬性方法:

屬性/方法值類(lèi)型讀寫(xiě)描述
cancelBubbleBooleanread/write默認(rèn)為false,置為true時(shí)取消事件冒泡(同DOM中stopPropagation)
returnValueBooleanread/write默認(rèn)為true,設(shè)為false取消事件默認(rèn)行為(同DOM中preventDefault)
srcElementElementreadonly事件目標(biāo)
typeStringreadonly事件類(lèi)型

跨瀏覽器事件對(duì)象

解決跨瀏覽器問(wèn)題的思路是一貫的,我們可以對(duì)瀏覽器進(jìn)行能力檢測(cè),這里我們對(duì)上面的EventUtil對(duì)象進(jìn)行擴(kuò)展,對(duì)我們學(xué)習(xí)原生JS,這是一個(gè)很漂亮的對(duì)象:

var EventUtil = {
    addEventHandler : function(element, eventType, handler) {
        if(element.addEventListener){
            element.addEventListener(eventType, handler, flase);
        } else if(element.attachEvent) {
            element.attachEvent("on" + eventType, handler);
        } else {
            element["on" + eventType] = handler;
        }
    },

    removeEventHandler : function(element, eventType, handler) {
        if(element.aremoveEventListener){
            element.addEventListener(eventType, handler, flase);
        } else if(element.detachEvent) {
            element.attachEvent("on" + eventType, handler);
        } else {
            element["on" + eventType] = null;
        }
    },

    getEvent: function (event) {
        return event ? event : window.event;
    },

    getTarget: function (event) {
        return event.target || event.srcElement;
    },

    preventDefault: function (event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },

    stopPropagation: function (event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubbles = true;
        }
    },

    getRelatedTarget: function (event) {
        if (event.relatedTarger) {            return event.relatedTarget;
        } else if (event.toElement) {            return event.toElement;
        } else if (event.fromElement) {            return event.fromElement;
        } else { return null; }
    }
}

看完了這篇文章,相信你對(duì)“JavaScript中如何實(shí)現(xiàn)事件冒泡與時(shí)間捕獲”有了一定的了解,如果想了解更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI