JavaScript 事件

DOM 級(jí)別

此文中涉及到DOM0DOM2級(jí)事件,故先行解釋

文檔對(duì)象模型(DOM)是一個(gè)平臺(tái),一個(gè)中立于語言的應(yīng)用程序編程接口(API),允許程序訪問并更改文檔的內(nèi)容、結(jié)構(gòu)和樣式。

DOM被分為不同的部分(核心、XMLHTML)和級(jí)別(DOM Level 1/2/3
DOM分類

  • DOM核心:針對(duì)任何結(jié)構(gòu)化文檔的標(biāo)準(zhǔn)模型
  • DOM XML:只針對(duì)XML文檔的標(biāo)準(zhǔn)模型
  • DOM HTML:只針對(duì)HTML文檔的標(biāo)準(zhǔn)模型

DOM級(jí)別是描述DOM對(duì)象,方法和行為的規(guī)范集合?;谙惹凹?jí)別構(gòu)建的DOM規(guī)范的更高級(jí)別。變化發(fā)生在兩個(gè)方面:

  1. 增加一個(gè)全新的規(guī)格類別(例如,3級(jí)添加了“驗(yàn)證”和“加載和保存”規(guī)范,這在2級(jí)中不存在)
  2. 修改現(xiàn)有規(guī)范類別(例如更新“核心”規(guī)范)

DOM級(jí)別

  • DOM0級(jí)別:不是W3C規(guī)范。而僅僅是對(duì)在Netscape Navigator 3.0Microsoft Internet Explorer 3.0中的等價(jià)功能性的一種定義。DOM1級(jí)別建立于此功能性之上
  • DOM1級(jí)別:專注于HTMLXML文檔模型。它含有文檔導(dǎo)航和處理功能。于1998年10月1日稱為W3C推薦標(biāo)準(zhǔn)
  • DOM2級(jí)別:對(duì)DOM1級(jí)別添加了樣式表對(duì)象模型,并定義了操作附于文檔至上的樣式信息的功能性。同時(shí)還定義了一個(gè)事件模型,并提供了對(duì)XML命名空間的支持。作為一項(xiàng)W3C推薦標(biāo)準(zhǔn),于2000年11月13日發(fā)布
  • DOM3級(jí)別:規(guī)定了內(nèi)容模型(DTDSchemas)和文檔驗(yàn)證。同時(shí)規(guī)定了文檔加載和保存、文檔查看、文檔格式化和關(guān)鍵事件

注:DOM1級(jí)標(biāo)準(zhǔn)中未定義事件相關(guān)的內(nèi)容,故沒有所謂的DOM1級(jí)別事件模型


事件

JavaScriptHTML的交互是通過事件實(shí)現(xiàn)的。事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特定的交互瞬間??梢允褂脗陕犉鳎ɑ蛱幚沓绦颍﹣眍A(yù)定事件,以便事件發(fā)生時(shí)執(zhí)行相應(yīng)的代碼。

網(wǎng)頁中的每個(gè)元素都可以產(chǎn)生某些可以觸發(fā)JavaScript函數(shù)的事件。比如說,我們可以在用戶點(diǎn)擊某按鈕時(shí)產(chǎn)生一個(gè)事件來觸發(fā)某個(gè)函數(shù)。事件在HTML中定義

事件舉例:

  • 鼠標(biāo)點(diǎn)擊
  • 頁面或圖像載入
  • 鼠標(biāo)懸浮于頁面的某個(gè)熱點(diǎn)至上
  • 在表單中選取輸入框
  • 確認(rèn)表單
  • 鍵盤按鍵

事件流

事件流描述的是從頁面中接受事件的順序

如圖2.1,目標(biāo)節(jié)點(diǎn)是<td>,當(dāng)點(diǎn)擊<td>事件的傳播如下所示:
[圖片上傳失敗...(image-5bef-1534760955972)]

事件冒泡

事件開始時(shí)由最具體的元素(文檔中嵌套層次最深的那個(gè)節(jié)點(diǎn))接收,然后逐級(jí)向上傳播到較為不具體的節(jié)點(diǎn)(文檔)。
所由的現(xiàn)在瀏覽器都支持事件冒泡,但在具體是線上還是有一些差別。IE5.5及更在版本中的事件冒泡會(huì)跳過<html>元素(從<body>直接跳到document)。IE9Firefox、ChromeSafari則將事件一直冒泡到window對(duì)象。
即上圖中的3階段 ,事件傳播順序:<td> - <tr> - <tbody> - <table> - <body> - <html> - <document> - <window>

事件捕獲

不太具體的節(jié)點(diǎn)更早接收事件,而最具體的元素最后接收事件,與事件冒泡相反
即上圖中的1階段 ,事件傳播順序:<window> - <document> - <html> - <body> - <table> - <tbody> - <tr> - <td>

DOM 事件流

DOM2級(jí)事件”規(guī)定的事件流包括三個(gè)階段:事件捕獲階段、處于目標(biāo)階段和事件冒泡階段。首先發(fā)生的事件捕獲,為截獲事件提供了機(jī)會(huì)。然后是實(shí)際的目標(biāo)接收到事件。最后一個(gè)階段是冒泡階段,可以在這個(gè)階段對(duì)事件做出響應(yīng)。
IE9、Opera、Firefox、ChromeSafari都支持DOM事件流;IE8及更早版本不支持DOM事件流。
即上圖中的1,2,3個(gè)階段


事件處理程序

事件就是用戶或?yàn)g覽器自身執(zhí)行的某種動(dòng)作。諸如clickloadmouseover,都是事件的名字。而響應(yīng)某個(gè)事件的函數(shù)就叫做事件處理程序(或事件偵聽器)。事件處理程序的名字以on開頭,因此click事件的事件處理程序就是onclick,load事件的事件處理程序就是onload。為事件指定處理程序的方式有好幾種。

HTML事件處理程序

元素支持的每個(gè)元素都可以使用一個(gè)相應(yīng)事件處理程序同名的HTML屬性指定。這個(gè)屬性的值應(yīng)該是可以執(zhí)行的JavaScript代碼,我們可以為一個(gè)button添加click事件處理程序

<input type="button" value="Click Here" onclick="alert('Clicked');">

HTML事件處理程序中可以包含要執(zhí)行的具體動(dòng)作,也可以調(diào)用在頁面其他地方定義的腳本,剛才的例子可以寫成如下形式:

<input type="button" value="Click Here" onclick="showMessage();">

HTML中指定事件處理程序書寫很方便,但有兩個(gè)缺點(diǎn):

  1. 存在時(shí)差問題。因?yàn)橛脩艨赡軙?huì)在HTML元素一出現(xiàn)在頁面上就觸發(fā)相應(yīng)的事件,但當(dāng)時(shí)的事件處理程序有可能尚不具備執(zhí)行條件。
    以上面的例子說明,假設(shè)showMessage()函數(shù)是在按鈕下方、頁面的最底部定義的。如果用戶在頁面解析showMessage()函數(shù)之前就單擊了按鈕,就會(huì)引發(fā)錯(cuò)誤。為此,很多HTML事件處理程序都會(huì)被封裝在一個(gè)try-catch塊中,以便錯(cuò)誤不會(huì)浮出水面,如下面的例子所示:
<input type="button" value="Click Here" onclick="try{showMessage();}catch(ex){}">

這樣,如果在showMessage()函數(shù)有定義之前單機(jī)了按鈕,用戶將不會(huì)看到JavaScript錯(cuò)誤,因?yàn)樵跒g覽器有機(jī)會(huì)處理錯(cuò)誤之前,錯(cuò)誤就被捕獲了。

  1. HTML代碼和JavaScript代碼緊密耦合。如果要更換事件處理程序,就要改動(dòng)這兩個(gè)地方:HTML代碼和JavaScript代碼。

DOM0 級(jí)事件處理程序

每個(gè)元素(包括windowdocument)都有自己的事件處理程序?qū)傩?,這些屬性全部小寫,例如onclick。將這種屬性的值設(shè)置為一個(gè)函數(shù),就可以指定事件處理程序。
使用DOM0 級(jí)方法指定的事件處理程序被認(rèn)為是元素的方法。因此,這時(shí)候的事件處理程序是在元素的作用域中進(jìn)行;換句話說,程序中的this引用當(dāng)前元素。例如:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
  alert(this.id); // "myBtn"
}

也可以刪除通過DOM0 級(jí)方法指定的事件處理程序,只要像下面這樣將事件處理程序?qū)傩缘闹翟O(shè)為null即可:

btn.onclick = null; // 刪除事件處理程序

將事件處理程序設(shè)置為null后,再單擊按鈕將不會(huì)有任何動(dòng)作發(fā)生。
這種做法的缺點(diǎn):一次只能定義一個(gè)事件處理程序,不能同時(shí)定義多個(gè)事件處理程序,后定義的事件處理程序會(huì)覆蓋前面定義的事件處理程序。

DOM2 級(jí)事件處理程序

DOM2 級(jí)事件定義了兩個(gè)方法,用于處理指定和刪除事件處理程序的操作:addEventListener()removeEventListener()。所有DOM節(jié)點(diǎn)中都包含這兩個(gè)方法,并且它們都接受3個(gè)參數(shù):要處理的事件名、作為事件處理程序的函數(shù)和一個(gè)布爾值。最后這個(gè)布爾值參數(shù)如果是true,表示再捕獲階段調(diào)用事件處理程序;如果是false,表示在冒泡階段調(diào)用事件處理程序;不寫的情況下默認(rèn)為false。
在按鈕上為click事件添加事件處理程序,如下:

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
  alert(this.id);
}, false);

DOM0 級(jí)方法一樣,這里添加的事件處理程序也是在其依附的元素的作用域中運(yùn)行。使用DOM2 級(jí)方法添加事件處理程序的主要好處是可以添加多個(gè)事件處理程序。如下所示:

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
  alert(this.id);
}, false);
btn.addEventListener("click", function(){
  alert("Hello World");
}, false);

這里為按鈕添加了兩個(gè)事件處理程序。這兩個(gè)事件處理程序會(huì)按照添加它們的順序觸發(fā),因此首先會(huì)顯示元素的ID,其次會(huì)顯示“Hello World”消息。
通過addEventListener()添加的事件處理程序只能使用removeEventListener()來移除;移除時(shí)傳入的參數(shù)與添加處理程序時(shí)使用的參數(shù)相同。這也意味著通過addEventListener()添加的匿名函數(shù)將無法移除。因?yàn)槟涿瘮?shù)體雖然方法體一樣,但是句柄卻不同是,是完全不同的函數(shù),所以應(yīng)寫成:

var btn = document.getElementById("myBtn");
var handler = function(){
  alert(this.id);
};
btn.addEventListener("click", handler, false);
btn.addEventListener("click", handler, false);

IE 事件處理程序

IE 實(shí)現(xiàn)了與DOM中類似的兩個(gè)方法:attachEvent()detachEvent()。這兩個(gè)方法接受相同的兩個(gè)參數(shù):事件處理程序名稱與事件處理程序函數(shù)。由于IE8及更早版本只支持事件冒泡,所以通過attachEvent()添加的事件處理程序都會(huì)被添加到冒泡階段。
使用attachEvent()添加的事件可以通過detachEvent()來移除,條件是必須提供相同的參數(shù)。與DOM方法一樣,這也意味著添加的匿名將不能被移除。不過,只要能夠?qū)⑾嗤瘮?shù)的引用傳給detachEvent(),就可以移除相應(yīng)的事件處理程序。
使用attachEvent()為按鈕添加一個(gè)事件處理程序,如下所示:

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
  alert("Clicked");
});

attachEvent()addEventListener()的區(qū)別:

  1. 參數(shù)個(gè)數(shù)不同。attachEvent()的有兩個(gè)參數(shù),添加的事件只能發(fā)生在冒泡階段;而addEventListener()有三個(gè)參數(shù),都最后一個(gè)參數(shù)決定添加的事件處理程序是在冒泡階段還是捕獲階段(一般為了兼容瀏覽器都設(shè)置為冒泡階段)

  2. 第一個(gè)參數(shù)意義不同。attachEvent()的第一個(gè)參數(shù)是事件處理程序名稱,例如onclick;而addEventListener()方法中第一個(gè)參數(shù)是事件處理類型,例如click

3.事件處理程序的作用域不同。在使用DOM2級(jí)方法的情況下,事件處理程序會(huì)在其所屬元素的作用域內(nèi)運(yùn)行;在使用attachEvent()方法的情況下,事件處理程序會(huì)在全局作用域中運(yùn)行,因此this等于window。如下:

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
  alert(this === window); // true
});
  1. 為一個(gè)事件添加多個(gè)事件處理程序時(shí),執(zhí)行順序不同。與addEventListener()類似,attachEvent()也可以為一個(gè)元素添加多個(gè)事件處理程序,不過,這些事件處理程序不是以添加它們的順序執(zhí)行的,而是以相反的順序被觸發(fā)。當(dāng)添加的事件處理程序較多時(shí)則順序無規(guī)律。

跨瀏覽器的事件處理程序

就不同瀏覽器的差異,對(duì)事件處理程序封裝,此方法接收三個(gè)參數(shù):要操作的元素、事件名稱、事件處理程序函數(shù)。jQuery創(chuàng)始人John Resig做法如下:

function addEvent(node, type, handler){
  if(!node){
    return false;
  }
  if(node.addEventListener){
    node.addEventListener(type, handler, false);
    return true;
  }else if(node.attachEvent){
    node['e' + type + handler] = handler;
    node[type + handler] = function(){
      node['e' + type + handler](window.event);
    };
    node.attachEvent('on' + type, node[type + handler]);
    return true;
  }
  return false;
}

在取消事件處理程序的時(shí)候,如下:

function removeEvent(node, type, handler){
  if(!node){
    return false;
  }
  if(node.removeEventListener){
    node.removeEventListener(type, handler, false);
    return true;
  }else if(node.detachEvent){
    node.detachEvent('on' + type, node[type + handler]);
    node[type + handler] = null;
    return true;
  }
  return false;
}

事件對(duì)象

在觸發(fā)DOM上的某個(gè)事件時(shí),會(huì)產(chǎn)生一個(gè)事件對(duì)象event,這個(gè)對(duì)象中包含著所有與事件有關(guān)的信息。包括導(dǎo)致事件的元素、事件的類型以及其他與特定事件相關(guān)的信息。例如,鼠標(biāo)操作導(dǎo)致的事件對(duì)象中,會(huì)包含鼠標(biāo)位置的信息,而鍵盤操作導(dǎo)致的事件對(duì)象中,會(huì)包含與按下的鍵有關(guān)的信息。所有的瀏覽器都支持event對(duì)象,但支持方式不同。

DOM中的事件對(duì)象

兼容DOM的瀏覽器會(huì)將一個(gè)event對(duì)象傳入到事件處理程序中。
event對(duì)象包含與創(chuàng)建它的特定事件有關(guān)的屬性和方法。觸發(fā)的事件類型不一樣,可用的屬性和方法也不一樣。不過所有事件都會(huì)有下表列出的成員。


在事件處理程序內(nèi)部,對(duì)象this始終等于currentTarget的值,而target則只包含事件的實(shí)際目標(biāo)。如果直接將使事件處理程序指定給了目標(biāo)元素,則this、currentTargettarget包含相同的值。

阻止事件的默認(rèn)行為
要阻止特定事件的默認(rèn)行為,可用使用preventDefault()方法。例如,鏈接的默認(rèn)行為就是在被單擊時(shí)會(huì)導(dǎo)航到其href特性指定的URL,若想阻止鏈接導(dǎo)航這一默認(rèn)行為,那么提供鏈接的onclick事件處理程序可以取消它,如下:

var link = document.getElementById("myLink");
link.onclick = function(event){
  event.preventDefault();
}

只有cancelable屬性設(shè)置為true的事件,才可以使用preventDefault()取消其默認(rèn)行為

阻止事件傳播
stopPropagation()方法用于立即停止事件在DOM層次中的傳播,即取消進(jìn)一步的事件捕獲或冒泡。例如,直接添加到一個(gè)按鈕的事件處理程序可以調(diào)用stopPropagation(),從而避免觸發(fā)注冊(cè)在document.body上面的事件處理程序,如下所示:

var btn = document.getElementById("myBtn");
btn.onclick = function(e){
  alert('Clicked');
  e.stopPropagation()
};
document.body.onclick = function(e){
  alert('Body clicked');
};

調(diào)用stopPropagation()后事件不會(huì)傳到document.body,因此不會(huì)觸發(fā)注冊(cè)在document.body上的onclick事件處理程序。

只有在事件處理程序執(zhí)行期間,event對(duì)象才會(huì)存在;一旦事件處理程序執(zhí)行完成,event對(duì)象就會(huì)被銷毀。

IE 中的事件對(duì)象

IE中的event對(duì)象有幾種不同的方式,取決于指定事件處理程序的方法。直接為DOM元素添加事件處理程序時(shí),event對(duì)象作為window對(duì)象的一個(gè)屬性存在,如下所示:

var btn = document.getElementById("myBtn");
btn.onclick = function(){
  var e = window.event;
  alert(e.type); // "click"
};

可是,如果事件處理程序是使用attachEvent()添加的,那么就會(huì)有一個(gè)event對(duì)象作為參數(shù)被傳入事件處理程序函數(shù)中,如下所示:

var btn = document.getElementById("myBtn");
btn.attachEvent('onclick', function(event){
  alert(event.type); // "click"
});

像這樣使用attachEvent()的情況下,也可以提供window對(duì)象來訪問event對(duì)象,就像使用DOM0級(jí)方法一樣。不過為方便起見,同一個(gè)對(duì)象也會(huì)作為參數(shù)傳遞。
如果是通過HTML特性指定的事件處理程序,那么還可以通過一個(gè)名叫event的變量來訪問event對(duì)象(與DOM中的 事件模型相同),如下所示:

<input type="button" value="Click Me" onclick="alert(event.type)">

IEevent對(duì)象同樣也包含與創(chuàng)建它的事件相關(guān)的屬性和方法。其中很多屬性和方法都有對(duì)應(yīng)的或者相關(guān)的DOM屬性和方法。與DOMevent對(duì)象一樣,這些屬性和方法也會(huì)因?yàn)槭录愋偷牟煌煌?,但所有事件?duì)象都會(huì)包含下表所列的屬性和方法。

注:使用returnValue達(dá)到了阻止默認(rèn)行為的目的,cancelBubble設(shè)為true可阻止事件冒泡。

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

雖然DOMIEevent對(duì)象不同,但基于它們的相似性,我們還是可以寫出跨瀏覽器的事件對(duì)象方案

function getEvent(e){
  return e || window.event;
}

function getTarget(e){
  return e.target || e.srcElement;
}

function preventDefault(e){
  if(e.preventDefault){
    e.preventDefault();
  }else {
    e.returnValue = false;
  }
}

function stopPropagation(e){
  if(e.stopPropagation){
    e.stopPropagation();
  }else {
    e.cancelBubble = true;
  }
}

事件冒泡的應(yīng)用

事件代理,又稱為事件委托。
例如:要求當(dāng)點(diǎn)擊每一個(gè)元素li時(shí)控制臺(tái)展示該元素的文本內(nèi)容。不考慮兼容,如下所示

<ul class="ct">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
<script>
    var ct = document.querySelector('.ct');
    ct.addEventListener('click', function(e){
        var target = e.target;
        if(target.tagName.toLowerCase() === 'li'){
            console.log(target.innerText);
        }
    })
</script>

事件類型

Web瀏覽器中可能發(fā)生的事件有很多類型,DOM3級(jí)事件規(guī)定了以下幾類事件:

  • UIUser Interface,用戶界面)事件,當(dāng)用戶與頁面上的元素交互時(shí)觸發(fā)
  • 焦點(diǎn)事件,當(dāng)元素獲得或失去焦點(diǎn)時(shí)觸發(fā)
  • 鼠標(biāo)事件,當(dāng)用戶通過鼠標(biāo)在頁面上執(zhí)行操作時(shí)觸發(fā)
  • 滾輪事件,當(dāng)使用鼠標(biāo)滾輪(或類似設(shè)備)時(shí)觸發(fā)
  • 文本事件,當(dāng)在文檔中輸入文本時(shí)觸發(fā)
  • 鍵盤事件,當(dāng)用戶通過鍵盤在頁面上執(zhí)行操作時(shí)觸發(fā)
  • 合成事件,當(dāng)為IMEInput Method Editor,輸入法編輯器)輸入字符時(shí)觸發(fā)
  • 變動(dòng)(mutation)事件,當(dāng)?shù)讓?code>DOM結(jié)構(gòu)發(fā)生變化時(shí)觸發(fā)
    此處不想述,具體的可以查看JavaScript程序設(shè)計(jì)

參考:

?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • ??JavaScript 與 HTML 之間的交互是通過事件實(shí)現(xiàn)的。 ??事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,681評(píng)論 1 11
  • JavaScript 與 HTML 之間的交互是通過事件實(shí)現(xiàn)的。事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特定的交互瞬...
    threetowns閱讀 409評(píng)論 0 0
  • 事件處理程序在應(yīng)用中是必不可少的,雖然現(xiàn)在很多框架都有自己實(shí)現(xiàn)事件處理方法,但是熟知原生才能讓我們應(yīng)對(duì)各種各樣的需...
    俗三瘋閱讀 364評(píng)論 0 1
  • 事件流 事件流描述的是從頁面中接受事件的順序。但是IE和Netscape開發(fā)團(tuán)隊(duì)提出了差不多相反的事件流的概念。I...
    losspm閱讀 309評(píng)論 0 0
  • 第13章 事件 1. 事件流 事件流描述的是從頁面中接收事件的順序。 (1) 事件冒泡 IE 的事件流叫做事件冒泡...
    yinxmm閱讀 1,026評(píng)論 0 17

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