JavaScript中事件處理


先看看下面一道題目,請評價以下代碼並給出改進意見:

if (window.addEventListener) {//標准瀏覽器
  var addListener = function(el, type, listener, useCapture) {
    el.addEventListener(type, listener, useCapture);
  };
} else if (document.all) {//IE
  addListener = function(el, type, listener) {
    el.attachEvent("on" + type, function() {
      listener.apply(el);
    });
  }
}

1)不應該在if和else語句中聲明addListener函數,應該先聲明;

2)不需要使用window.addEventListener或document.all來進行檢測瀏覽器,應該使用能力檢測;

3)attachEvent在IE中有this指向問題,會指向window,雖然上面的代碼做了指向處理,但是匿名函數不能做detachEvent解綁

改進后的代碼稍后加上。

 

一、冒泡與捕獲

使用過addEventListener方法的會發現最后一個參數“useCapture”,用於控制是捕獲還是冒泡。

何為冒泡、捕獲,請看下面的例子:查看在線代碼,在線代碼中用了三層。

<div id="click1">
  <div id="click2">事件</div>
</div>

1)Netscape主張元素1的事件首先發生,這種事件發生順序被稱為捕獲型,如下圖所示:

               | |
---------------| |-----------------
| click1       | |                |
|   -----------| |-----------     |
|   |click2    \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------

2)微軟則保持元素2具有優先權,這種事件順序被稱為冒泡型,如下圖所示:

               / \
---------------| |-----------------
| click1       | |                |
|   -----------| |-----------     |
|   |click2    | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------

3)W3C選擇了一個擇中的方案。任何發生在w3c事件模型中的事件,首是進入捕獲階段,直到達到目標元素,再進入冒泡階段,如下圖所示:

               | |  / \
---------------| |--| |------------
| click1       | |  | |           |
|   -----------| |--| |------     |
|   |click2    \ /  | |     |     |
|   -------------------------     |
|      W3C event model            |
-----------------------------------

4)阻止冒泡,很多時候是不想觸發父級的相同事件的,那么就需要阻止這種行為。

W3C方:event.stopPropagation()。(chrome、firefox、safrai等)

IE方:event.cancelBubble設置為true。

經過我的在線測試,stopPropagation這個方法不能阻止捕獲。

這里順帶說下阻止默認事件的方法,何為默認事件?就比如a標簽設置了href,就會做跳轉,這里阻止它跳轉。

W3C方:event.preventDefault(),但只有event的cancelable屬性為true時才能使用。

IE方:event.returnValue設置為false。

 

二、事件系統

瀏覽器提供了3種層次的API。

1)最原始的是寫在元素標簽內

2)以el.onXXX=function綁定的方式,通稱為DOM0事件系統。

3)一個元素的同一類型事件可以綁定多個回調,通稱為DOM2事件系統。

IE與W3C依舊不同,語法如下:

序號  操作與對象 IE方 W3C方

1

綁定事件

el.attachEvent("on"+type, callback) el.addEventListener(type,callback,[useCapture])

2

卸載事件

el.detachEvent("on"+type,callback) el.removeEventListener(type,callback,[useCapture])
3 創建事件 document.createEventObject()

document.createEvent(types) 創建事件(過時

event.initEvent() 初始化事件(過時

new Event(types)

4

派發事件

el.fireEvent(type,event) el.dispatchEvent(event)
5 event屬性 srcElement:等於target,默認目標

currentTarget:其事件處理程序當前正處理事件的元素

target:事件的目標

6 event方法

returnValue:等於preventDefault()

cancelBubble:設為true等於stopPropagation()

preventDefault():阻止默認行為

stopPropagation():阻止冒泡

7

type

被觸發的事件類型,需要“on”前綴 被觸發的事件類型

8

事件執行順序

與添加順序相反 與添加順序一致
9

匿名函數

無法移除 無法移除

10

this

window 當前綁定的元素

 

注意第8點,在測試代碼中,我綁定了兩個相同的“click”事件, 查看在線完整代碼

var func1 = function(e) {
  alert(1); //測試執行順序
};
var func2 = function(e) {
  alert(2);
};
var type = 'click';
bind(ele, type, func1);
bind(ele, type, func2);

在IE中先彈出2,再彈出1。而在chrome中先彈出1,再彈出2。

 

注意上面的第9點和第10點,上面那道題目中要解決的就是這個問題。下面的代碼是個片段,查看在線完整代碼

var bind = function(ele, type, callback) {
  if (!ele[type + "event"]) {
    ele[type + "event"] = {}; //聲明一個空對象,緩存事件
  }
  var name = callback.toString();
  if (!ele[type + "event"][name]) {
    var handler = function(event) {
      //可以做更多event封裝操作
      var ev = event || window.event;
      callback.call(ele, ev);
    };
    ele[type + "event"][name] = handler; //做個臨時變量
  }

  if (ele.addEventListener) {
    ele.addEventListener(type, handler, false);
  } else if (ele.attachEvent) {
    ele.attachEvent('on' + type, handler);
  }
}
var unbind = function(ele, type, callback) {
  var handler = ele[type + "event"][callback.toString()]; //讀取臨時變量
  if (ele.removeEventListener) {
    ele.removeEventListener(type, handler);
  } else if (ele.detachEvent) {
    ele.detachEvent('on' + type, handler);
  }
}

上面的代碼還比較粗糙,僅僅是用於演示一下。方法有很多,自己可以揣摩。

我看到網上有人直接不用attachEvent,將相應的事件保存在一個數組中,當detachEvent的時候做splice數組的操作。

大家也可以參考下一些成熟的類庫,例如jQuery1.8.3版本,2642行的event.add封裝,2757行的event.remove封裝,3485行的on封裝。

也可以參考Dean Wdwards寫的addEvent方法,這是Prototype時代早期出現的一個事件系統,jQuery事件系統的源頭。

 

參考資料:

前端事件系統(一)

JavaScript要點歸檔:事件

生動詳細解釋javascript的冒泡和捕獲

解決attachEvent中this指向問題

創建和觸發 events

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM