摘要:而事件分為個(gè)級(jí)別級(jí)事件處理程序,級(jí)事件處理程序和級(jí)事件處理程序。級(jí)中沒(méi)有規(guī)范事件的相關(guān)內(nèi)容,所以沒(méi)有級(jí)事件處理。
HTML依托于JavaScript來(lái)實(shí)現(xiàn)用戶與WEB網(wǎng)頁(yè)之間的動(dòng)態(tài)交互,接收用戶操作并做出相應(yīng)的反饋,而事件在此間則充當(dāng)橋梁的重要角色。
日常開(kāi)發(fā)中,經(jīng)常會(huì)為某個(gè)元素綁定一個(gè)事件,編寫(xiě)相應(yīng)的業(yè)務(wù)邏輯,在元素被點(diǎn)擊時(shí)執(zhí)行,并反饋到用戶操作界面。
這個(gè)過(guò)程中,事件就像一個(gè)偵聽(tīng)器,當(dāng)點(diǎn)擊動(dòng)作發(fā)生時(shí),才會(huì)執(zhí)行對(duì)應(yīng)的程序。這種模式可稱之為觀察員模式。
接下來(lái)就講講DOM事件相關(guān)知識(shí)。
何為事件事件就是用戶或?yàn)g覽器自身執(zhí)行的某種動(dòng)作
常用的DOM事件有click/mouseover/mouseout/keyup/keydown等。
事件流事件流描述的是從頁(yè)面中接收事件的順序
HTML描述的是一個(gè)DOM文檔結(jié)構(gòu),而事件流所描述的是DOM文檔節(jié)點(diǎn)接收事件順序。
而事件流有兩種事件模式,捕獲/冒泡,兩者所描述的事件傳遞順序?qū)α⑾喾础?/p> 事件模式:捕獲與冒泡
事件冒泡:事件開(kāi)始時(shí)由最具體的元素(文檔中嵌套層次最深的那個(gè)節(jié)點(diǎn))接收,然后逐級(jí)向上傳播到較為不具體的節(jié)點(diǎn)(文檔)
規(guī)范要求事件冒泡到document對(duì)象,而瀏覽器則會(huì)將事件一直冒泡到window對(duì)象。
所有瀏覽器都支持事件冒泡(包括IE9以下)。
事件捕獲:(與事件冒泡相反)事件捕獲的思想是不太具體的節(jié)點(diǎn)應(yīng)該更早接收到事件,而最具體的節(jié)點(diǎn)應(yīng)該最后接收到事件
與冒泡一樣,雖然規(guī)定事件應(yīng)該從document對(duì)象開(kāi)始傳播,但瀏覽器普遍都是從window對(duì)象開(kāi)始捕獲。
IE9以下不支持事件捕獲
DOM 事件流"DOM2級(jí)事件"規(guī)定事件流包括三個(gè)階段,順序進(jìn)行
事件捕獲階段
處于目標(biāo)階段
事件冒泡階段
TIPS: 實(shí)際的目標(biāo)元素在捕獲階段不會(huì)接收到事件,在處于目標(biāo)階段時(shí)接收事件發(fā)生處理,并被看成是冒泡階段的一部分。
盡管"DOM2級(jí)事件"規(guī)范明確要求捕獲階段不會(huì)涉及事件目標(biāo),但瀏覽器會(huì)在捕獲階段觸發(fā)事件對(duì)象上的事件。
?事件處理程序響應(yīng)某個(gè)事件的函數(shù)方法,我們稱之為事件處理程序(或事件偵聽(tīng)器)
window.onclick = function() { //... } // 這里的function(){}就是事件處理程序HTML事件處理程序
HTML中元素支持的事件,可以使用一個(gè)同名的HTML特性來(lái)指定,而這個(gè)特性的值就是js能執(zhí)行的代碼或表達(dá)式。寫(xiě)法上可以看出類似HTML中id/type/class等屬性的寫(xiě)法,都是on+"..."
缺點(diǎn):HTML是結(jié)構(gòu)層(顯示層),而JavaScript是行為層(業(yè)務(wù)層)。在顯示層上去編寫(xiě)業(yè)務(wù)邏輯代碼處理,會(huì)使得HTML與JavaScript代碼耦合過(guò)于緊密,不好維護(hù)。
DOM級(jí)別一共可以分為四個(gè)級(jí)別:DOM0級(jí)、DOM1級(jí)、DOM2級(jí)和DOM3級(jí)。
而DOM事件分為3個(gè)級(jí)別:DOM 0級(jí)事件處理程序,DOM 2級(jí)事件處理程序和DOM 3級(jí)事件處理程序。DOM 1級(jí)中沒(méi)有規(guī)范事件的相關(guān)內(nèi)容,所以沒(méi)有DOM 1級(jí)事件處理。
DOM0 級(jí)事件處理程序每個(gè)元素(HTML元素)都有自己的事件處理程序?qū)傩?,屬性名通常以on開(kāi)頭,例如onclick/onmouseover。為這個(gè)屬性的值設(shè)置一個(gè)函數(shù),就可以指定事件處理程序。而將其屬性值賦值為null,則完成解綁。(同個(gè)元素?zé)o法綁定多個(gè)同名事件)
var myBtn = document.getElementById("myBtn"); // 為myBtn綁定事件處理程序, 只能綁定一個(gè) myBtn.onclick = function() { alert("Hello world!"); } // 解綁 myBtn.onclick = null;DOM2 級(jí)事件處理程序
"DOM2級(jí)事件"定義了兩個(gè)方法,addEventListener()/removeEventListener(),用于為元素綁定和解綁事件。
(可綁定多個(gè)事件,區(qū)別于DOM0級(jí)/HTML僅能綁定一個(gè))。
el.addEventListener(eventName, callBack, useCapture)
eventName: 事件名稱
callBack: 回調(diào)函數(shù),當(dāng)事件觸發(fā)時(shí),函數(shù)會(huì)傳入一個(gè)參數(shù)event,為當(dāng)前的事件對(duì)象
useCapture: 默認(rèn)是false,代表事件句柄在冒泡階段執(zhí)行, true則代表在捕獲階段執(zhí)行
var myBtn = document.getElementById("myBtn"); var handleClick = function() { alert("Hello world!"); } // 綁定事件處理程序 myBtn.addEventListener("click", handleClick, false); // 解綁 myBtn.removeEventListener("click", handleClick);
TIPS:DOM2級(jí)事件處理程序,解綁時(shí)function必須與傳入addEventListener相同
// 綁定 myBtn.addEventListener("click", function() { // 匿名函數(shù) }); // 解綁 myBtn.removeEventListener("click",function() { // 匿名函數(shù) }); // add/remove 分別綁定了兩個(gè)匿名函數(shù)(函數(shù)為引用類型),所以兩個(gè)函數(shù)并不相同,所以無(wú)法成功解綁
TIPS:綁定多個(gè)事件處理程序時(shí),執(zhí)行順序按綁定順序執(zhí)行
myBtn.addEventListener("click", function() { // step1... }) myBtn.addEventListener("click", function() { // step2... }) // 執(zhí)行順序:step1 -> step2
瀏覽器支持情況:IE9以下不支持DOM2級(jí)事件處理程序
IE 事件處理程序IE9以下不支持DOM2級(jí)事件,但I(xiàn)E提供了與DOM2級(jí)事件類似的兩個(gè)方法,attachEvent()/detachEvent,IE9以下不支持事件捕獲,所以attachEvent僅支持冒泡階段觸發(fā),只接收兩個(gè)參數(shù)(eventName, function)。
// 綁定 myBtn.attachEvent("onclick", handleClick); // 解綁 myBtn.detachEvent("onclick", handleClick);
TIPS:
解綁時(shí)function必須與傳入attachEvent相同,這點(diǎn)與DOM2級(jí)事件相同
與DOM0級(jí)的區(qū)別,DOM0級(jí)事件處理在元素的作用域運(yùn)行,而attachEvent事件處理在全局,this指向window
綁定多個(gè)事件處理程序時(shí),執(zhí)行順序按綁定順序逆反執(zhí)行(與DOM2級(jí)相反)
myBtn.attachEvent("click", function() { // step1... }) myBtn.attachEvent("click", function() { // step2... }) // 執(zhí)行順序:step2 -> step1Event 事件對(duì)象 常見(jiàn)應(yīng)用
阻止默認(rèn)事件
阻止事件流發(fā)生傳遞(冒泡/捕獲)
阻止剩余事件處理函數(shù)的執(zhí)行,并阻止當(dāng)前事件在事件流上傳遞
當(dāng)前綁定事件的元素
當(dāng)前觸發(fā)事件的元素
同個(gè)元素綁定多個(gè)同名事件時(shí),stopImmediatePropagation不僅阻止了冒泡,而且會(huì)阻止后續(xù)事件的執(zhí)行,可以理解為加強(qiáng)版的stopPropagation
myBtn.addEventListener("click", function(event) { // step1; event.stopImmediatePropagation(); }) myBtn.addEventListener("click", function(event) { // step2; // 我被stopImmediatePropagation阻止掉了?。?! })
事件處理程序內(nèi)部,this等于currentTarget(當(dāng)前綁定事件的元素),而target(當(dāng)前觸發(fā)事件的元素)
// currentTarget == target myBtn.addEventListener("click", function(event) { event.target == event.currentTarget; // true -> myBtn }) // currentTarget != target 捕獲/冒泡 document.body.addEventListener("click", function(event){ event.target == event.currentTarget; // false // event.target -> myBtn // event.currentTarget -> body })內(nèi)存與性能
WEB網(wǎng)頁(yè)是運(yùn)行在瀏覽器客戶端的,而計(jì)算機(jī)分配給瀏覽器的內(nèi)存及CPU占用是有限制的。雖說(shuō)瀏覽器引擎不斷地發(fā)展優(yōu)化,但是內(nèi)存占用多了, 性能不免會(huì)損耗。
為元素指定事件綁定程序,事實(shí)上是賦值了一個(gè)函數(shù)方法,而函數(shù)在javaScript中是一種引用類型的數(shù)據(jù)格式,既然是數(shù)據(jù)那就需要用到內(nèi)存儲(chǔ)存。函數(shù)創(chuàng)建多了,消耗掉內(nèi)存。
為元素指定事件綁定程序,首先需要對(duì)DOM進(jìn)行查詢,找出要綁定事件的元素。而這也會(huì)造成DOM元素的訪問(wèn)次數(shù)增加。DOM的操作一直是網(wǎng)頁(yè)性能的一個(gè)優(yōu)化點(diǎn)。
了解完事件綁定帶來(lái)內(nèi)存跟性能的原理,我們來(lái)看一個(gè)例子,例如我們有一個(gè)ul>li的列表,要監(jiān)聽(tīng)每一個(gè)li的點(diǎn)擊事件,并觸發(fā)事件處理程序。
多帶帶綁定的話,10個(gè)li就要對(duì)DOM元素查詢10次,創(chuàng)建的匿名函數(shù)就有10個(gè)(當(dāng)然可以共同創(chuàng)建同個(gè)函數(shù)引用),如果還有20個(gè),30個(gè),100個(gè),那么這種為每個(gè)li元素多帶帶綁定事件的方法,絕對(duì)不是最優(yōu)解。
這就引出下面的優(yōu)化方案:"事件委托"。
事件委托(事件代理)對(duì)"事件處理程序綁定過(guò)多"的問(wèn)題,最好的解決方案就是"事件委托"。它的原理是利用了事件流的"冒泡"機(jī)制,事件目標(biāo)元素會(huì)把事件向上層傳遞,直到document(瀏覽器會(huì)傳到window),所以父級(jí)節(jié)點(diǎn)是可以接收子節(jié)點(diǎn)的事件傳遞。
以剛剛ul>li的例子,li有很多個(gè), 但它們有一個(gè)共同的父節(jié)點(diǎn)ul。li的點(diǎn)擊事件會(huì)冒泡到ul,因此我們可以在ul上綁定一個(gè)事件處理程序,處理所有li的點(diǎn)擊事件,然后通過(guò)event.target可以確定觸發(fā)事件的元素。
var ulParent = document.getElementById("parent"); ulParent.addEventListener("click", function(event) { var taget = event.target; })
通過(guò)"事件委托"減少了DOM元素的查詢,以及多個(gè)函數(shù)的內(nèi)存占用,而且還有一個(gè)好處,當(dāng)我們的li是動(dòng)態(tài)的,增加和移除時(shí),都無(wú)需再做綁定和解綁事件操作,因?yàn)樗紩?huì)冒泡到父級(jí)節(jié)點(diǎn)。
移除多余的事件綁定文檔中移除了綁定了事件的DOM元素,如innerHTML/removeChild()/replaceChild()等可以對(duì)DOM進(jìn)行替換,而移除的DOM元素原先所綁定的事件處理程序,并不能有效被瀏覽器垃圾回收,所以占用一直存在。
所以建議在移除某個(gè)DOM元素時(shí),如果其綁定了事件處理程序,需手動(dòng)解除綁定,釋放內(nèi)存。
自定義事件除了為元素綁定支持的事件以外,我們還可以通過(guò)Event/CustomEvent來(lái)創(chuàng)建開(kāi)發(fā)者自定義事件。
兩者不同的是CustomEvent可傳遞一個(gè)Object對(duì)象來(lái)傳輸數(shù)據(jù)。
// Event var eve = new Event("custome"); // CustomeEvent 可傳參數(shù) var eve = new CustomeEvent("custome", { detail: { name: "KenTsang", age: 28 } }); // 為DOM元素添加事件監(jiān)聽(tīng) ele.addEventListener("custome", function(event) { console.log(event.detail); }) // 觸發(fā)ele綁定的自定義事件 ele.dispatch(eve);
事件這塊還剩下一部分知識(shí)點(diǎn),后續(xù)文章會(huì)再就模擬事件這塊知識(shí)點(diǎn)進(jìn)行拆分詳解。
天冷了,更文不易,望大家多多點(diǎn)贊。
《JavaScript高級(jí)程序設(shè)計(jì)》
作者:以樂(lè)之名本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。轉(zhuǎn)載請(qǐng)指明出處。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/99880.html
摘要:由于計(jì)算機(jī)的國(guó)際化,組織的標(biāo)準(zhǔn)牽涉到很多其他國(guó)家,因此組織決定改名表明其國(guó)際性。規(guī)范由萬(wàn)維網(wǎng)聯(lián)盟制定。級(jí)標(biāo)準(zhǔn)級(jí)標(biāo)準(zhǔn)是不存在的,級(jí)一般指的是最初支持的。 這篇筆記的內(nèi)容對(duì)應(yīng)的是《JavaScript高級(jí)程序設(shè)計(jì)(第三版)》中的第一章。 1.ECMA 和 ECMA-262 ECMA 是歐洲計(jì)算機(jī)制造商協(xié)會(huì)的縮寫(xiě),全程是 European Computer Manufacturers Ass...
摘要:解耦優(yōu)勢(shì)代碼復(fù)用,單元測(cè)試。常用比較誤區(qū)可同時(shí)判斷,可用來(lái)判斷對(duì)象屬性是否存在。使用作判斷無(wú)法進(jìn)行充分的類型檢查。文件中應(yīng)用常量參考文檔高級(jí)程序設(shè)計(jì)作者以樂(lè)之名本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。 showImg(https://segmentfault.com/img/bVburXw?w=500&h=400); 編寫(xiě)可維護(hù)性代碼 可維護(hù)的代碼遵循原則: 可理解性 (方便他人理解) 直觀...
摘要:項(xiàng)目中我們可通過(guò)設(shè)置采集率,或?qū)σ?guī)定時(shí)間內(nèi)數(shù)據(jù)匯總再上報(bào),減少請(qǐng)求數(shù)量,從而緩解服務(wù)端壓力。借鑒別人的一個(gè)例子只采集上報(bào)錯(cuò)誤參考文檔高級(jí)程序設(shè)計(jì)如何優(yōu)雅處理前端異常作者以樂(lè)之名本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。 showImg(https://segmentfault.com/img/bVbnuud?w=640&h=640); 錯(cuò)誤類型 即時(shí)運(yùn)行錯(cuò)誤 (代碼錯(cuò)誤) 資源加載錯(cuò)誤 常見(jiàn)...
摘要:作用域鏈查找作用域鏈的查找是逐層向上查找。而全局變量和閉包則會(huì)與之相反,繼續(xù)保存,所以使用用后需手動(dòng)標(biāo)記清除,以免造成內(nèi)存泄漏。獲取元素的屬性獲取元素的屬性等參考文檔高級(jí)程序設(shè)計(jì)作者以樂(lè)之名本文原創(chuàng),有不當(dāng)?shù)牡胤綒g迎指出。 showImg(https://segmentfault.com/img/bVburXV?w=500&h=399); 作用域鏈查找 作用域鏈的查找是逐層向上查找。查...
摘要:文件內(nèi)部使用使用到的代碼引入外部文件外部代碼的地址標(biāo)簽的位置一般情況下,標(biāo)簽的位置放在標(biāo)簽中引入代碼頁(yè)面結(jié)構(gòu)對(duì)于需要引入很多的中間,如果把放在頭部,無(wú)疑會(huì)導(dǎo)致瀏覽器呈現(xiàn)頁(yè)面出現(xiàn)延遲,就是導(dǎo)致頁(yè)面出現(xiàn)空白。頁(yè)面結(jié)構(gòu)引入代碼 這篇筆記的內(nèi)容對(duì)應(yīng)的是《JavaScript高級(jí)程序設(shè)計(jì)(第三版)》中的第二章。 1.使用方式 在HTML中使用 JavaScript 的方式有兩種,第一種就是直接內(nèi)...
閱讀 3489·2023-04-25 14:07
閱讀 3650·2021-09-28 09:35
閱讀 2162·2019-08-30 15:55
閱讀 1483·2019-08-30 13:48
閱讀 2572·2019-08-30 13:16
閱讀 3293·2019-08-30 12:54
閱讀 3301·2019-08-30 11:19
閱讀 1954·2019-08-29 17:17