詳解javascript中的事件處理
一.事件傳播機(jī)制
客戶端JavaScript程序(就是瀏覽器啦)采用了異步事件驅(qū)動編程模型。當(dāng)文檔、瀏覽器、元素或與之相關(guān)的對象發(fā)生某些有趣的事情時,Web瀏覽器就會產(chǎn)生事件(event)。如果JavaScript應(yīng)用程序關(guān)注特定類型的事件,那么它可以注冊當(dāng)這類事件發(fā)生時要調(diào)用的一個或多個函數(shù)。當(dāng)然了,這種風(fēng)格并非Web編程獨(dú)有,所有使用圖形用戶界面的應(yīng)用程序都采用了它。
既然要詳解事件處理,那我們先從幾個基礎(chǔ)概念說起吧:
①事件類型(event type):是一個用來說明發(fā)生什么類型事件的字符串。例如,“mousemove”表示用戶移動鼠標(biāo),“keydown”表示鍵盤上某個鍵被按下。事件類型只是一個字符串,有時候又稱之為事件名字(event name);
②事件目標(biāo)(event target):是發(fā)生事件或與之相關(guān)的對象。Window、Document和Element對象是最常見的事件目標(biāo)。當(dāng)然,AJAX中的XMLHttpRequest對象也是一個事件目標(biāo);
③事件處理程序(event handler):是處理或響應(yīng)事件的函數(shù),它也叫事件監(jiān)聽程序(event listener)。應(yīng)用程序通過指明事件類型和事件目標(biāo),在Web瀏覽器中注冊它們的事件處理函數(shù)。
④事件對象(event object):是與特定事件相關(guān)且包含有關(guān)該事件詳細(xì)信息的對象。事件對象作為參數(shù)傳遞給事件處理函數(shù)(但是在IE8以及其之前版本中,全局變量event才是事件對象)。事件對象都有用來指定事件類型(event type)的type屬性和指定事件目標(biāo)(event target)的target屬性(但是在IE8以及其之前版本中,用的是srcElement而非target)。當(dāng)然,不同類型的事件還會為其相關(guān)事件對象定義一些其他的獨(dú)有屬性。例如,鼠標(biāo)事件的相關(guān)對象會包含鼠標(biāo)指針的坐標(biāo),而鍵盤事件的相關(guān)對象會包含按下的鍵和輔助鍵的詳細(xì)信息。
以上說完了四個基本概念。那么問題來了——如果在一個web頁面上用鼠標(biāo)點(diǎn)擊一個元素a的某一子元素b時,應(yīng)該先執(zhí)行子元素b注冊的事件處理程序還是先執(zhí)行元素a注冊的事件處理程序呢(假設(shè)元素a和它的子元素b都有注冊事件處理程序)?身為讀者的你是否想過這個問題呢?
這個問題就涉及到瀏覽器中的事件傳播(event propagation)機(jī)制。相信大家都聽說過事件冒泡(event bubble)和事件捕獲(event capturing)吧!沒錯,它們就是瀏覽器中的事件傳播機(jī)制。無圖無真相,沒有配圖?那怎么闊以:
看了圖之后相信你已經(jīng)大概理解了瀏覽器中的事件傳播機(jī)制了:當(dāng)一個事件發(fā)生時,它會先從瀏覽器頂級對象Window一路向下傳遞,一直傳遞到觸發(fā)這個事件的那個元素,這也就是事件捕獲過程。然而,一切并沒有結(jié)束,事件又從這個元素一路向上傳遞到Window對象,這也就是事件冒泡過程(但是在IE8以及其之前版本中,事件模型并未定義捕獲過程,只有冒泡過程)。
所以,關(guān)于上面的問題,還得看元素a注冊的事件處理程序是在捕獲過程還是在冒泡過程了。那么到底什么是在捕獲過程注冊事件處理程序,在冒泡過程注冊事件處理程序又是怎么做的呢?這就得好好說說幾種注冊事件處理程序的方式了:
1. 設(shè)置HTML標(biāo)簽屬性為事件處理程序
文檔元素的事件處理程序?qū)傩?,其名字由“on”后面跟著事件名組成,例如:onclick、onmouseover。當(dāng)然了,這種形式只能為DOM元素注冊事件處理程序。實(shí)例:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow:hidden;} #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;} #div3{margin:50px auto; width: 100px; height: 100px; background: blue;} </style> </head> <body> <div id="div1" onClick="console.log('div1');">div1 <div id="div2" oNClick="console.log('div2');">div2 <div id="div3" onclick="console.log('div3');" onclick="console.log('div3333');">div3 </div> </div> </div> <script type="text/javascript"> </script> </body> </html>
結(jié)果(鼠標(biāo)點(diǎn)擊div3區(qū)域后):
從結(jié)果中可以看出:
?、僖?yàn)镠TML里面不區(qū)分大小寫,所以這里事件處理程序?qū)傩悦髮?、小寫、大小混寫均可,屬性值就是相?yīng)事件處理程序的JavaScript代碼;
?、谌艚o同一元素寫多個onclick事件處理屬性,瀏覽器只執(zhí)行第一個onclick里面的代碼,后面的會被忽略;
?、圻@種形式是在事件冒泡過程中注冊事件處理程序的;
2.設(shè)置JavaScript對象屬性為事件處理程序
可以通過設(shè)置某一事件目標(biāo)的事件處理程序?qū)傩詠頌槠渥韵鄳?yīng)的事件處理程序。事件處理程序?qū)傩悦钟伞皁n”后面跟著事件名組成,例如:onclick、onmouseover。實(shí)例:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow:hidden;} #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;} #div3{margin:50px auto; width: 100px; height: 100px; background: blue;} </style> </head> <body> <div id="div1">div1 <div id="div2">div2 <div id="div3">div3 </div> </div> </div> <script type="text/javascript"> var div1 = document.getElementById('div1'); var div2 = document.getElementById('div2'); var div3 = document.getElementById('div3'); div1.onclick = function(){ console.log('div1'); }; div2.onclick = function(){ console.log('div2'); }; div3.onclick = function(){ console.log('div3'); }; div1.onclick = function(){ console.log('div11111'); }; div1.onClick = function(){ console.log('DIV11111'); }; </script> </body> </html>
結(jié)果(鼠標(biāo)點(diǎn)擊div3區(qū)域后):
從結(jié)果中可以看出:
?、僖?yàn)镴avaScript是嚴(yán)格區(qū)分大小寫的,所以,這種形式下屬性名只能按規(guī)定小寫;
?、谌艚o同一元素對象寫多個onclick事件處理屬性,后面寫的會覆蓋前面的(ps:這就是在修改一個對象屬性的值,屬性的值是唯一確定的);
?、圻@種形式也是在事件冒泡過程中注冊事件處理程序的;
3.addEventListener()
前兩種方式出現(xiàn)在Web初期,眾多瀏覽器都有實(shí)現(xiàn)。而addEventListener()方法是標(biāo)準(zhǔn)事件模型中定義的。任何能成為事件目標(biāo)的對象——這些對象包括Window對象、Document對象和所有文檔元素等——都定義了一個名叫addEventListener()的方法,使用這個方法可以為事件目標(biāo)注冊事件處理程序。addEventListener()接受三個參數(shù):第一個參數(shù)是要注冊處理程序的事件類型,其值是字符串,但并不包括前綴“on”;第二個參數(shù)是指當(dāng)指定類型的事件發(fā)生時應(yīng)該調(diào)用的函數(shù);第三個參數(shù)是布爾值,其可以忽略(某些舊的瀏覽器上不能忽略這個參數(shù)),默認(rèn)值為false。這種情況是在事件冒泡過程中注冊事件處理程序。當(dāng)其為true時,就是在事件捕獲過程中注冊事件處理程序。實(shí)例:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow:hidden;} #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;} #div3{margin:50px auto; width: 100px; height: 100px; background: blue;} </style> </head> <body> <div id="div1">div1 <div id="div2">div2 <div id="div3">div3 </div> </div> </div> <script type="text/javascript"> var div1 = document.getElementById('div1'); var div2 = document.getElementById('div2'); var div3 = document.getElementById('div3'); div1.addEventListener('click', function(){ console.log('div1-bubble'); }, false); div2.addEventListener('click', function(){ console.log('div2-bubble'); }, false); div3.addEventListener('click', function(){ console.log('div3-bubble'); }, false); div3.addEventListener('click', function(){ console.log('div3-bubble222'); }, false); div1.addEventListener('click', function(){ console.log('div1-capturing'); }, true); div2.addEventListener('click', function(){ console.log('div2-capturing'); }, true); div3.addEventListener('click', function(){ console.log('div3-capturing'); }, true); </script> </body> </html>
結(jié)果(鼠標(biāo)點(diǎn)擊div3區(qū)域后):
從結(jié)果中可以看出:
?、賏ddEventListener()第三個參數(shù)的作用正如上面所說;
?、谕ㄟ^addEventListener()方法給同一對象注冊多個同類型的事件,并不會發(fā)生忽略或覆蓋,而是會按順序依次執(zhí)行;
相對addEventListener()的是removeEventListener()方法,它同樣有三個參數(shù),前兩個參數(shù)自然跟addEventListener()的意義一樣,而第三個參數(shù)也只需跟相應(yīng)的addEventListener()的第三個參數(shù)保持一致即可,同樣可以省略,默認(rèn)值為false。它表示從對象中刪除某個事件處理函數(shù)。實(shí)例:
div1.addEventListener('click', div1BubbleFun, false); div1.removeEventListener('click', div1BubbleFun, false); function div1BubbleFun(){ console.log('div1-bubble'); }
4.attachEvent()
但是,IE8以及其之前版本的瀏覽器并不支持addEventListener()和removeEventListener()。相應(yīng)的,IE定義了類似的方法attachEvent()和detachEvent()。因?yàn)镮E8以及其之前版本瀏覽器也不支持事件捕獲,所以attachEvent()并不能注冊捕獲過程中的事件處理函數(shù),因此attachEvent()和detachEvent()要求只有兩個參數(shù):事件類型和事件處理函數(shù)。而且,它們的第一個參數(shù)使用了帶“on”前綴的事件處理程序?qū)傩悦?。?shí)例:
var div1 = document.getElementById('div1'); div1.attachEvent('onclick', div1BubbleFun); function div1BubbleFun(){ console.log('div1-bubble'); }
相應(yīng)的,從對象上刪除事件處理程序函數(shù)使用detachEvent()。例如:
div1.detachEvent('onclick', div1BubbleFun);
到此為止,我們已經(jīng)說了瀏覽器中事件傳播機(jī)制以及各種注冊事件處理程序的方法。下面我們就再說說事件處理程序調(diào)用時的一些問題吧!
二.事件處理程序的調(diào)用
1.事件處理程序的參數(shù):正如前面所說,通常事件對象作為參數(shù)傳遞給事件處理函數(shù),但I(xiàn)E8以及其之前版本的瀏覽器中全局變量event才是事件對象。所以,我們在寫相關(guān)代碼時應(yīng)該注意兼容性問題。實(shí)例(給頁面上id為div1的元素添加點(diǎn)擊事件,當(dāng)點(diǎn)擊該元素時在控制臺輸出事件類型和被點(diǎn)擊元素本身):
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow: hidden;} </style> </head> <body> <div id="div1">div1</div> <script type="text/javascript"> var div1 = document.getElementById('div1'); if(div1.addEventListener){ div1.addEventListener('click', div1Fun, false); }else if(div1.attachEvent){ div1.attachEvent('onclick', div1Fun); } function div1Fun(event){ event = event || window.event; var target = event.target || event.srcElement; console.log(event.type); console.log(target); } </script> </body> </html>
2.事件處理程序的運(yùn)行環(huán)境:關(guān)于事件處理程序的運(yùn)行環(huán)境,也就是在事件處理程序中調(diào)用上下文(this值)的指向問題,可以看下面四個實(shí)例。
實(shí)例一:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow: hidden;} </style> </head> <body> <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div> <script type="text/javascript"> </script> </body> </html>
結(jié)果一:
從結(jié)果可以看出:
?、俚谝环N方法事件處理程序中this指向這個元素本身;
實(shí)例二:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow: hidden;} </style> </head> <body> <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div> <script type="text/javascript"> var div1 = document.getElementById('div1'); div1.onclick = function(){ console.log('div1.onclick:'); console.log(this); }; </script> </body> </html>
結(jié)果二:
從結(jié)果可以看出:
?、俚诙N方法事件處理程序中this也指向這個元素本身;
?、诖嬖诘诙N方法時,它會覆蓋第一種方法注冊的事件處理程序;
實(shí)例三:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow: hidden;} </style> </head> <body> <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div> <script type="text/javascript"> var div1 = document.getElementById('div1'); div1.onclick = function(){ console.log('div1.onclick:'); console.log(this); }; div1.addEventListener('click', function(){ console.log('div1.addEventListener:'); console.log(this); }, false); </script> </body> </html>
結(jié)果三:
從結(jié)果可以看出:
?、俚谌N方法事件處理程序中this也指向這個元素本身;
②第三種方法并不會覆蓋第一種或第二種方法注冊的事件處理程序;
實(shí)例四:
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow: hidden;} </style> </head> <body> <div id="div1" onclick="console.log('html:'); console.log(this);">div1</div> <script type="text/javascript"> var div1 = document.getElementById('div1'); div1.onclick = function(){ console.log('div1.onclick:'); console.log(this); }; div1.attachEvent('onclick', function(){ console.log('div1.attachEvent:'); console.log(this === window); }); </script> </body> </html>
結(jié)果四:
從結(jié)果可以看出:
①第四種方法事件處理程序中this指向全局對象Window;
?、诘谒姆N方法也不會覆蓋第一種或第二種方法注冊的事件處理程序;
3.事件處理程序的調(diào)用順序:多個事件處理程序調(diào)用規(guī)則如下:
①通過HTML屬性注冊的處理程序和通過設(shè)置對象屬性的處理程序一直優(yōu)先調(diào)用;
?、谑褂胊ddEventListener()注冊的處理程序按照它們的注冊順序依次調(diào)用;
?、凼褂胊ttachEvent()注冊的處理程序可能按照任何順序調(diào)用,所以代碼不應(yīng)該依賴于調(diào)用順序;
4.事件取消:
①取消事件的瀏覽器默認(rèn)操作(比如點(diǎn)擊超鏈接元素會自動發(fā)生頁面跳轉(zhuǎn)的默認(rèn)操作):如果使用前兩種方法注冊事件處理程序,可以在處理程序中添加返回值false來取消事件的瀏覽器默認(rèn)操作。在支持addEventListener()的瀏覽器中,也可以通過調(diào)用事件對象的preventDefault()方法取消事件的默認(rèn)操作。至于IE8及其之前的瀏覽器可以通過設(shè)置事件對象的returnValue屬性為false來取消事件的默認(rèn)操作。參考代碼:
function cancelHandler(event){ var event = event || window.event; if(event.preventDefault){ event.preventDefault(); } if(event.returnValue){ event.returnValue = false; } return false; }
②取消事件傳播:在支持addEventListener()的瀏覽器中,可以調(diào)用事件對象的一個stopPropagation()方法阻止事件的繼續(xù)傳播,它能工作在事件傳播期間的任何階段(捕獲期階段、事件目標(biāo)本身、冒泡階段);但是在IE8以及其之前版本的瀏覽器中并不支持stopPropagation()方法,而且這些瀏覽器也不支持事件傳播的捕獲階段,相應(yīng)的,IE事件對象有一個cancelBubble屬性,設(shè)置這個屬性為true能阻止事件進(jìn)一步傳播(即阻止其冒泡)。參考代碼(阻止發(fā)生在div3區(qū)域的點(diǎn)擊事件冒泡到div2和div1):
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>test</title> <style type="text/css"> #div1{width: 300px; height: 300px; background: red; overflow:hidden;} #div2{margin:50px auto; width: 200px; height: 200px; background: green; overflow:hidden;} #div3{margin:50px auto; width: 100px; height: 100px; background: blue;} </style> </head> <body> <div id="div1">div1 <div id="div2">div2 <div id="div3">div3 </div> </div> </div> <script type="text/javascript"> var div1 = document.getElementById('div1'); var div2 = document.getElementById('div2'); var div3 = document.getElementById('div3'); div1.onclick = function(){ console.log('div1'); }; div2.onclick = function(){ console.log('div2'); }; div3.onclick = function(event){ stopEventPropagation(event); console.log('div3'); }; function stopEventPropagation(event){ var event = event || window.event; if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true; } } </script> </body> </html>
以上就是javascript事件處理的全部介紹,當(dāng)然,關(guān)于事件冒泡還是有可利用之處的,這也就是我們常說的事件代理或者事件委托,對于這方面知識以后會陸續(xù)更新。
- 用JavaScript事件串連執(zhí)行多個處理過程的方法
- JavaScript中的事件處理
- JavaScript入門教程(11) js事件處理
- Ext javascript建立超鏈接,進(jìn)行事件處理的實(shí)現(xiàn)方法
- js event事件的傳遞與冒泡處理
- javascript 處理事件綁定的一些兼容寫法
- 淺談Javascript事件處理程序的幾種方式
- js onload處理html頁面加載之后的事件
- 引用其它js時如何同時處理多個window.onload事件
- 輕松創(chuàng)建nodejs服務(wù)器(5):事件處理程序
- 詳細(xì)解讀JavaScript的跨瀏覽器事件處理
- JavaScript事件處理的方式(三種)
相關(guān)文章
直接在JS里創(chuàng)建JSON數(shù)據(jù)然后遍歷使用
本節(jié)為大家講解下直接在JS里創(chuàng)建JSON數(shù)據(jù),然后遍歷使用,需要的朋友可以參考下2014-07-07JavaScript指定斷點(diǎn)操作實(shí)例教程
這篇文章主要給大家介紹了關(guān)于JavaScript指定斷點(diǎn)操作的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09JavaScript基礎(chǔ)之運(yùn)算符與表達(dá)式詳解
這篇文章主要為大家詳細(xì)介紹了JavaScript中一些常見的運(yùn)算符與表達(dá)式的具體使用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-04-04div當(dāng)滾動到頁面頂部的時候固定在頂部實(shí)例代碼
使用Javascript實(shí)現(xiàn)了滾動頁面時,DIV到達(dá)頂部時固定在頂部。在IE下效果有點(diǎn)閃,具體代碼如下,感興趣的朋友可以參考下哈2013-05-05JS如何獲取未來n天的時間(返回日期:yyyy-mm-dd,并且判斷是否是今天和星期)
開發(fā)中經(jīng)常遇到獲取時間的業(yè)務(wù),將常用的方法做個筆記記錄下,對JS如何獲取未來n天的時間相關(guān)知識感興趣的朋友一起看看吧2024-03-03javascript removeChild 使用注意事項(xiàng)
removeChild的注意事項(xiàng)。大家可以參考下。2009-04-04