轉(zhuǎn)帖|其它|編輯:郝浩|2011-08-11 15:24:48.000|閱讀 542 次
概述:本文主要對Javascript事件進(jìn)行了一次深入的總結(jié),希望對大家有幫助。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
一、事件冒泡
1.1 事件的不同階段
Javascript事件在2個(gè)階段執(zhí)行:捕獲與冒泡。
如下圖的Dom結(jié)構(gòu)中如果指向錨點(diǎn)#1.1的鏈接被點(diǎn)擊,則依次會(huì)觸發(fā)document > body > ul > li > ul > li > a 的Click處理函數(shù)。至此完成捕獲階段。當(dāng)這階段完成,開始冒泡階段,如圖中向上箭頭的順序。事件處理函數(shù)全部觸發(fā)。有興趣可以移步,可以看到動(dòng)態(tài)的過程。
我們對上述代碼稍加更改,假如alert,因?yàn)槟莻€(gè)demo中的效果切換太快了,我們慢一點(diǎn)洗洗體會(huì)。【注:這里訂閱的事件都是冒泡階段的,也是最 常用的,因?yàn)镮E并不支持訂閱捕獲階段的時(shí)間。比較特殊的還有Opera,常常遇到有些特性向Firefox系,偶爾會(huì)有個(gè)別特性像IE】。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"//www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="//www.w3.org/1999/xhtml" lang="zh" xml:lang="zh">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="developer" content="Realazy" />
<title>Bubble in JavaScript DOM</title>
<style type="text/css" media="screen">
div * {display:block; margin:4px; padding:4px; border:1px solid white;}
textarea {width:20em; height:2em;}
</style>
<script type="text/javascript">
//<![CDATA[
function init(){
var log = document.getElementsByTagName('textarea')[0];
var all = document.getElementsByTagName('div')[0].getElementsByTagName('*');
for (var i = 0, n = all.length; i < n; ++i){
all[i].onmouseover = function(e){
alert('鼠標(biāo)現(xiàn)在進(jìn)入的是: ' + this.nodeName);
this.style.border = '1px solid red';
log.value = '鼠標(biāo)現(xiàn)在進(jìn)入的是: ' + this.nodeName;
};
all[i].onmouseout = function(e){
alert('鼠標(biāo)現(xiàn)在移出的是: ' + this.nodeName);
this.style.border = '1px solid white';
};
}
var all2 = document.getElementsByTagName('div')[1].getElementsByTagName('*');
for (var i = 0, n = all2.length; i < n; ++i){
all2[i].onmouseover = function(e){
this.style.border = '1px solid red';
if (e) //停止事件冒泡
e.stopPropagation();
else
window.event.cancelBubble = true;
log.value = '鼠標(biāo)現(xiàn)在進(jìn)入的是: ' + this.nodeName;
};
all2[i].onmouseout = function(e){
this.style.border = '1px solid white';
};
}
}
window.onload = init;
//]]>
</script>
</head>
<body>
<h1>Bubble in JavaScript DOM</h1>
<p>DOM樹的結(jié)構(gòu)是:</p>
<pre><code>
UL
- LI
- A
- SPAN
</code></pre>
<div>
<ul>
<li><a href="#"><span>Bubbllllllllllllllle</span></a></li>
<li><a href="#"><span>Bubbllllllllllllllle</span></a></li>
</ul>
</div>
<textarea></textarea>
<p>鼠標(biāo)進(jìn)入U(xiǎn)L的任何一個(gè)子元素,如果不停止冒泡,我們從UL到SPAN都定義了鼠標(biāo)懸停(<code>mouseover</code>)事件,這個(gè)事件會(huì)上升了UL,從而從鼠標(biāo)所進(jìn)入的元素到UL元素都會(huì)有紅色的邊。</p>
<div>
<ul>
<li><a href="#"><span>Bubbllllllllllllllle</span></a></li>
<li><a href="#"><span>Bubbllllllllllllllle</span></a></li>
</ul>
</div>
<p>如果停止冒泡,事件不會(huì)上升,我們就可以獲取精確的鼠標(biāo)進(jìn)入元素。</p>
</body>
</html>
1.2 取消事件冒泡
其實(shí)1.1的代碼中已經(jīng)包含了取消事件冒泡的代碼。這里我們專門提出來寫,使其具有更好的兼容性與美觀。
1 function stopBubble(e) {
2 if (e && e.stopPropagation) {
3 e.stopPropagation(); //因?yàn)閭魅肓耸录ο骵,并且支持W3C標(biāo)準(zhǔn)的stopPropagation()
4 } else {
5 window.event.cancelBubble = true; //For IE
6 }
7 }
【注】:我們不能簡單的看到傳入了事件對象就判斷為非IE瀏覽器,因?yàn)橛袝r(shí)候我們使用3.1的方式來綁定事件,此時(shí)極有可能也會(huì)傳入一個(gè)window.event的引用。
1.3 重載瀏覽器默認(rèn)行為
對于a標(biāo)簽等具有默認(rèn)行為(如跳轉(zhuǎn)到某URL)的HTML元素,我們可能想要部分a表現(xiàn)的有特色些,點(diǎn)擊某a就是不跳轉(zhuǎn),可以重載其默認(rèn)行為。
function stopDetault(e) {
if (e&&e.preventDetault) {
e.preventDefault();
} else {
window.event.returnValue = false;
}
return false;
}
使用方法:
document.getElementById("##").onclick = function (e) {
//do sth.
return stopDetault(e);
}
我們也常用下面的方式阻止默認(rèn)行為,所以阻止事件處理函數(shù)本身return false也就可以理解了。
<a href="javascript:alert('clicked');return false;">a link without redirect action</a>
《Pro Javascript Techniques》[美 John Resig]一書中提到95%的情況中防止默認(rèn)行為都有效,但是偶爾也會(huì)失效,因?yàn)樵撔袨槭怯蔀g覽器決定的,尤其是在文本域中防止敲擊和iframe內(nèi)的 行為。除此之外,都應(yīng)該無大礙。這是一本學(xué)習(xí)javascript的好書。推薦。
二、 常見事件對象
2.1 this
this 關(guān)鍵字是javascript中提供對當(dāng)前對象引用的變量。綁定事件時(shí)this通常指的是當(dāng)前元素,但是也有例外!!初接觸javascript覺得這個(gè)this有點(diǎn)變換莫測,難以捉摸。
如果你的感覺也是這樣,可以看看下邊的文章:
通常在綁定事件時(shí)可以這樣使用this
document.getElementById("input1").onclick = function(e){
this.style.color="Red";
};
2.2 事件對象
通過對下面的代碼調(diào)試我們可以看到,事件對象通常包含當(dāng)前鍵碼等事件相關(guān)信息。值得一提的是IE的實(shí)現(xiàn)把事件對象放在一個(gè)全局變量window.event變量中保存,而其他遵從W3C標(biāo)準(zhǔn)瀏覽器則作為一個(gè)參數(shù)傳進(jìn)處理函數(shù)。
三、 事件綁定
各瀏覽器雖然支持的方式都不太一樣,但是相比混亂的CSS,事件綁定還是有章可循的。IE有自己的實(shí)現(xiàn)方式,并且各版本統(tǒng)一,其他現(xiàn)代瀏覽器都按照W3C標(biāo)準(zhǔn)來實(shí)現(xiàn)。
3.1 傳統(tǒng)Dom綁定
這種方式最簡單,最有效。而且this關(guān)鍵詞指向的是當(dāng)前元素。但是缺點(diǎn)也不少,他們是:
1.只能綁定一次。假如我們引用的多個(gè)類庫中都對window.onload事件進(jìn)行綁定,則前邊的綁定將會(huì)被后邊的覆蓋,并且常常難以察覺。
2.只支持訂閱冒泡階段事件。
3.事件參數(shù)只支持非IE,雖然事件對象參數(shù)僅支持非IE瀏覽器,但是我們可以使用下邊這種方式解決。
<a onclick="handle(event)" href="#">link</a>
3.2 W3C標(biāo)準(zhǔn)綁定
這個(gè)是最開心的方式了,除IE以外的現(xiàn)代瀏覽器都支持,我們可以直接使用每個(gè)dom元素的 addEventListener(eventName,handleFunc,trueOrFalse),第一個(gè)參數(shù)為訂閱的事件名稱,如 click(沒有on),第二個(gè)參數(shù)為時(shí)間處理函數(shù),第三個(gè)參數(shù)為是否訂閱事件捕獲階段。
下面是使用addEventListener的例子
document.getElementById("linkA").addEventListener('click', function (e) {
alert('i am clicked!');
return stopDetault(e);
}, false);
優(yōu)點(diǎn):
1.支持冒泡與捕獲階段。
2.在處理函數(shù)內(nèi)部,this關(guān)鍵字引用當(dāng)前元素。
3.可以為同一元素的同一時(shí)間綁定多個(gè)處理函數(shù),不會(huì)覆蓋。
缺點(diǎn):
1.IE不支持
3.3 IE綁定
既然上邊提到IE不支持addEventListener,那么肯定要找個(gè)解決方案幫幫IE小兄弟,那就是attachEvent,雖然有很多缺點(diǎn),但也夠用。
下面是attachEvent的例子
document.attachEvent("onload", function () {
alert("i am load");
});
優(yōu)點(diǎn):
1.當(dāng)然這個(gè)優(yōu)點(diǎn)是和第一種綁定方式相比的:),同一元素支持多次綁定。
缺點(diǎn):
1.僅支持IE事件的冒泡階段。
2.事件處理函數(shù)內(nèi)部this關(guān)鍵字引用了window對象。要解決這個(gè)問題請繼續(xù)往下看。
3.事件名前必須加on,當(dāng)然這個(gè)只是叫法不同,也沒什么大礙。
4.只支持IE啊,這個(gè)很痛啊。
四、 牛人們的解決方案
Dean Edwards的方案:addEvent/removeEvent庫
這個(gè)方案比較特別。詳細(xì)請移步
特點(diǎn): 1 it performs no object detection
2 it does not use the addeventListener/attachEvent methods
3 it keeps the correct scope (the this keyword)
4 it passes the event object correctly
5 it is entirely cross-browser (it will probably work on IE4 and NS4)
6 and from what I can tell it does not leak memory
源碼:
function handleEvent(event) {
var returnValue = true;
// grab the event object (IE uses a global event object)
event = event || fixEvent(window.event);
// get a reference to the hash table of event handlers
var handlers = this.events[event.type];
// execute each event handler
for (var i in handlers) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
}
}
return returnValue;
};
function fixEvent(event) {
// add W3C standard event methods
event.preventDefault = fixEvent.preventDefault;
event.stopPropagation = fixEvent.stopPropagation;
return event;
};
fixEvent.preventDefault = function() {
this.returnValue = false;
};
fixEvent.stopPropagation = function() {
this.cancelBubble = true;
};
【注】:這個(gè)方案有一個(gè)致命的東西,千萬不要這樣做這樣會(huì)覆蓋之前綁定的處理函數(shù)
<body onload="alert('hi');"></body>
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:博客園