DOM中事件處理概覽與原理的全面解析
事件是一種異步編程的實(shí)現(xiàn)方式,本質(zhì)上是程序各個(gè)組成部分之間的通信,DOM支持大量的事件;
本文通過這幾點(diǎn)向大家詳細(xì)解析事件處理的基本原理:事件類型、事件目標(biāo)、事件處理程序、事件對(duì)象、事件傳播
最后再向大家介紹Event對(duì)象;
一、事件類型(event type):是一個(gè)用來說明發(fā)生了什么類型事件的全小寫的字符串,如‘mouseover'
傳統(tǒng)事件類型:表單事件,Window事件,鼠標(biāo)事件,鍵盤事件,DOM事件, HTML5事件,觸摸屏和移動(dòng)設(shè)備事件等
二、事件目標(biāo)(event target):觸發(fā)事件的對(duì)象
三、事件處理程序(或事件監(jiān)聽程序)(event listener):處理或響應(yīng)事件的函數(shù)。當(dāng)某對(duì)象觸發(fā)某事件時(shí),瀏覽器將自動(dòng)調(diào)用在該對(duì)象上注冊(cè)的函數(shù);
注冊(cè)事件處理程序(監(jiān)聽事件):
1.作為HTML屬性注冊(cè)(只會(huì)在冒泡階段觸發(fā))如<table id="t" onclick="modifyText();">;而某些事件類型通常直接在瀏覽器上觸發(fā),而非任何特定文檔元素上觸發(fā),把這些事件處理程序放在<body>標(biāo)簽上,但瀏覽器會(huì)在Window對(duì)象上注冊(cè)它們,如<body onload="alert('Hello world!')">,這些事件有:
onafterprint onfocus ononline onresize onbeforeprint onhashchange
onpagehide onstorage onbeforeunload onload onpageshow onundo
onblur onmessage onpopstate onunload onerror onoffline onredo
作為HTML屬性的事件的值是JS代碼字符串,是處理函數(shù)的主體,不含{},注意:盡量不要在任何其他HTML標(biāo)簽上注冊(cè)事件,它違反了HTML與JavaScript代碼相分離的原則,倘若事件函數(shù)可能還沒加載進(jìn)來就點(diǎn)擊了事件對(duì)象元素,這會(huì)導(dǎo)致錯(cuò)誤;
2.作為DOM元素的屬性來注冊(cè)(只會(huì)在冒泡階段觸發(fā)),此時(shí)的事件處理程序?qū)傩缘拿中杓印畂n'前綴,這種方式兼容所有瀏覽器,唯一的缺點(diǎn)是只能注冊(cè)一個(gè)事件處理函數(shù),如果定義兩次onclick屬性,后一次定義會(huì)覆蓋前一次;如:window.onload = function(){...};
3.除了IE8及之前版本外的所有瀏覽器中,DOM的事件操作(監(jiān)聽和觸發(fā)),都定義在EventTarget接口。Element節(jié)點(diǎn)、document節(jié)點(diǎn)和window對(duì)象,都部署了這個(gè)接口。此外,XMLHttpRequest、AudioNode、AudioContext等瀏覽器內(nèi)置對(duì)象,也部署了這個(gè)接口。該接口有三個(gè)方法,addEventListener和removeEventListener用于綁定和移除監(jiān)聽函數(shù),dispatchEvent用于觸發(fā)事件;
addEventListener(type,listener,boolean)方法來注冊(cè)listener,第三個(gè)參數(shù)設(shè)置事件的傳播方式,通常使用默認(rèn)值false,表示監(jiān)聽函數(shù)只在冒泡階段(pupple)被觸發(fā),當(dāng)設(shè)為true時(shí),表示監(jiān)聽函數(shù)在捕獲階段(capture)觸發(fā);可以為同一對(duì)象上的同一類型事件注冊(cè)任意多個(gè)listener,所有l(wèi)istener會(huì)按照注冊(cè)順序觸發(fā)(注冊(cè)重復(fù)的listener將會(huì)被瀏覽器忽略);
如果希望向監(jiān)聽函數(shù)傳遞參數(shù),可以用匿名函數(shù)包裝一下監(jiān)聽函數(shù),如elm.addEventListener('click',function(){listen('實(shí)參')},false);
當(dāng)注冊(cè)的listener是一個(gè)對(duì)函數(shù)的引用變量,就可以用removeEventLestener(type,listener,boolean)在事件目標(biāo)上刪除該listener,對(duì)同一監(jiān)聽事件的冒泡事件和捕獲事件需要分別刪除,兩者互不干擾;
var div = document.getElementById('div'); var listener = function (event) { /* do something here */ }; div.addEventListener('click', listener, false); div.removeEventListener('click', listener, false);
dispatchEvent(event)方法在當(dāng)前節(jié)點(diǎn)上手動(dòng)觸發(fā)指定事件,從而觸發(fā)監(jiān)聽函數(shù)的執(zhí)行。該方法返回一個(gè)布爾值,只要有一個(gè)監(jiān)聽函數(shù)調(diào)用了Event.preventDefault(),就返回false,否則為true,參數(shù)是一個(gè)Event對(duì)象的實(shí)例,該參數(shù)不能為空,且必須是一個(gè)有效的事件對(duì)象,否則報(bào)錯(cuò);
btn.addEventListener('click', listener, false);
var e = new Event('click');
btn.dispatchEvent(e); //在btn上立即觸發(fā)click事件,將立即調(diào)用listener
下面例子根據(jù)dispatchEvent方法的返回值,判斷事件是否被取消了
var canceled = !btn.dispatchEvent(event);
if (canceled) { console.log('事件取消'); }
else { console.log('事件未取消'); }}
4.IE8及之前版本僅支持attachEvent (type,listener)和detachEvent(type,listener),它們的用法和addEventListener的區(qū)別:a.參數(shù)只有兩個(gè);b.參數(shù)type必須加'on'前綴;c.它允許對(duì)同一監(jiān)聽事件進(jìn)行重復(fù)注冊(cè),且都會(huì)被調(diào)用;d.使用attachEvent方法有個(gè)缺點(diǎn),是this的值會(huì)變成 window 對(duì)象而不是觸發(fā)事件的元素;
調(diào)用順序問題:1).通過設(shè)置對(duì)象屬性或HTML屬性注冊(cè)的處理程序一直優(yōu)先調(diào)用;
2).使用addEventListener 注冊(cè)的處理程序按照它們的注冊(cè)順序調(diào)用;
3).舊版IE中使用attachEvent注冊(cè)的處理程序可能按照任何順序調(diào)用。
返回值問題:
1).事件處理程序的返回值只對(duì)通過屬性注冊(cè)的處理程序才有意義,通過設(shè)置對(duì)象屬性或HTML屬性注冊(cè) 事件處理程序的返回值為false,就是告訴瀏覽器不要執(zhí)行這個(gè)事件相關(guān)的默認(rèn)操作。當(dāng)瀏覽器要跳轉(zhuǎn)到新頁面時(shí)觸發(fā)Window對(duì)象的onbeforeunload事件,若它的的返回值為字符串,則它將出現(xiàn)在詢問確認(rèn)對(duì)話框中;
2).addEventListener()或attachEvent()注冊(cè)事件處理程序若要取消瀏覽器的默認(rèn)操作必須調(diào)用preventDefault()方法或設(shè)置事件對(duì)象的returnValue屬性。
this指向問題:
1).addEventListener方法指定的監(jiān)聽函數(shù),內(nèi)部的this對(duì)象總是指向觸發(fā)事件的那個(gè)節(jié)點(diǎn);
2).IE8及以前的attachEvent方法注冊(cè)的事件處理函數(shù)的this指向全局對(duì)象;
以下寫法的this對(duì)象都指向Element節(jié)點(diǎn)。
element.onclick = print;
element.addEventListener('click', print, false)
element.onclick = function () {console.log(this.id);}
<element onclick="console.log(this.id)">
以下寫法的this對(duì)象,都指向全局對(duì)象。
element.onclick = function (){ doSomething() };
element.setAttribute('onclick', 'doSomething()');
<element onclick="doSomething()">
element.attachEvent('onclick',doSomething) //IE8
內(nèi)存問題:對(duì)如下代碼,每個(gè)循環(huán)中都會(huì)創(chuàng)建一個(gè)新的匿名函數(shù),占用的內(nèi)存越來越多;由于沒有保持到匿名函數(shù)的引用,它不可能被調(diào)用 removeEventListener;所以應(yīng)當(dāng)把第二參數(shù)listener保持為對(duì)處理事件函數(shù)的引用;
for(i=0 ; i<els.length ; i++){ els[i].addEventListener("click", function(e){/*do something*/}, false}); }
通用的兼容舊版IE的工具函數(shù):
確保事件處理程序的this指向事件的目標(biāo)對(duì)象的工具函數(shù)addEvent
function addEvent(target,type,func){ if(target.addEventListener){ target.addEventListener(type,func,false); }else{ target.attachEvent('on'+type,function(e){ //這里attachEvent注冊(cè)的處理函數(shù)未綁定引用,所以無法用detachEvent刪除 return func.call(target,e); }); } }
通用的事件處理程序(因?yàn)镮E8及以前版本,作為事件目標(biāo)的on-屬性的處理程序需要window.event來獲得事件對(duì)象,且觸發(fā)事件的目標(biāo)節(jié)點(diǎn)對(duì)象通過event.srcElement屬性獲得)
function func(event){ var event = event||window.event; var target = event.target || event.srcElement; //......處理程序代碼 }
四、事件傳播(event propagation):是瀏覽器決定哪個(gè)對(duì)象觸發(fā)其事件處理程序的過程。
“DOM2級(jí)事件”規(guī)定的事件流包括三個(gè)階段:事件捕獲階段==>處于目標(biāo)階段==>事件冒泡階段。首先發(fā)生的是事件捕獲階段(從外層向內(nèi)層傳播),為事件傳播經(jīng)過的所有節(jié)點(diǎn)截獲事件提供了機(jī)會(huì)。然后是實(shí)際的目標(biāo)接收事件(按注冊(cè)順序執(zhí)行)。最后一個(gè)階段是冒泡階段(從內(nèi)層向外層冒泡)。
當(dāng)容器元素及嵌套元素,即在捕獲階段又在冒泡階段調(diào)用事件處理程序時(shí):事件按DOM事件流的順序執(zhí)行事件處理程序,且當(dāng)事件處于目標(biāo)階段時(shí),事件調(diào)用順序決定于綁定事件的書寫順序
如果希望事件到某個(gè)節(jié)點(diǎn)為止,不再傳播,有兩種方式:
1.使用事件對(duì)象的event.stopPropagation()方法來阻止當(dāng)前監(jiān)聽函數(shù)的傳播;
2.使用事件對(duì)象的event.stopImmediatePropagation()方法來阻止當(dāng)前事件在其事件對(duì)象上的所有監(jiān)聽函數(shù)的傳播;
事件的代理(delegation):由于事件會(huì)在冒泡階段向上傳播到父節(jié)點(diǎn),因此可以把子節(jié)點(diǎn)的監(jiān)聽函數(shù)定義在父節(jié)點(diǎn)上,由父節(jié)點(diǎn)的監(jiān)聽函數(shù)統(tǒng)一處理多個(gè)子元素的事件;
五、事件對(duì)象(Event):事件發(fā)生以后,會(huì)生成一個(gè)事件對(duì)象,作為參數(shù)傳給監(jiān)聽函數(shù)。瀏覽器原生提供一個(gè)Event對(duì)象,所有的事件都是這個(gè)對(duì)象的實(shí)例,或者說繼承了Event.prototype對(duì)象。Event對(duì)象本身就是一個(gè)構(gòu)造函數(shù),可以用來生成新的實(shí)例。
var ev = new Event("look", {"bubbles":true, "cancelable":false});
document.dispatchEvent(ev);
Event構(gòu)造函數(shù)接受兩個(gè)參數(shù)。第一個(gè)參數(shù)是字符串,表示事件的名稱;第二個(gè)參數(shù)是一個(gè)對(duì)象,表示事件對(duì)象的配置。該參數(shù)可以有以下兩個(gè)屬性。
bubbles:布爾值,可選,默認(rèn)為false,表示事件對(duì)象是否冒泡。
cancelable:布爾值,可選,默認(rèn)為false,表示事件是否可以被取消。
Event對(duì)象的屬性:
1.與事件的階段有關(guān):
bubbles: 只讀屬性,返回一個(gè)布爾值,表示當(dāng)前事件是否會(huì)冒泡,可根據(jù)事件是否會(huì)冒泡來調(diào)用不同的函數(shù)。
eventPhase:返回一個(gè)整數(shù)值(0,1,2,3之一),表示事件目前所處的狀態(tài)
<0,事件目前沒有發(fā)生。
<1,事件目前處于捕獲階段,即處于從祖先節(jié)點(diǎn)向目標(biāo)節(jié)點(diǎn)的傳播過程中。該過程是從Window對(duì)象到Document節(jié)點(diǎn),再到HTMLHtmlElement節(jié)點(diǎn),直到目標(biāo)節(jié)點(diǎn)的父節(jié)點(diǎn)為止。
<2,事件到達(dá)目標(biāo)節(jié)點(diǎn),即target屬性指向的那個(gè)節(jié)點(diǎn)。
<3,事件處于冒泡階段,即處于從目標(biāo)節(jié)點(diǎn)向祖先節(jié)點(diǎn)的反向傳播過程中。該過程是從父節(jié)點(diǎn)一直到Window對(duì)象。只有bubbles屬性為true時(shí),這個(gè)階段才可能發(fā)生
2.與事件的默認(rèn)行為有關(guān):
cancelable:返回一個(gè)布爾值,表示事件是否可以取消。如果要取消某個(gè)事件,需要在這個(gè)事件上面調(diào)用preventDefault方法
defaultPrevented:返回一個(gè)布爾值,表示該事件是否調(diào)用過preventDefault方法。
3.與事件的目標(biāo)節(jié)點(diǎn)有關(guān):
currentTarget:返回事件執(zhí)行的監(jiān)聽函數(shù)所綁定的那個(gè)節(jié)點(diǎn)。
target:返回觸發(fā)事件的那個(gè)節(jié)點(diǎn)。在IE6—IE8之中,該屬性的名字不是target,而是srcElement
4.與事件對(duì)象的其他信息相關(guān):
type:返回一個(gè)字符串,表示事件類型
detail:返回一個(gè)數(shù)值,表示事件的某種信息。具體含義與事件類型有關(guān),對(duì)于鼠標(biāo)事件,表示鼠標(biāo)按鍵在某個(gè)位置按下的次數(shù),比如對(duì)于dblclick事件,detail屬性的值總是2
timeStamp:返回一個(gè)毫秒時(shí)間戳,表示事件發(fā)生的時(shí)間。從PerformanceTiming.navigationStart開始計(jì)算,即表示距離用戶導(dǎo)航至該網(wǎng)頁的時(shí)間。如果想將這個(gè)值轉(zhuǎn)為Unix紀(jì)元時(shí)間戳,就要計(jì)算event.timeStamp + performance.timing.navigationStart
isTrusted:返回一個(gè)布爾值,表示該事件是否可以信任。用處不大,不同瀏覽器的支持不一樣。
Event對(duì)象的方法:
preventDefault():取消瀏覽器對(duì)當(dāng)前事件的默認(rèn)行為,該方法生效的前提是,事件的cancelable屬性為true,如果為false,則調(diào)用該方法沒有任何效果。
stopPropagation():終止事件在傳播過程的捕獲、目標(biāo)處理或起泡階段進(jìn)一步傳播。調(diào)用該方法后,該節(jié)點(diǎn)上處理該事件的處理程序?qū)⒈徽{(diào)用,事件不再被分派到其他節(jié)點(diǎn)。注意:該方法不能阻止同一個(gè) Document 節(jié)點(diǎn)上的其他事件句柄被調(diào)用,但是它可以阻止把事件分派到其他節(jié)點(diǎn)
stopImmediatePropagation():阻止同一個(gè)事件的其他監(jiān)聽函數(shù)被調(diào)用,只要其中有一個(gè)監(jiān)聽函數(shù)調(diào)用了該方法,其他的監(jiān)聽函數(shù)就不會(huì)再執(zhí)行了。
參考鏈接:
http://javascript.ruanyifeng.com/dom/event.html#toc31
https://developer.mozilla.org/zh-CN/docs/Web/API
JavaScript權(quán)威指南第六版
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 一些主流JS框架中DOMReady事件的實(shí)現(xiàn)小結(jié)
- JQuery為頁面Dom元素綁定事件及解除綁定方法
- JavaScript DOM 添加事件
- IE之動(dòng)態(tài)添加DOM節(jié)點(diǎn)觸發(fā)window.resize事件
- javascript 刪除dom對(duì)象的事件函數(shù)代碼
- Javascript Event事件中IE與標(biāo)準(zhǔn)DOM的比較
- jQuery 選擇器、DOM操作、事件、動(dòng)畫
- Google Map V3 綁定氣泡窗口(infowindow)Dom事件實(shí)現(xiàn)代碼
- jquery中dom操作和事件的實(shí)例學(xué)習(xí) 下拉框應(yīng)用
- jquerydom對(duì)象的事件隱藏顯示和對(duì)象數(shù)組示例
相關(guān)文章
微信小程序?qū)崿F(xiàn)手勢(shì)滑動(dòng)卡片效果
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)手勢(shì)滑動(dòng)卡片效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08Svelte反應(yīng)式變量和函數(shù)工作原理詳解
這篇文章主要為大家介紹了Svelte反應(yīng)式變量和函數(shù)工作原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12JavaScript實(shí)現(xiàn)三種常用網(wǎng)頁特效(offset、client、scroll系列)
這篇文章主要為大家介紹了三種常用的PC端網(wǎng)頁特效:元素偏移量offset系列,元素可視區(qū)client系列和元素滾動(dòng)scroll系列,文中示例代碼介紹詳細(xì),需要的可以參考一下2021-12-12JavaScript 獲取事件對(duì)象的注意點(diǎn)
平時(shí)我們獲取事件對(duì)象一定要將firefox考慮進(jìn)去。2009-07-07在js里怎么實(shí)現(xiàn)Xcode里的callFuncN方法(詳解)
下面小編就為大家?guī)硪黄趈s里怎么實(shí)現(xiàn)Xcode里的callFuncN方法(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-11-11JS實(shí)現(xiàn)多物體運(yùn)動(dòng)的方法詳解
這篇文章主要介紹了JS實(shí)現(xiàn)多物體運(yùn)動(dòng)的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了javascript實(shí)現(xiàn)多物體運(yùn)動(dòng)的原理與相關(guān)操作技巧,需要的朋友可以參考下2018-01-01