Javascript事件總結

 

一、事件冒泡

1.1 事件的不同階段

Javascript事件在2個階段執行:捕獲與冒泡。

 

如下圖的Dom結構中如果指向錨點#1.1的鏈接被點擊,則依次會觸發document > body > ul > li > ul > li > a 的Click處理函數。至此完成捕獲階段。當這階段完成,開始冒泡階段,如圖中向上箭頭的順序。事件處理函數全部觸發。有興趣可以移步這裡,可以看到動態的過程。

 

 

我們對上述代碼稍加更改,假如alert,因為那個demo中的效果切換太快瞭,我們慢一點洗洗體會。【註:這裡訂閱的事件都是冒泡階段的,也是最常用的,因為IE並不支持訂閱捕獲階段的時間。比較特殊的還有Opera,常常遇到有些特性向Firefox系,偶爾會有個別特性像IE】。這篇文章也助於加深理解

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

 

 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html xmlns="http://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">

 

 p * {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('p')[0].getElementsByTagName('*');

 

  for (var i = 0, n = all.length; i < n; ++i){

 

   all[i].onmouseover = function(e){

alert('鼠標現在進入的是:' + this.nodeName);

    this.style.border = '1px solid red';

 

 

 

    log.value = '鼠標現在進入的是:' + this.nodeName;

 

   };

 

   all[i].onmouseout = function(e){

alert('鼠標現在移出的是:' + this.nodeName);

    this.style.border = '1px solid white';

 

   };

 

  }

 

 

 

  var all2 = document.getElementsByTagName('p')[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 = '鼠標現在進入的是:' + 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樹的結構是:</p>

 

<pre><code>

 

UL

 

  – LI

 

     – A

 

   – SPAN

 

</code></pre>

 

<p>

 

 <ul>

 

  <li><a href="#"><span>Bubbllllllllllllllle</span></a></li>

 

  <li><a href="#"><span>Bubbllllllllllllllle</span></a></li>

 

 </ul>

 

</p>

 

<textarea></textarea>

 

<p>鼠標進入UL的任何一個子元素,如果不停止冒泡,我們從UL到SPAN都定義瞭鼠標懸停(<code>mouseover</code>)事件,這個事件會上升瞭UL,從而從鼠標所進入的元素到UL元素都會有紅色的邊。</p>

 

<p>

 

 <ul>

 

  <li><a href="#"><span>Bubbllllllllllllllle</span></a></li>

 

  <li><a href="#"><span>Bubbllllllllllllllle</span></a></li>

 

 </ul>

 

</p>

 

<p>如果停止冒泡,事件不會上升,我們就可以獲取精確的鼠標進入元素。</p>

 

</body>

 

</html>

 

 

1.2 取消事件冒泡

其實1.1的代碼中已經包含瞭取消事件冒泡的代碼。這裡我們專門提出來寫,使其具有更好的兼容性與美觀。

 

1 function stopBubble(e) {2     if (e && e.stopPropagation) {3         e.stopPropagation(); //因為傳入瞭事件對象e,並且支持W3C標準的stopPropagation()4     } else {5         window.event.cancelBubble = true; //For IE6     }7 }         

 

【註】:我們不能簡單的看到傳入瞭事件對象就判斷為非IE瀏覽器,因為有時候我們使用3.1的方式來綁定事件,此時極有可能也會傳入一個window.event的引用。

 

1.3 重載瀏覽器默認行為

對於a標簽等具有默認行為(如跳轉到某URL)的HTML元素,我們可能想要部分a表現的有特色些,點擊某a就是不跳轉,可以重載其默認行為。

 

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);}

我們也常用下面的方式阻止默認行為,所以阻止事件處理函數本身return false也就可以理解瞭。

 

<a href="javascript:alert('clicked');return false;">a link without redirect action</a>  

 

《Pro Javascript Techniques》[美John Resig]一書中提到95%的情況中防止默認行為都有效,但是偶爾也會失效,因為該行為是由瀏覽器決定的,尤其是在文本域中防止敲擊和iframe內的行為。除此之外,都應該無大礙。這是一本學習javascript的好書。推薦。

 

二、 常見事件對象

2.1 this

this 關鍵字是javascript中提供對當前對象引用的變量。綁定事件時this通常指的是當前元素,但是也有例外!!初接觸javascript覺得這個this有點變換莫測,難以捉摸。

 

如果你的感覺也是這樣,可以看看下邊的文章:

 

Javascript this關鍵字使用分析

[圖解] 你不知道的JavaScript – “this”

通常在綁定事件時可以這樣使用this

 

document.getElementById("input1").onclick = function(e){    this.style.color="Red";};    

 

2.2 事件對象

通過對下面的代碼調試我們可以看到,事件對象通常包含當前鍵碼等事件相關信息。值得一提的是IE的實現把事件對象放在一個全局變量window.event變量中保存,而其他遵從W3C標準瀏覽器則作為一個參數傳進處理函數。

e

 

 

 

 

三、 事件綁定

各瀏覽器雖然支持的方式都不太一樣,但是相比混亂的CSS,事件綁定還是有章可循的。IE有自己的實現方式,並且各版本統一,其他現代瀏覽器都按照W3C標準來實現。

 

3.1 傳統Dom綁定

這種方式最簡單,最有效。而且this關鍵詞指向的是當前元素。但是缺點也不少,他們是:

1.隻能綁定一次。假如我們引用的多個類庫中都對window.onload事件進行綁定,則前邊的綁定將會被後邊的覆蓋,並且常常難以察覺。

2.隻支持訂閱冒泡階段事件。

3.事件參數隻支持非IE,雖然事件對象參數僅支持非IE瀏覽器,但是我們可以使用下邊這種方式解決。

 

<a onclick="handle(event)" href="#">link</a>3.2 W3C標準綁定

這個是最開心的方式瞭,除IE以外的現代瀏覽器都支持,我們可以直接使用每個dom元素的addEventListener(eventName,handleFunc,trueOrFalse),第一個參數為訂閱的事件名稱,如click(沒有on),第二個參數為時間處理函數,第三個參數為是否訂閱事件捕獲階段。

 

下面是使用addEventListener的例子

 

document.getElementById("linkA").addEventListener('click', function (e) {    alert('i am clicked!');    return stopDetault(e);}, false);優點:

1.支持冒泡與捕獲階段。

2.在處理函數內部,this關鍵字引用當前元素。

3.可以為同一元素的同一時間綁定多個處理函數,不會覆蓋。

缺點:

1.IE不支持

 

3.3 IE綁定

既然上邊提到IE不支持addEventListener,那麼肯定要找個解決方案幫幫IE小兄弟,那就是attachEvent,雖然有很多缺點,但也夠用。

 

下面是attachEvent的例子

 

document.attachEvent("onload", function () {    alert("i am load");});  

 

優點:

1.當然這個優點是和第一種綁定方式相比的:),同一元素支持多次綁定。

缺點:

1.僅支持IE事件的冒泡階段。

2.事件處理函數內部this關鍵字引用瞭window對象。要解決這個問題請繼續往下看。

3.事件名前必須加on,當然這個隻是叫法不同,也沒什麼大礙。

4.隻支持IE啊,這個很痛啊。

 

四、 牛人們的解決方案

Dean Edwards的方案:addEvent/removeEvent庫

這個方案比較特別。詳細請移步這裡

 

特點:1 it performs no object detection2 it does not use the addeventListener/attachEvent methods3 it keeps the correct scope (the this keyword)4 it passes the event object correctly5 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;};  

 

【註】:這個方案有一個致命的東西,千萬不要這樣做這樣會覆蓋之前綁定的處理函數

 

<body onload="alert('hi');"></body>

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *