JS事件處理機(jī)制及事件代理(事件委托)實(shí)例詳解
一、先記個(gè)小知識(shí)點(diǎn)。cssText
cssText 本質(zhì):設(shè)置 HTML 元素的 style 屬性值。
用法:document.getElementById("d1").style.cssText= "color:red; font-size:13px;";
cssText 返回值:在某些瀏覽器中(比如 Chrome),你給他賦什么值,它就返回什么值。在 IE 中則比較痛苦,它會(huì)格式化輸出、會(huì)把屬性大寫、會(huì)改變屬性順序、會(huì)去掉最后一個(gè)分號(hào),比如:
cssText的使用優(yōu)勢(shì):樣式一多,代碼就很多;而且通過(guò)JS來(lái)覆寫對(duì)象的樣式是比較典型的一種銷毀原樣式并重建的過(guò)程,這種銷毀和重建,都會(huì)增加瀏覽器的開銷。語(yǔ)法為:obj.style.cssText=”樣式”;這樣就可以盡量避免頁(yè)面reflow,提高頁(yè)面性能。
但是,這樣會(huì)有一個(gè)問(wèn)題,會(huì)把原有的cssText清掉,比如原來(lái)的style中有’display:none;’,那么執(zhí)行完上面的JS后,display就被刪掉了。為了解決這個(gè)問(wèn)題,可以采用cssText累加的方法:
Element.style.cssText += 'width:100px;height:100px;top:100px;left:100px;'
注意:上面cssText累加的方法在IE中是無(wú)效的。解決辦法是,可以在前面添加一個(gè)分號(hào)來(lái)解決這個(gè)問(wèn)題:
Element.style.cssText += ';width:100px;height:100px;top:100px;left:100px;'
補(bǔ)充:如果前面有樣式表文件寫著 div {text-decoration:underline; },這個(gè)會(huì)被覆蓋嗎?不會(huì)!因?yàn)樗皇侵苯幼饔糜?HTML元素的 style 屬性。
二、JS的事件處理機(jī)制
1、事件流:指從頁(yè)面中接收事件的順序,有冒泡流和捕獲流。
2、DOM2級(jí)事件規(guī)定事件流包括三個(gè)階段:事件捕獲階段、處于目標(biāo)階段、事件冒泡階段。首先發(fā)生的是事件捕獲,然后是實(shí)際的目標(biāo)接收道事件,最后是冒泡階段,可以在這個(gè)階段對(duì)事件做出響應(yīng)。

分析:實(shí)際的(text)元素在捕獲階段不會(huì)接收到事件,意味著在捕獲階段,事件從document到<body>再到<div>后就停止了。下一個(gè)階段是“處于目標(biāo)階段”,于是事件在(text)上發(fā)生,并在事件處理中被看成是冒泡階段的一部分。最后,冒泡階段發(fā)生,事件又傳播回文檔。
3、事件處理程序
諸如click,load,mouseover都是事件的名字,而響應(yīng)某個(gè)事件的函數(shù)就是事件處理程序(事件偵聽(tīng)器)。事件處理程序的名字以on開頭,比如onclick.onmouseover等。
(1)HTML事件處理程序:某個(gè)元素支持的每種事件,都可以用一個(gè)相應(yīng)事件處理程序同名的HTML特性來(lái)決定。
<input type="button" value="click" οnclick="alert('clicked')"/><input type="button" value="click" οnclick="alert(event.type)"/>
第二動(dòng)態(tài)創(chuàng)建的函數(shù)中會(huì)有一個(gè)局部變量event,也就是事件對(duì)象。通過(guò)event變量,可以直接訪問(wèn)事件對(duì)象。
另外,這個(gè)動(dòng)態(tài)創(chuàng)建的函數(shù)擴(kuò)展作用域的方式如下:使用with
在這個(gè)函數(shù)內(nèi)部,可以像訪問(wèn)局部變量一樣訪問(wèn)document及該元素本身的成員。
function(){
with(documnet){
with(this){
/元素屬性值
}
}
}(2)DOM0級(jí)事件處理程序
基于DOM0的事件,對(duì)于同一個(gè)dom節(jié)點(diǎn)而言,只能注冊(cè)一個(gè),后邊注冊(cè)的 同種事件 會(huì)覆蓋之前注冊(cè)的。利用這個(gè)原理我們可以解除事件,btn5.onclick=null;其中this就是綁定事件的那個(gè)元素;
這里添加的事件處理程序是在其依附的元素的作用域中運(yùn)行。
DOM0級(jí)對(duì)每個(gè)事件只支持一個(gè)事件處理程序。
(3)DOM2級(jí)事件處理程序
DOM2支持同一dom元素注冊(cè)多個(gè)同種事件,事件發(fā)生的順序按照添加的順序依次觸發(fā)(IE是相反的)。DOM2事件通過(guò)addEventListener和removeEventListener管理。 DOM2級(jí)事件定義了兩個(gè)方法,用于處理指定和刪除事件處理程序的操作:addEventListener(eventName,handlers,boolean)和removeEventListener(),兩個(gè)方法都一樣接收三個(gè)參數(shù), 要處理的事件名,第二個(gè)是 事件處理程序函數(shù),第三個(gè)值為 布爾值。
布爾值是true,表示在捕獲階段調(diào)用事件處理程序。false時(shí)表示在事件冒泡階段調(diào)用事件處理程序,一般建議在冒泡階段使用,特殊情況才在捕獲階段; 注意:通過(guò)addEventListener() 添加的事件處理程序只能用removeEventListener() 來(lái)移除,并且移除時(shí)傳入的參數(shù)必須與添加時(shí)傳入的參數(shù)一樣;通過(guò)addEventListener()添加的匿名函數(shù)將無(wú)法移除。(js高程P351-P352)
使用DOM2級(jí)事件處理程序可以添加多個(gè)事件處理程序:
var btn2 = document.getElementById('btn2');
var handlers = function () {
console.log(this.id);
};
btn2.addEventListener('click',handlers,false);
btn2.addEventListener("click",function(){alert("hello")},false);
btn2.removeEventListener('click',handlers.false);這里為按鈕添加了兩個(gè)事件處理程序,他們會(huì)按照添加他們的順序觸發(fā)。
(4)IE事件處理程序
IE用了attachEvent(),和detachEvent(),接收兩個(gè)參數(shù),事件名稱和事件處理程序函數(shù)。由于IE8及以前只支持事件冒泡;通過(guò)attachEvent()添加的事件處理程序都會(huì)被添加到冒泡階段。所以平時(shí)為了兼容更多的瀏覽器最好將事件添加到事件冒泡階段。
var btn3 = document.getElementById('btn3');
var handlers2=function(){
console.log(this===window);//true,注意attachEvent()添加的事件處理程序運(yùn)行在全局作用域中;
};
?btn3.attachEvent('onclick',handlers2);分析:attachEvent()的第一個(gè)參數(shù)是“onclick”DOM則是“click”
重點(diǎn):在使用attachEvent()方法的情況下,事件處理程序會(huì)在全局作用域中運(yùn)行。因此this等于window。
attachEvent()也可以為同一元素添加兩個(gè)不同的事件處理程序。只是執(zhí)行事件時(shí)以相反的順序被觸發(fā)。
(5)跨瀏覽器事件處理程序
為了以跨瀏覽器的方式處理事件,有兩個(gè)方法,addHandler(),它的職責(zé)是視情況分別使用DOM0和DOM2或者IE方法來(lái)添加或刪除事件。這個(gè)方法屬于一個(gè)名叫EventUtil的對(duì)象,可以處理瀏覽器差異。這個(gè)方法接收三個(gè)參數(shù)。要操作的元素、事件名稱、和事件處理程序函數(shù)。對(duì)應(yīng)的方法是removeHandler()函數(shù),它的職責(zé)是移除事件處理程序。默認(rèn)采用DOM0級(jí)方法。
4 事件對(duì)象
觸發(fā)DOM上的某個(gè)事件時(shí),會(huì)產(chǎn)生一個(gè)事件對(duì)象event,這個(gè)對(duì)象中包含了所有與事件有關(guān)的信息,比如導(dǎo)致事件的元素target,事件的類型,及其他特定的相關(guān)信息。例如鼠標(biāo)操作導(dǎo)致的事件對(duì)象中會(huì)包含鼠標(biāo)的位置,單雙擊等,而鍵盤操作導(dǎo)致的事件對(duì)象會(huì)包含按下的鍵等信息;
事件被觸發(fā)時(shí),會(huì)默認(rèn)給事件處理程序傳入一個(gè)參數(shù)e , 表示事件對(duì)象;通過(guò)e,我們可以獲得其中包含的與事件有關(guān)的信息; 只有在事件處理程序執(zhí)行期間,event對(duì)象才會(huì)存在,一旦事件處理程序執(zhí)行完畢,event對(duì)象就會(huì)被銷毀;
(1)DOM中的事件對(duì)象
兼容DOM的瀏覽器會(huì)自動(dòng)將一個(gè)事件對(duì)象event傳遞給事件處理程序。
在通過(guò)HTML特性指定事件處理函數(shù)時(shí),變量event中保存著event對(duì)象。event對(duì)象包含與創(chuàng)建它的特定事件的有關(guān)的屬性和方法。觸發(fā)的事件類型不一樣,可用的屬性和方法也不一樣。
| currentTarget | 只讀 | 事件處理程序當(dāng)前正在處理事件的那個(gè)元素 |
| datail | 只讀 | 與事件相關(guān)的細(xì)節(jié) |
| eventPhase | 只讀 | 調(diào)用事件處理程序的階段1 捕獲階段 2 處于目標(biāo) 3 冒泡階段 |
| target | 只讀 | 事件的目標(biāo) |
| type | 只讀 | 被觸發(fā)的事件的類型 |
在事件處理程序內(nèi)部,對(duì)象this始終等于currentTarget的值,target包含事件的實(shí)際目標(biāo)。
ps:關(guān)于事件對(duì)象中的this,target,currentTarget,看個(gè)例子:(注:event.target不支持IE瀏覽器,應(yīng)該用event.srcElement;還有 IE中通過(guò)attachment添加的事件是運(yùn)行在全局作用域中的,this===window。
| preventDefault() 阻止事件的默認(rèn)行為,只有cancelabel屬性的值設(shè)為true時(shí),才可以使用preventDefalut. |
| event.stopPropagation()可以阻止事件的傳播.,取消進(jìn)一步的事件冒泡或者捕獲 |
(2)IE中的事件對(duì)象
要訪問(wèn)IE的event對(duì)象有幾種不同的方式,取決于指定事件處理程序的方法。
比如使用DOM0級(jí)方法添加事件處理程序時(shí),event對(duì)象作為window對(duì)象的一個(gè)屬性存在,因此可以通過(guò)window.event來(lái)訪問(wèn)event對(duì)象。
var btn=document.getElementById("myBtn");
btn.οnclick=function(){
var event=window.event;
alert(event.type);
}輸出結(jié)果是click.
如果是使用attachEvent()來(lái)添加事件處理程序,那么會(huì)有一個(gè)對(duì)象作為參數(shù)傳入事件處理程序函數(shù)中。
var btn=document.getElementById("myBtn");
btn.attachEvent("onclick",function(event){
alert(event.type);
});輸出結(jié)果是click.
IE中event對(duì)象同樣包含與創(chuàng)建它的事件相關(guān)的方法和屬性。其中很多屬性和方法都有對(duì)應(yīng)的或者相關(guān)的DOM屬性和方法。這些屬性和方法會(huì)因?yàn)槭骂愋偷牟煌煌?/p>
| srcElement | 只讀 | 事件的目標(biāo)(與DOM中target屬性相同) |
| type | 只讀 | 被觸發(fā)的事件的類型 |
| cancelBubble | 讀/寫 | 默認(rèn)值為false,設(shè)置為true可以取消事件冒泡(與DOM中stopPropagation()一樣) |
| returnValue | 讀/寫 | 默認(rèn)為true,設(shè)置為false,就可以阻止默認(rèn)行為。(與DOM中的preventDefault()一樣) |
| 將returnValue設(shè)置為false,就可以阻止默認(rèn)行為。 |
| cancelBubble屬性值為true,可以取消事件冒泡。 |
(3)跨瀏覽器的事件對(duì)象
雖然DOM和IE中對(duì)象不同,但基于二者之間的相似性依舊可以拿出跨瀏覽器的方案來(lái)。
IE中的event中的全部信息和方法都是類似的只是實(shí)現(xiàn)方式不同,可以用前面提到過(guò)的EventUtil對(duì)象來(lái)求同存異。
var EventUtil(){
addHandler:function(element,type,handler){
//省略代碼
},
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;
}
?},
removeHandler:function(element,type,handler){
//省略代碼
},
stopPropagation:function(event){
?if(event.stopPropagation){
event.preventDefault();
}else{
event.cancelBubble=true; }
}
};
以上代碼為EventUtil 添加了4個(gè)方法;getEvent(),返回event對(duì)象的引用。其它方法類似。
5 事件委托
因?yàn)槊芭輽C(jī)制,比如既然點(diǎn)擊子元素,也會(huì)觸發(fā)父元素的點(diǎn)擊事件,那我們完全可以將子元素的事件要做的事寫到父元素的事件里,也就是將子元素的事件處理程序?qū)懙礁冈氐氖录幚沓绦蛑?,這就是事件委托;利用事件委托,只指定一個(gè)事件處理程序,就可以管理某一個(gè)類型的所有事件;
通俗來(lái)說(shuō):事件委托是利用事件的冒泡原理來(lái)實(shí)現(xiàn)的,何為事件冒泡呢?就是事件從最深的節(jié)點(diǎn)開始,然后逐步向上傳播事件,舉個(gè)例子:頁(yè)面上有這么一個(gè)節(jié)點(diǎn)樹,div>ul>li>a;比如給最里面的a加一個(gè)click點(diǎn)擊事件,那么這個(gè)事件就會(huì)一層一層的往外執(zhí)行,執(zhí)行順序a>li>ul>div,有這樣一個(gè)機(jī)制,那么我們給最外面的div加點(diǎn)擊事件,那么里面的ul,li,a做點(diǎn)擊事件的時(shí)候,都會(huì)冒泡到最外層的div上,所以都會(huì)觸發(fā),這就是事件委托,委托它們父級(jí)代為執(zhí)行事件。
示例1:
<ul>
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
實(shí)現(xiàn)點(diǎn)擊li出現(xiàn)123.
傳統(tǒng)方法:
window.onload = function(){
var oUl = document.getElementById("ul1");
var aLi = oUl.getElementsByTagName('li');
for(var i=0;i<aLi.length;i++){
aLi[i].onclick = function(){
alert(123);
}
}
}使用事件委托:
window.onload = function(){
var oUl = document.getElementById("ul1");
oUl.onclick = function(){
alert(123);
}
}這里用父級(jí)ul做事件處理,當(dāng)li被點(diǎn)擊時(shí),由于冒泡原理,事件就會(huì)冒泡到ul上,因?yàn)閡l上有點(diǎn)擊事件,所以事件就會(huì)觸發(fā),當(dāng)然,這里當(dāng)點(diǎn)擊ul的時(shí)候,也是會(huì)觸發(fā)的,那么問(wèn)題就來(lái)了,如果我想讓事件代理的效果跟直接給節(jié)點(diǎn)的事件效果一樣怎么辦,比如說(shuō)只有點(diǎn)擊li才會(huì)觸發(fā)???
示例2:
Event對(duì)象提供了一個(gè)屬性叫target,可以返回事件的目標(biāo)節(jié)點(diǎn),我們成為事件源,也就是說(shuō),target就可以表示為當(dāng)前的事件操作的dom,但是不是真正操作dom,當(dāng)然,這個(gè)是有兼容性的,標(biāo)準(zhǔn)瀏覽器用ev.target,IE瀏覽器用event.srcElement。
window.onload = function(){
var oUl = document.getElementById("ul1");
oUl.onclick = function(ev){
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == 'li'){
alert(123);
alert(target.innerHTML);
}
}
}這樣,只有點(diǎn)擊li才會(huì)觸發(fā)事件。
示例3
對(duì)比下列兩段代碼實(shí)現(xiàn):
window.onload = function(){
var oBtn = document.getElementById("btn");
var oUl = document.getElementById("ul1");
var aLi = oUl.getElementsByTagName('li');
var num = 4;
//鼠標(biāo)移入變紅,移出變白
for(var i=0; i<aLi.length;i++){
aLi[i].onmouseover = function(){
this.style.background = 'red';
};
aLi[i].onmouseout = function(){
this.style.background = '#fff';
}
}
//添加新節(jié)點(diǎn)
oBtn.onclick = function(){
num++;
var oLi = document.createElement('li');
oLi.innerHTML = 111*num;
oUl.appendChild(oLi);
};
}注意:這里添加的新節(jié)點(diǎn)并不會(huì)有事件處理程序。
window.onload = function(){
var oBtn = document.getElementById("btn");
var oUl = document.getElementById("ul1");
var aLi = oUl.getElementsByTagName('li');
var num = 4;
//事件委托,添加的子元素也有事件
oUl.onmouseover = function(ev){
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == 'li'){
target.style.background = "red";
}
};
oUl.onmouseout = function(ev){
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == 'li'){
target.style.background = "#fff";
}
};
//添加新節(jié)點(diǎn)
oBtn.onclick = function(){
num++;
var oLi = document.createElement('li');
oLi.innerHTML = 111*num;
oUl.appendChild(oLi);
};
}用事件委托的方式,新添加的子元素是帶有事件效果的,我們可以發(fā)現(xiàn),當(dāng)用事件委托的時(shí)候,根本就不需要去遍歷元素的子節(jié)點(diǎn),只需要給父級(jí)元素添加事件就好了,其他的都是在js里面的執(zhí)行,這樣可以大大的減少dom操作,這才是事件委托的精髓所在。
示例4: 點(diǎn)擊某一個(gè) Li 標(biāo)簽時(shí),將 Li 的背景色顯示在 P 標(biāo)簽內(nèi),并將 P 標(biāo)簽中的文字顏色設(shè)置成 Li 的背景色
傳統(tǒng)實(shí)現(xiàn):
var list = document.querySelectorAll("li");
for (var i = 0, len = list.length; i < len; i++) {
list[i].onclick = function(e) {
var t = e.target;
var c = t.style.backgroundColor;
var p = document.getElementsByClassName("color-picker")[0];
p.innerHTML = c;
p.style.color = c;
}
}運(yùn)用事件委托:
var ulist=document.getElementsByClassName("palette")[0];
ulist.οnclick=function(ev){
var ev = ev || window.event;
var target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() === 'li') {
var c = target.style.backgroundColor;
var p = document.getElementsByClassName("color-picker")[0];
p.innerHTML = c;
p.style.color = c;
}
}注意:ul只有一個(gè),要用索引,[0],如果不寫,無(wú)法實(shí)現(xiàn)。
總結(jié)一下js委托相關(guān)的:
- 因?yàn)榘咽录壎ǖ搅烁腹?jié)點(diǎn)上,因此省了綁定事件。就算后面新增的子節(jié)點(diǎn)也有了相關(guān)事件,刪除部分子節(jié)點(diǎn)不用去銷毀對(duì)應(yīng)節(jié)點(diǎn)上綁定的事件
- 父節(jié)點(diǎn)是通過(guò)event.target來(lái)找對(duì)應(yīng)的子節(jié)點(diǎn)的。(事件處理程序中的this值始終等于currentTarget的值,指向的是綁定到的那個(gè)元素)
相關(guān)文章
JavaScript實(shí)現(xiàn)點(diǎn)擊按鈕字體放大、縮小
字體可以調(diào)節(jié)大小,極大了滿足了用戶體驗(yàn)度,接下來(lái)通過(guò)本文給大家介紹JavaScript實(shí)現(xiàn)點(diǎn)擊按鈕字體放大、縮小實(shí)例代碼,需要的朋友參考下吧2016-02-02
使用AJAX實(shí)現(xiàn)Web頁(yè)面進(jìn)度條的實(shí)例分享
這篇文章主要介紹了使用AJAX實(shí)現(xiàn)Web頁(yè)面進(jìn)度條的實(shí)例分享,利用AJAX的異步來(lái)顯示服務(wù)器端的處理進(jìn)度是當(dāng)下比較流行的做法,需要的朋友可以參考下2016-05-05
JS高級(jí)運(yùn)動(dòng)實(shí)例分析
這篇文章主要介紹了JS高級(jí)運(yùn)動(dòng),結(jié)合實(shí)例形式分析了javascript運(yùn)動(dòng)框架原理、實(shí)現(xiàn)與應(yīng)用技巧,需要的朋友可以參考下2016-12-12
Webpack打包時(shí)將文件內(nèi)聯(lián)方法實(shí)現(xiàn)
本文主要介紹了Webpack打包時(shí)將文件內(nèi)聯(lián)方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
如何檢測(cè)JavaScript中的死循環(huán)示例詳解
這篇文章主要給大家介紹了關(guān)于如何檢測(cè)JavaScript中死循環(huán)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
讓低版本瀏覽器支持input的placeholder屬性(js方法)
低版本瀏覽器一般都不會(huì)支持input的placeholder屬性,接下來(lái)使用js實(shí)現(xiàn)下,感興趣的朋友可以參考下哈2013-04-04
在Firefox下js select標(biāo)簽點(diǎn)擊無(wú)法彈出
在Firefox下js select標(biāo)簽點(diǎn)擊無(wú)法彈出,在IE和CHROME下沒(méi)有此現(xiàn)象2014-03-03
JavaScript調(diào)用瀏覽器打印功能實(shí)例分析
這篇文章主要介紹了JavaScript調(diào)用瀏覽器打印功能的方法,實(shí)例分析了針對(duì)各種常用瀏覽器調(diào)用打印功能的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
JavaScript的原型存在的安全問(wèn)題及解決辦法
JavaScript的原型很多人都知道也很好用,但是很多人在使用原型繼承中導(dǎo)致的安全問(wèn)題卻很少人知道,接下來(lái)我們就來(lái)好好了解一下,感興趣的小伙伴跟著小編一起來(lái)看看吧2023-08-08

