JS之事件

JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的趣斤。事件土至,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬間》成可以使用偵聽器(或處理程序)來預(yù)訂事件臂港,以便事件發(fā)生時執(zhí)行相應(yīng)的代碼森枪。

一、事件流

事件流描述的是從頁面中接收事件的順序审孽。

1县袱、事件冒泡

IE 的事件流叫做事件冒泡(event bubbling),即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節(jié)點)接收佑力,然后逐級向上傳播到較為不具體的節(jié)點(文檔)式散。

<!DOCTYPE html>
<html>
<head>
    <title>Event Bubbling Example</title>
</head>
<body>
    <div id="myDiv">Click Me</div>
</body>
</html>
//如果你單擊了頁面中的 <div> 元素,那么這個 click 事件會按照如下順序傳播:
(1)  <div>
(2)  <body>
(3)  <html>
(4)  document

所有現(xiàn)代瀏覽器都支持事件冒泡搓萧,但在具體實現(xiàn)上存在一些差別杂数。

2、事件捕獲

事件捕獲的思想是不太具體的節(jié)點應(yīng)該更早接收到事件瘸洛,而最具體的節(jié)點應(yīng)該最后接收到事件揍移。事件捕獲的用意在于在事件到達預(yù)定目標之前捕獲它。
事件捕獲觸發(fā)的 click 事件:

(1) document
(2) <html>
(3) <body>
(4) <div>

由于老版本的瀏覽器不支持反肋,因此很少有人使用事件捕獲那伐。

3、DOM事件流

“DOM2級事件”規(guī)定的事件流包括三個階段:事件捕獲階段石蔗、處于目標階段和事件冒泡階段罕邀。首先發(fā)生的是事件捕獲,為截獲事件提供了機會养距。然后是實際的目標接收到事件诉探。最后一個階段是冒泡階段,可以在這個階段對事件做出響應(yīng)棍厌。


二肾胯、事件處理程序

響應(yīng)某個事件的函數(shù)就叫做事件處理程序(或事件偵聽器)。事件處理程序的名字以 "on" 開頭耘纱,因此click 事件的事件處理程序就是 onclick 敬肚, load 事件的事件處理程序就是 onload 。

1束析、HTML事件處理程序

某個元素支持的每種事件艳馒,都可以使用一個與相應(yīng)事件處理程序同名的 HTML 特性來指定。這個特性的值應(yīng)該是能夠執(zhí)行的 JavaScript 代碼员寇。

// HTML 中定義的事件
<input type="button" value="Click Me" onclick="alert('Clicked')" />
//調(diào)用腳本形式
<input type="button" value="Click Me" onclick="showMessage()" />
<script type="text/javascript">
function showMessage(){
    alert("Hello world!");
}
</script>

2弄慰、DOM0級事件處理程序

通過 JavaScript 指定事件處理程序的傳統(tǒng)方式第美,就是將一個函數(shù)賦值給一個事件處理程序?qū)傩浴_@種為事件處理程序賦值的方法是在第四代 Web 瀏覽器中出現(xiàn)的曹动,而且至今仍然為所有現(xiàn)代瀏覽器所支持斋日。原因一是簡單牲览,二是具有跨瀏覽器的優(yōu)勢墓陈。要使用 JavaScript 指定事件處理程序,首先必須取得一個要操作的對象的引用第献。

每個元素(包括 window 和 document )都有自己的事件處理程序?qū)傩怨北兀@些屬性通常全部小寫,例如 onclick 庸毫。將這種屬性的值設(shè)置為一個函數(shù)仔拟,就可以指定事件處理程序。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert("Clicked");
};

通過文檔對象取得了一個按鈕的引用飒赃,然后為它指定了 onclick 事件處理程序利花。

使用 DOM0 級方法指定的事件處理程序被認為是元素的方法。因此载佳,這時候的事件處理程序是在元素的作用域中運行炒事;換句話說,程序中的 this 引用當(dāng)前元素蔫慧。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id); //"myBtn"
};

不僅僅是 ID挠乳,實際上可以在事件處理程序中通過 this 訪問元素的任何屬性和方法。以這種方式添加的事件處理程序會在事件流的冒泡階段被處理姑躲。

也可以刪除通過 DOM0 級方法指定的事件處理程序睡扬。

btn.onclick = null; //刪除事件處理程序

將事件處理程序設(shè)置為 null 之后,再單擊按鈕將不會有任何動作發(fā)生黍析。

3卖怜、DOM2級事件處理程序

“DOM2級事件”定義了兩個方法,用于處理指定和刪除事件處理程序的操作: addEventListener()和 removeEventListener() 阐枣。所有 DOM 節(jié)點中都包含這兩個方法马靠,并且它們都接受 3 個參數(shù):要處理的事件名、作為事件處理程序的函數(shù)和一個布爾值侮繁。最后這個布爾值參數(shù)如果是 true 虑粥,表示在捕獲階段調(diào)用事件處理程序;如果是 false 宪哩,表示在冒泡階段調(diào)用事件處理程序娩贷。

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
    alert(this.id);
}, false);

通過 addEventListener() 添加的事件處理程序只能使用 removeEventListener() 來移除;移除時傳入的參數(shù)與添加處理程序時使用的參數(shù)相同锁孟。這也意味著通過 addEventListener() 添加的匿名函數(shù)將無法移除彬祖。

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
    alert(this.id);
}, false);
// 匿名函數(shù)
btn.removeEventListener("click", function(){ // 沒有用茁瘦!
    alert(this.id);
}, false);

var handler = function(){
    alert(this.id);
};
btn.addEventListener("click", handler, false);
// 傳入函數(shù)名
btn.removeEventListener("click", handler, false); // 有效!

傳入 removeEventListener() 中的事件處理程序函數(shù)必須與傳入addEventListener() 中的相同储笑。

4甜熔、IE事件處理程序

IE 實現(xiàn)了與 DOM 中類似的兩個方法: attachEvent() 和 detachEvent() 。這兩個方法接受相同的兩個參數(shù):事件處理程序名稱與事件處理程序函數(shù)突倍。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
    alert("Clicked");
});

注意腔稀, attachEvent() 的第一個參數(shù)是 "onclick" ,而非 DOM 的 addEventListener() 方法中的 "click" 羽历。

在 IE 中使用 attachEvent() 與使用 DOM0 級方法的主要區(qū)別在于事件處理程序的作用域焊虏。在使用 DOM0 級方法的情況下,事件處理程序會在其所屬元素的作用域內(nèi)運行秕磷;在使用 attachEvent() 方法的情況下诵闭,事件處理程序會在全局作用域中運行,因此 this 等于 window 澎嚣。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
    alert(this === window); //true
});

使用 attachEvent() 添加的事件可以通過 detachEvent() 來移除疏尿,條件是必須提供相同的參數(shù)。與 DOM 方法一樣易桃,這也意味著添加的匿名函數(shù)將不能被移除褥琐。不過,只要能夠?qū)ο嗤瘮?shù)的引用傳給 detachEvent() 颈抚,就可以移除相應(yīng)的事件處理程序踩衩。

var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
btn.attachEvent("onclick", handler);
// 移除相應(yīng)事件
btn.detachEvent("onclick", handler);

5、跨瀏覽器的事件處理程序

addHandler() 方法接受 3 個參數(shù):要操作的元素贩汉、事件名稱和事件處理程序函數(shù)驱富。它的職責(zé)是視情況分別使用 DOM0 級方法、DOM2 級方法或 IE 方法來添加事件匹舞。這個方法屬于一個名叫 EventUtil 的對象褐鸥。

與 addHandler() 對應(yīng)的方法是 removeHandler() ,它也接受相同的參數(shù)赐稽。這個方法的職責(zé)是移除之前添加的事件處理程序叫榕。

var EventUtil = {
    addHandler: function(element, type, handler) {
        if (element.addEventListener) {
            element.addEventListener(type, handler, false);
        } else if (element.attachEvent) {
            element.attachEvent("on" + type, handler);
        } else {
            element["on" + type] = handler;
        }
    },
    removeHandler: function(element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false);
        } else if (element.detachEvent) {
            element.detachEvent("on" + type, handler);
        } else {
            element["on" + type] = null;
        }
    }
};

使用 EventUtil 對象:

var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
// 這里省略了其他代碼
EventUtil.removeHandler(btn, "click", handler);

三、事件對象

在觸發(fā) DOM 上的某個事件時姊舵,會產(chǎn)生一個事件對象 event 晰绎,這個對象中包含著所有與事件有關(guān)的信息。包括導(dǎo)致事件的元素括丁、事件的類型以及其他與特定事件相關(guān)的信息荞下。

1、DOM中的事件對象

兼容 DOM 的瀏覽器會將一個 event 對象傳入到事件處理程序中。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
    alert(event.type); //"click"
}, false);
//通過 HTML 特性指定事件處理程序時尖昏,變量 event 中保存著 event 對象
<input type="button" value="Click Me" onclick="alert(event.type)"/>

event 對象包含與創(chuàng)建它的特定事件有關(guān)的屬性和方法仰税。觸發(fā)的事件類型不一樣,可用的屬性和方法也不一樣抽诉。



在事件處理程序內(nèi)部陨簇,對象 this 始終等于 currentTarget 的值,而 target 則只包含事件的實際目標迹淌。如果直接將事件處理程序指定給了目標元素河绽,則 this 、 currentTarget 和 target 包含相同的值巍沙。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.currentTarget === this); //true
    alert(event.target === this); //true
};
//事件處理程序存在于按鈕的父節(jié)點中
document.body.onclick = function(event){
    alert(event.currentTarget === document.body); //true
    alert(this === document.body); //true
    alert(event.target === document.getElementById("myBtn")); //true
};

在需要通過一個函數(shù)處理多個事件時葵姥,可以使用 type 屬性荷鼠。

var btn = document.getElementById("myBtn");
var handler = function(event) {
    switch (event.type) {
        case "click":
            alert("Clicked");
            break;
        case "mouseover":
            event.target.style.backgroundColor = "red";
            break;
        case "mouseout":
            event.target.style.backgroundColor = "";
            break;
    }
};
btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

要阻止特定事件的默認行為句携,可以使用 preventDefault() 方法。例如允乐,阻止鏈接的默認行為:

var link = document.getElementById("myLink");
link.onclick = function(event){
    event.preventDefault();
};

只有 cancelable 屬性設(shè)置為 true 的事件矮嫉,才可以使用 preventDefault() 來取消其默認行為。

stopPropagation() 方法用于立即停止事件在 DOM 層次中的傳播牍疏,即取消進一步的事件捕獲或冒泡蠢笋。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert("Clicked");
    event.stopPropagation();
};
document.body.onclick = function(event){
    alert("Body clicked");
};

如果不調(diào)用 stopPropagation() ,就會在單擊按鈕時出現(xiàn)兩個警告框鳞陨。

事件對象的 eventPhase 屬性昨寞,可以用來確定事件當(dāng)前正位于事件流的哪個階段。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.eventPhase); //2
};
document.body.addEventListener("click", function(event){
    alert(event.eventPhase); //1
}, true);
document.body.onclick = function(event){
    alert(event.eventPhase); //3
};

提示:只有在事件處理程序執(zhí)行期間厦滤, event 對象才會存在援岩;一旦事件處理程序執(zhí)行完成, event 對象就會被銷毀掏导。

2享怀、IE中的事件對象

與訪問 DOM 中的 event 對象不同,要訪問 IE 中的 event 對象有幾種不同的方式趟咆,取決于指定事件處理程序的方法添瓷。

在使用 DOM0 級方法添加事件處理程序時, event 對象作為 window 對象的一個屬性存在值纱。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    var event = window.event;
    alert(event.type); //"click"
};

如果事件處理程序是使用 attachEvent() 添加的鳞贷,那么就會有一個 event 對象作為參數(shù)被傳入事件處理程序函數(shù)中。

var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(event){
    alert(event.type); //"click"
});

如果是通過HTML特性指定的事件處理程序虐唠,那么還可以通過一個名叫 event 的變量來訪問 event對象(與 DOM中的事件模型相同)搀愧。

<input type="button" value="Click Me" onclick="alert(event.type)">

IE 的 event 對象同樣也包含與創(chuàng)建它的事件相關(guān)的屬性和方法。



因為事件處理程序的作用域是根據(jù)指定它的方式來確定的,所以不能認為 this 會始終等于事件目標妈橄。故而庶近,最好還是使用 event.srcElement 比較保險。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(window.event.srcElement === this);  //true
};
btn.attachEvent("onclick", function(event){
    alert(event.srcElement === this);  //false
});

returnValue 屬性相當(dāng)于 DOM中的 preventDefault() 方法眷蚓,作用都是取消
給定事件的默認行為鼻种。只要將 returnValue 設(shè)置為 false ,就可以阻止默認行為沙热。

var link = document.getElementById("myLink");
link.onclick = function(){
    window.event.returnValue = false;
};

cancelBubble 屬性與 DOM 中的 stopPropagation() 方法作用相同叉钥,都是用來停止事件冒泡的。但 stopPropagatioin() 可以同時取消事件捕獲和冒泡篙贸。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert("Clicked");
    window.event.cancelBubble = true;
};
document.body.onclick = function(){
    alert("Body clicked");
}; 

3投队、跨瀏覽器的事件對象

雖然 DOM 和 IE 中的 event 對象不同,但基于它們之間的相似性依舊可以拿出跨瀏覽器的方案來爵川。

var EventUtil = {
    addHandler: function(element, type, handler) {
        //省略的代碼
    },
   //返回對 event對象的引用敷鸦。
    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.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }
};

var btn = document.getElementById("myBtn");
// getEvent()的使用方法
btn.onclick = function(event) {
    event = EventUtil.getEvent(event);
};
// getTarget()的使用方法
btn.onclick = function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
};
//取消事件的默認行為
var link = document.getElementById("myLink");
link.onclick = function(event) {
    event = EventUtil.getEvent(event);
    EventUtil.preventDefault(event);
};
//
btn.onclick = function(event) {
    alert("Clicked");
    event = EventUtil.getEvent(event);
    EventUtil.stopPropagation(event);
};
document.body.onclick = function(event) {
    alert("Body clicked");
};

stopPropagation()方法由于 IE 不支持事件捕獲,因此這個方法在跨瀏覽器的情況下寝贡,只能用來阻止事件冒泡扒披。

四、事件類型

“DOM3級事件”規(guī)定了以下幾類事件:

  • UI(User Interface圃泡,用戶界面)事件碟案,當(dāng)用戶與頁面上的元素交互時觸發(fā);
  • 焦點事件颇蜡,當(dāng)元素獲得或失去焦點時觸發(fā)价说;
  • 鼠標事件,當(dāng)用戶通過鼠標在頁面上執(zhí)行操作時觸發(fā)风秤;
  • 滾輪事件鳖目,當(dāng)使用鼠標滾輪(或類似設(shè)備)時觸發(fā);
  • 文本事件唁情,當(dāng)在文檔中輸入文本時觸發(fā)疑苔;
  • 鍵盤事件,當(dāng)用戶通過鍵盤在頁面上執(zhí)行操作時觸發(fā)甸鸟;
  • 合成事件惦费,當(dāng)為 IME(Input Method Editor,輸入法編輯器)輸入字符時觸發(fā)抢韭;
  • 變動(mutation)事件薪贫,當(dāng)?shù)讓?DOM 結(jié)構(gòu)發(fā)生變化時觸發(fā)。
  • 變動名稱事件刻恭,當(dāng)元素或?qū)傩悦儎訒r觸發(fā)瞧省。此類事件已經(jīng)被廢棄

1扯夭、UI事件

UI 事件指的是那些不一定與用戶操作有關(guān)的事件。

  • load :當(dāng)頁面完全加載后在 window 上面觸發(fā)鞍匾,當(dāng)所有框架都加載完畢時在框架集上面觸發(fā)交洗,當(dāng)圖像加載完畢時在 <img> 元素上面觸發(fā),或者當(dāng)嵌入的內(nèi)容加載完畢時在 <object> 元素上面觸發(fā)橡淑。
  • unload :當(dāng)頁面完全卸載后在 window 上面觸發(fā)构拳,當(dāng)所有框架都卸載后在框架集上面觸發(fā),或者當(dāng)嵌入的內(nèi)容卸載完畢后在 <object> 元素上面觸發(fā)梁棠。
  • abort :在用戶停止下載過程時置森,如果嵌入的內(nèi)容沒有加載完,則在 <object> 元素上面觸發(fā)符糊。
  • error :當(dāng)發(fā)生 JavaScript 錯誤時在 window 上面觸發(fā)凫海,當(dāng)無法加載圖像時在 <img> 元素上面觸發(fā),當(dāng)無法加載嵌入內(nèi)容時在 <object> 元素上面觸發(fā)男娄,或者當(dāng)有一或多個框架無法加載時在框架集上面觸發(fā)行贪。
  • select :當(dāng)用戶選擇文本框( <input> 或 <texterea> )中的一或多個字符時觸發(fā)
  • resize :當(dāng)窗口或框架的大小變化時在 window 或框架上面觸發(fā)。
  • scroll :當(dāng)用戶滾動帶滾動條的元素中的內(nèi)容時沪伙,在該元素上面觸發(fā)瓮顽。 <body> 元素中包含所加載頁面的滾動條。

要確定瀏覽器是否支持 DOM2 級事件規(guī)定的 HTML 事件:

var isSupported = document.implementation.hasFeature("HTMLEvents", "2.0");

注意围橡,只有根據(jù)“DOM2 級事件”實現(xiàn)這些事件的瀏覽器才會返回 true 。而以非標準方式支持這些事件的瀏覽器則會返回 false 缕贡。

要確定瀏覽器是否支持“DOM3 級事件”定義的事件:

var isSupported = document.implementation.hasFeature("UIEvent", "3.0");

(1) load 事件

load 事件JavaScript 中最常用的一個事件就是 load 翁授。當(dāng)頁面完全加載后(包括所有圖像、JavaScript 文件晾咪、CSS 文件等外部資源)收擦,就會觸發(fā) window 上面的 load 事件。

有兩種定義 onload 事件處理程序的方式谍倦。
第一種:通過 JavaScript 來指定事件處理程序的方式塞赂,使用了跨瀏覽器的 EventUtil對象。

EventUtil.addHandler(window, "load", function(event){
    alert("Loaded!");
});

第二種:指定 onload 事件處理程序的方式是為 <body> 元素添加一個 onload 特性昼蛀。

<!DOCTYPE html>
<html>
<head>
<title>Load Event Example</title>
</head>
<body onload="alert('Loaded!')">
</body>
</html>

建議盡可能使用 JavaScript 方式宴猾。

(2)unload事件

與 load 事件對應(yīng)的是 unload 事件,這個事件在文檔被完全卸載后觸發(fā)叼旋。只要用戶從一個頁面切換到另一個頁面仇哆,就會發(fā)生 unload 事件。而利用這個事件最多的情況是清除引用夫植,以避免內(nèi)存泄漏讹剔。

有兩種指定 onunload 事件處理程序的方式。
第一種方式是使用 JavaScript:

EventUtil.addHandler(window, "unload", function(event){
    alert("Unloaded");
});

指定事件處理程序的第二種方式,為 <body> 元素添加一個特性:

<!DOCTYPE html>
<html>
<head>
<title>Unload Event Example</title>
</head>
<body onunload="alert('Unloaded!')">
</body>
</html>

(3) resize 事件

當(dāng)瀏覽器窗口被調(diào)整到一個新的高度或?qū)挾葧r延欠,就會觸發(fā) resize 事件陌兑。可以通過 JavaScript 或者 <body> 元素中的 onresize 特性來指定事件處理程序由捎。

EventUtil.addHandler(window, "resize", function(event){
    alert("Resized");
});

提示:瀏覽器窗口最小化或最大化時也會觸發(fā) resize 事件诀紊。

(4)scroll事件

雖然 scroll 事件是在 window 對象上發(fā)生的,但它實際表示的則是頁面中相應(yīng)元素的變化隅俘。

EventUtil.addHandler(window, "scroll", function(event) {
    if (document.compatMode == "CSS1Compat") {
        alert(document.documentElement.scrollTop);
    } else {
        alert(document.body.scrollTop);
    }
});

2邻奠、焦點事件

焦點事件會在頁面元素獲得或失去焦點時觸發(fā)。

  • blur :在元素失去焦點時觸發(fā)为居。這個事件不會冒泡碌宴;
  • focus :在元素獲得焦點時觸發(fā)。這個事件不會冒泡蒙畴;
  • focusin :在元素獲得焦點時觸發(fā)贰镣。這個事件與 HTML 事件 focus 等價,但它冒泡膳凝。
  • focusout :在元素失去焦點時觸發(fā)碑隆。這個事件是 HTML 事件 blur 的通用版本。

最主要的兩個是 focus 和 blur 蹬音。

3上煤、鼠標與滾輪事件

DOM3 級事件中定義了 9 個鼠標事件:

  • click :在用戶單擊主鼠標按鈕(一般是左邊的按鈕)或者按下回車鍵時觸發(fā)。
  • dblclick :在用戶雙擊主鼠標按鈕(一般是左邊的按鈕)時觸發(fā)著淆。
  • mousedown :在用戶按下了任意鼠標按鈕時觸發(fā)劫狠。
  • mouseenter :在鼠標光標從元素外部首次移動到元素范圍之內(nèi)時觸發(fā)。
  • mouseleave :在位于元素上方的鼠標光標移動到元素范圍之外時觸發(fā)永部。
  • mousemove :當(dāng)鼠標指針在元素內(nèi)部移動時重復(fù)地觸發(fā)独泞。不能通過鍵盤觸發(fā)這個事件。
  • mouseout :在鼠標指針位于一個元素上方苔埋,然后用戶將其移入另一個元素時觸發(fā)懦砂。又移入的另一個元素可能位于前一個元素的外部,也可能是這個元素的子元素组橄。不能通過鍵盤觸發(fā)這個事件荞膘。
  • mouseover :在鼠標指針位于一個元素外部,然后用戶將其首次移入另一個元素邊界之內(nèi)時觸發(fā)晨炕。不能通過鍵盤觸發(fā)這個事件衫画。
  • mouseup :在用戶釋放鼠標按鈕時觸發(fā)。不能通過鍵盤觸發(fā)這個事件瓮栗。

除了 mouseenter 和 mouseleave 削罩,所有鼠標事件都會冒泡瞄勾,也可以被取消,而取消鼠標事件將會影響瀏覽器的默認行為弥激。

只有在同一個元素上相繼觸發(fā) mousedown 和 mouseup 事件进陡,才會觸發(fā) click 事件;如果mousedown 或 mouseup 中的一個被取消微服,就不會觸發(fā) click 事件趾疚。

(1)客戶區(qū)坐標位置

鼠標事件都是在瀏覽器視口中的特定位置上發(fā)生的。這個位置信息保存在事件對象的 clientX 和clientY 屬性中以蕴。所有瀏覽器都支持這兩個屬性糙麦,它們的值表示事件發(fā)生時鼠標指針在視口中的水平和垂直坐標。

取得鼠標事件的客戶端坐標信息:

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
    event = EventUtil.getEvent(event);
    alert("Client coordinates: " + event.clientX + "," + event.clientY);
});

(2)頁面坐標位置

頁面坐標通過事件對象的 pageX 和pageY 屬性丛肮,能告訴你事件是在頁面中的什么位置發(fā)生的赡磅。換句話說,這兩個屬性表示鼠標光標在頁面中的位置宝与,因此坐標是從頁面本身而非視口的左邊和頂邊計算的焚廊。

取得鼠標事件在頁面中的坐標:

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
    event = EventUtil.getEvent(event);
    alert("Page coordinates: " + event.pageX + "," + event.pageY);
});

IE8 及更早版本不支持事件對象上的頁面坐標,可以使用客戶區(qū)坐標和滾動信息可以計算出來习劫。

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event) {
    event = EventUtil.getEvent(event);
    var pageX = event.pageX,
        pageY = event.pageY;
    if (pageX === undefined) {
        pageX = event.clientX + (document.body.scrollLeft ||
            document.documentElement.scrollLeft);
    }
    if (pageY === undefined) {
        pageY = event.clientY + (document.body.scrollTop ||
            document.documentElement.scrollTop);
    }
    alert("Page coordinates: " + pageX + "," + pageY);
});

(3)屏幕坐標位置

鼠標事件發(fā)生時咆瘟,不僅會有相對于瀏覽器窗口的位置,還有一個相對于整個電腦屏幕的位置诽里。而通過 screenX 和 screenY 屬性就可以確定鼠標事件發(fā)生時鼠標指針相對于整個屏幕的坐標信息袒餐。

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event){
    event = EventUtil.getEvent(event);
    alert("Screen coordinates: " + event.screenX + "," + event.screenY);
});

(4)修改鍵

這些修改鍵就是 Shift、Ctrl须肆、Alt 和 Meta(在 Windows鍵盤中是 Windows鍵匿乃,在蘋果機中是 Cmd 鍵),它們經(jīng)常被用來修改鼠標事件的行為豌汇。DOM 為此規(guī)定了 4 個屬性,表示這些修改鍵的狀態(tài): shiftKey 泄隔、 ctrlKey 拒贱、 altKey 和 metaKey 。這些屬性中包含的都是布爾值佛嬉,如果相應(yīng)的鍵被按下了逻澳,則值為 true ,否則值為 false 暖呕。

檢測不同修改鍵的狀態(tài):

var div = document.getElementById("myDiv");
EventUtil.addHandler(div, "click", function(event) {
    event = EventUtil.getEvent(event);
    var keys = new Array();
    if (event.shiftKey) {
        keys.push("shift");
    }
    if (event.ctrlKey) {
        keys.push("ctrl");
    }
    if (event.altKey) {
        keys.push("alt");
    }
    if (event.metaKey) {
        keys.push("meta");
    }
    alert("Keys: " + keys.join(","));
});

(5)鼠標按鈕

只有在主鼠標按鈕被單擊(或鍵盤回車鍵被按下)時才會觸發(fā) click 事件斜做,因此檢測按鈕的信息并不是必要的。

DOM 的 button 屬性可能有如下 3 個值: 0 表示主鼠標按鈕湾揽, 1 表示中間的鼠標按鈕(鼠標滾輪按鈕)瓤逼, 2 表示次鼠標按鈕笼吟。

(6)鼠標滾輪事件

當(dāng)用戶通過鼠標滾輪與頁面交互、在垂直方向上滾動頁面時(無論向上還是向下)霸旗,就會觸發(fā) mousewheel事件。

IE 6.0 首先實現(xiàn)了 mousewheel 事件。此后搓译,Opera吠架、Chrome 和 Safari 也都實現(xiàn)了這個事件。

與 mousewheel 事件對應(yīng)的 event 對象除包含鼠標事件的所有標準信息外精居,還包含一個特殊的 wheelDelta 屬性锄禽。當(dāng)用戶向前滾動鼠標滾輪時, wheelDelta 是 120 的倍數(shù)靴姿;當(dāng)用戶向后滾動鼠標滾輪時沃但, wheelDelta 是-120 的倍數(shù)。

EventUtil.addHandler(document, "mousewheel", function(event){
    event = EventUtil.getEvent(event);
    alert(event.wheelDelta);
});

Firefox 支持一個名為 DOMMouseScroll 的類似事件空猜,也是在鼠標滾輪滾動時觸發(fā)绽慈。有關(guān)鼠標滾輪的信息則保存在 detail 屬性中,當(dāng)向前滾動鼠標滾輪時辈毯,這個屬性的值是 -3 的倍數(shù)坝疼,當(dāng)向后滾動鼠標滾輪時,這個屬性的值是 3 的倍數(shù)谆沃。

EventUtil.addHandler(window, "DOMMouseScroll", function(event){
    event = EventUtil.getEvent(event);
    alert(event.detail);
});

跨瀏覽器環(huán)境下的解決方案:

var EventUtil = {
    //省略了其他代碼
    getWheelDelta: function(event) {
        if (event.wheelDelta) {
            return (client.engine.opera && client.engine.opera < 9.5 ?
                -event.wheelDelta : event.wheelDelta);
        } else {
            return -event.detail * 40;
        }
    },
    //省略了其他代碼
};

getWheelDelta() 方法首先檢測了事件對象是否包含 wheelDelta 屬性钝凶,如果是則通過瀏覽器檢測代碼確定正確的值。如果 wheelDelta 不存在唁影,則假設(shè)相應(yīng)的值保存在 detail 屬性中耕陷。由于Firefox 的值有所不同,因此首先要將這個值的符號反向据沈,然后再乘以 40哟沫,就可以保證與其他瀏覽器的值相同了。

使用示例

(function() {
    function handleMouseWheel(event) {
        event = EventUtil.getEvent(event);
        var delta = EventUtil.getWheelDelta(event);
        alert(delta);
    }
    EventUtil.addHandler(document, "mousewheel", handleMouseWheel);
    EventUtil.addHandler(document, "DOMMouseScroll", handleMouseWheel);
})();

4锌介、鍵盤與文本事件

用戶在使用鍵盤時會觸發(fā)鍵盤事件嗜诀。
有 3 個鍵盤事件:

  • keydown :當(dāng)用戶按下鍵盤上的任意鍵時觸發(fā),而且如果按住不放的話孔祸,會重復(fù)觸發(fā)此事件隆敢。
  • keypress :當(dāng)用戶按下鍵盤上的字符鍵時觸發(fā),而且如果按住不放的話崔慧,會重復(fù)觸發(fā)此事件拂蝎。
  • keyup :當(dāng)用戶釋放鍵盤上的鍵時觸發(fā)。

雖然所有元素都支持以上 3 個事件惶室,但只有在用戶通過文本框輸入文本時才最常用到温自。

只有一個文本事件: textInput 玄货。這個事件是對 keypress 的補充,用意是在將文本顯示給用戶之前更容易攔截文本捣作。在文本插入文本框之前會觸發(fā) textInput 事件誉结。

在用戶按了一下鍵盤上的字符鍵時,首先會觸發(fā) keydown 事件券躁,然后緊跟著是 keypress 事件惩坑,最后會觸發(fā) keyup 事件。其中也拜, keydown 和keypress 都是在文本框發(fā)生變化之前被觸發(fā)的以舒;而 keyup事件則是在文本框已經(jīng)發(fā)生變化之后被觸發(fā)的。如果用戶按下了一個字符鍵不放慢哈,就會重復(fù)觸發(fā)keydown 和 keypress 事件蔓钟,直到用戶松開該鍵為止。

(1)鍵碼

在發(fā)生 keydown 和 keyup 事件時卵贱, event 對象的 keyCode 屬性中會包含一個代碼滥沫,與鍵盤上一個特定的鍵對應(yīng)。

(2)字符編碼

發(fā)生 keypress 事件意味著按下的鍵會影響到屏幕中文本的顯示键俱。在所有瀏覽器中兰绣,按下能夠插入或刪除字符的鍵都會觸發(fā) keypress 事件。

charCode 屬性只有在發(fā)生keypress 事件時才包含值编振,而且這個值是按下的那個鍵所代表字符的 ASCII 編碼缀辩。

5、HTML5事件

(1) contextmenu 事件

contextmenu 事件用以表示何時應(yīng)該顯示上下文菜單踪央,以便開發(fā)人員取消默認的上下文菜單而提供自定義的菜單臀玄。

由于 contextmenu 事件是冒泡的,因此可以為 document 指定一個事件處理程序畅蹂,用以處理頁面中發(fā)生的所有此類事件健无。這個事件的目標是發(fā)生用戶操作的元素。

通常使用 contextmenu 事件來顯示自定義的上下文菜單液斜,而使用 onclick 事件處理程序來隱藏該菜單睬涧。
HTML結(jié)構(gòu):

<!DOCTYPE html>
<html>
<head>
    <title>ContextMenu Event Example</title>
</head>
<body>
    <div id="myDiv">Right click or Ctrl+click me to get a custom context menu. Click anywhere else to get the default context menu.</div>
    <ul id="myMenu" style="position:absolute;visibility:hidden;background-color:
silver">
        <li><a >Nicholas’ site</a></li>
        <li><a >Wrox site</a></li>
        <li><a >Yahoo!</a></li>
    </ul>
</body>
</html>

JS 部分代碼:

EventUtil.addHandler(window, "load", function(event) {
    var div = document.getElementById("myDiv");
    EventUtil.addHandler(div, "contextmenu", function(event) {
        event = EventUtil.getEvent(event);
        EventUtil.preventDefault(event);
        var menu = document.getElementById("myMenu");
        menu.style.left = event.clientX + "px";
        menu.style.top = event.clientY + "px";
        menu.style.visibility = "visible";
    });
    EventUtil.addHandler(document, "click", function(event) {
        document.getElementById("myMenu").style.visibility = "hidden";
    });
});

為 <div> 元素添加了 oncontextmenu 事件的處理程序。這個事件處理程序首先會取消默認行為旗唁,以保證不顯示瀏覽器默認的上下文菜單。然后痹束,再根據(jù) event 對象 clientX 和clientY 屬性的值检疫,來確定放置 <ul> 元素的位置。最后一步就是通過將 visibility 屬性設(shè)置為"visible" 來顯示自定義上下文菜單祷嘶。另外屎媳,還為 document 添加了一個 onclick 事件處理程序夺溢,以便用戶能夠通過鼠標單擊來隱藏菜單(單擊也是隱藏系統(tǒng)上下文菜單的默認操作)。
雖然這個例子很簡單烛谊,但它卻展示了 Web 上所有自定義上下文菜單的基本結(jié)構(gòu)风响。

(2)beforeunload 事件

這個事件會在瀏覽器卸載頁面之前觸發(fā),可以通過它來取消卸載并繼續(xù)使用原有頁面丹禀。

(3)DOMContentLoaded 事件

window 的 load 事件會在頁面中的一切都加載完畢時觸發(fā)状勤,但這個過程可能會因為要加載的外部資源過多而頗費周折。而 DOMContentLoaded 事件則在形成完整的 DOM樹之后就會觸發(fā)双泪,不理會圖像持搜、JavaScript 文件、CSS 文件或其他資源是否已經(jīng)下載完畢焙矛。與 load 事件不同葫盼,DOMContentLoaded 支持在頁面下載的早期添加事件處理程序,這也就意味著用戶能夠盡早地與頁面進行交互村斟。

要處理 DOMContentLoaded 事件贫导,可以為 document 或 window 添加相應(yīng)的事件處理程序(盡管這個事件會冒泡到 window ,但它的目標實際上是 document )蟆盹。

EventUtil.addHandler(document, "DOMContentLoaded", function(event){
    alert("Content loaded");
});

DOMContentLoaded 事件對象不會提供任何額外的信息(其 target 屬性是 document )孩灯。

通常這個事件既可以添加事件處理程序,也可以執(zhí)行其他 DOM 操作日缨。這個事件始終都會在 load 事件之前觸發(fā)钱反。

(4) readystatechange 事件

IE 為 DOM 文檔中的某些部分提供了 readystatechange 事件。這個事件的目的是提供與文檔或元素的加載狀態(tài)有關(guān)的信息匣距,但這個事件的行為有時候也很難預(yù)料面哥。支持 readystatechange 事件的每個對象都有一個 readyState 屬性,可能包含下列 5 個值中的一個毅待。

  • uninitialized (未初始化):對象存在但尚未初始化尚卫。
  • loading (正在加載):對象正在加載數(shù)據(jù)。
  • loaded (加載完畢):對象加載數(shù)據(jù)完成尸红。
  • interactive (交互):可以操作對象了吱涉,但還沒有完全加載。
  • complete (完成):對象已經(jīng)加載完畢外里。

并非所有對象都會經(jīng)歷 readyState 的這幾個階段怎爵。

同時檢測交互和完成階段:

EventUtil.addHandler(document, "readystatechange", function(event){
    if (document.readyState == "interactive" || document.readyState == "complete"){
        EventUtil.removeHandler(document, "readystatechange",arguments.callee);
        alert("Content loaded");
    }
});

由于事件處理程序使用的是匿名函數(shù),因此使用了 arguments.callee 來引用該函數(shù)盅蝗。

(5) pageshow 和 pagehide 事件

Firefox 和 Opera 有一個特性鳖链,名叫“往返緩存”(back-forward cache,或 bfcache)墩莫,可以在用戶使用瀏覽器的“后退”和“前進”按鈕時加快頁面的轉(zhuǎn)換速度芙委。這個緩存中不僅保存著頁面數(shù)據(jù)逞敷,還保存了 DOM 和 JavaScript 的狀態(tài);實際上是將整個頁面都保存在了內(nèi)存里灌侣。

pageshow 事件在頁面顯示時觸發(fā)推捐,無論該頁面是否來自 bfcache。在重新加載的頁面中侧啼, pageshow 會在 load 事件觸發(fā)后觸發(fā)牛柒;而對于 bfcache 中的頁面, pageshow 會在頁面狀態(tài)完全恢復(fù)的那一刻觸發(fā)慨菱。另外要注意的是焰络,雖然這個事件的目標是 document ,但必須將其事件處理程序添加到 window 符喝。

pageshow 事件的 event 對象還包含一個名為 persisted 的布爾值屬性闪彼。
如果頁面被保存在了 bfcache 中,則這個屬性的值為 true 协饲;否則畏腕,這個屬性的值為 false 。

(function() {
    var showCount = 0;
    EventUtil.addHandler(window, "load", function() {
        alert("Load fired");
    });
    EventUtil.addHandler(window, "pageshow", function() {
        showCount++;
        alert("Show has been fired " + showCount +
            " times. Persisted? " + event.persisted);
    });
})();

通過檢測 persisted 屬性茉稠,就可以根據(jù)頁面在 bfcache 中的狀態(tài)來確定是否需要采取其他操作描馅。

與 pageshow 事件對應(yīng)的是 pagehide 事件,該事件會在瀏覽器卸載頁面的時候觸發(fā)而线,而且是在unload 事件之前觸發(fā)铭污。與 pageshow 事件一樣, pagehide 在 document 上面觸發(fā)膀篮,但其事件處理程序必須要添加到 window 對象嘹狞。這個事件的 event 對象也包含 persisted 屬性,不過其用途稍有不同誓竿。

對于 pagehide 事件磅网,如果頁面在卸載之后會被保存在 bfcache 中,那么 persisted 的值也會被設(shè)置為 true 筷屡。因此涧偷,當(dāng)?shù)谝淮斡|發(fā) pageshow 時, persisted 的值一定是 false毙死。

提示:指定了 onunload 事件處理程序的頁面會被自動排除在 bfcache 之外燎潮,即使事件處理程序是空的。原因在于扼倘, onunload 最常用于撤銷在 onload 中所執(zhí)行的操作跟啤,而跳過 onload 后再次顯示頁面很可能就會導(dǎo)致頁面不正常。

(6)hashchange 事件

HTML5 新增了 hashchange 事件,以便在 URL 的參數(shù)列表(及 URL 中“#”號后面的所有字符串)發(fā)生變化時通知開發(fā)人員隅肥。之所以新增這個事件,是因為在 Ajax 應(yīng)用中袄简,開發(fā)人員經(jīng)常要利用 URL 參數(shù)列表來保存狀態(tài)或?qū)Ш叫畔ⅰ?/p>

必須要把 hashchange 事件處理程序添加給 window 對象腥放,然后 URL 參數(shù)列表只要變化就會調(diào)用它。此時的 event 對象應(yīng)該額外包含兩個屬性: oldURL 和 newURL 绿语。這兩個屬性分別保存著參數(shù)列表變化前后的完整 URL秃症。

EventUtil.addHandler(window, "hashchange", function(event){
    alert("Old URL: " + event.oldURL + "\nNew URL: " + event.newURL);
});

只有 Firefox 6+、Chrome 和 Opera 支持 oldURL 和 newURL 屬性吕粹。為此种柑,最好是使用 location對象來確定當(dāng)前的參數(shù)列表。

EventUtil.addHandler(window, "hashchange", function(event){
    alert("Current hash: " + location.hash);
});

檢測瀏覽器是否支持 hashchange 事件:

var isSupported = ("onhashchange" in window) && (document.documentMode ===
undefined || document.documentMode > 7);

6匹耕、觸摸與手勢事件

(1)觸摸事件

觸摸事件會在用戶手指放在屏幕上面時聚请、在屏幕上滑動時或從屏幕上移開時觸發(fā)。

  • touchstart :當(dāng)手指觸摸屏幕時觸發(fā)稳其;即使已經(jīng)有一個手指放在了屏幕上也會觸發(fā)驶赏。
  • touchmove :當(dāng)手指在屏幕上滑動時連續(xù)地觸發(fā)。在這個事件發(fā)生期間既鞠,調(diào)用 preventDefault()可以阻止?jié)L動煤傍。
  • touchend :當(dāng)手指從屏幕上移開時觸發(fā)。
  • touchcancel :當(dāng)系統(tǒng)停止跟蹤觸摸時觸發(fā)嘱蛋。

這幾個事件都會冒泡蚯姆,也都可以取消。每個觸摸事件的 event 對象都提供了在鼠標事件中常見的屬性:bubbles 洒敏、 cancelable 龄恋、 view 、 clientX 桐玻、 clientY 篙挽、 screenX 、 screenY 镊靴、 detail 铣卡、 altKey 、 shiftKey 偏竟、 ctrlKeymetaKey煮落。

除了常見的 DOM屬性外,觸摸事件還包含三個用于跟蹤觸摸的屬性踊谋。

  • touches :表示當(dāng)前跟蹤的觸摸操作的 Touch 對象的數(shù)組蝉仇。
  • targetTouchs :特定于事件目標的 Touch 對象的數(shù)組。
  • changeTouches :表示自上次觸摸以來發(fā)生了什么改變的 Touch 對象的數(shù)組。

每個 Touch 對象包含下列屬性轿衔。

  • clientX :觸摸目標在視口中的 x 坐標沉迹。
  • clientY :觸摸目標在視口中的 y 坐標。
  • identifier :標識觸摸的唯一 ID害驹。
  • pageX :觸摸目標在頁面中的 x 坐標鞭呕。
  • pageY :觸摸目標在頁面中的 y 坐標。
  • screenX :觸摸目標在屏幕中的 x 坐標宛官。
  • screenY :觸摸目標在屏幕中的 y 坐標葫松。
  • target :觸摸的 DOM 節(jié)點目標。

使用這些屬性可以跟蹤用戶對屏幕的觸摸操作底洗。

function handleTouchEvent(event) {
    //只跟蹤一次觸摸
    if (event.touches.length == 1) {
        var output = document.getElementById("output");
        switch (event.type) {
            case "touchstart":
                output.innerHTML = "Touch started (" + event.touches[0].clientX +
                    "," + event.touches[0].clientY + ")";
                break;
            case "touchend":
                output.innerHTML += "<br>Touch ended (" +
                    event.changedTouches[0].clientX + "," +
                    event.changedTouches[0].clientY + ")";
                break;
            case "touchmove":
                event.preventDefault(); //阻止?jié)L動
                output.innerHTML += "<br>Touch moved (" +
                    event.changedTouches[0].clientX + "," +
                    event.changedTouches[0].clientY + ")";
                break;
        }
    }
}
EventUtil.addHandler(document, "touchstart", handleTouchEvent);
EventUtil.addHandler(document, "touchend", handleTouchEvent);
EventUtil.addHandler(document, "touchmove", handleTouchEvent);

(2)手勢事件

當(dāng)兩個手指觸摸屏幕時就會產(chǎn)生手勢腋么,手勢通常會改變顯示項的大小,或者旋轉(zhuǎn)顯示項亥揖。有三個手勢事件:

  • gesturestart :當(dāng)一個手指已經(jīng)按在屏幕上而另一個手指又觸摸屏幕時觸發(fā)珊擂。
  • gesturechange :當(dāng)觸摸屏幕的任何一個手指的位置發(fā)生變化時觸發(fā)。
  • gestureend :當(dāng)任何一個手指從屏幕上面移開時觸發(fā)徐块。

只有兩個手指都觸摸到事件的接收容器時才會觸發(fā)這些事件未玻。

觸摸事件和手勢事件之間存在某種關(guān)系。當(dāng)一個手指放在屏幕上時胡控,會觸發(fā) touchstart 事件扳剿。如果另一個手指又放在了屏幕上,則會先觸發(fā) gesturestart 事件昼激,隨后觸發(fā)基于該手指的 touchstart事件庇绽。如果一個或兩個手指在屏幕上滑動,將會觸發(fā) gesturechange 事件橙困。但只要有一個手指移開瞧掺,就會觸發(fā) gestureend 事件,緊接著又會觸發(fā)基于該手指的 touchend 事件凡傅。

與觸摸事件一樣辟狈,每個手勢事件的 event 對象都包含著標準的鼠標事件屬性: bubbles 、cancelable 夏跷、 view 哼转、 clientX 、 clientY 槽华、 screenX 壹蔓、 screenY 、 detail 猫态、 altKey 佣蓉、 shiftKey 披摄、ctrlKeymetaKey

還包含兩個額外的屬性: rotation 和 scale 勇凭。其中疚膊, rotation 屬性表
示手指變化引起的旋轉(zhuǎn)角度,負值表示逆時針旋轉(zhuǎn)套像,正值表示順時針旋轉(zhuǎn)(該值從 0 開始)酿联。而 scale屬性表示兩個手指間距離的變化情況(例如向內(nèi)收縮會縮短距離);這個值從 1 開始夺巩,并隨距離拉大而增長,隨距離縮短而減小周崭。

使用手勢事件的一個示例:

function handleGestureEvent(event) {
    var output = document.getElementById("output");
    switch (event.type) {
        case "gesturestart":
            output.innerHTML = "Gesture started (rotation=" + event.rotation +
                ",scale=" + event.scale + ")";
            break;
        case "gestureend":
            output.innerHTML += "<br>Gesture ended (rotation=" + event.rotation +
                ",scale=" + event.scale + ")";
            break;
        case "gesturechange":
            output.innerHTML += "<br>Gesture changed (rotation=" + event.rotation +
                ",scale=" + event.scale + ")";
            break;
    }
}
document.addEventListener("gesturestart", handleGestureEvent, false);
document.addEventListener("gestureend", handleGestureEvent, false);
document.addEventListener("gesturechange", handleGestureEvent, false);

五柳譬、內(nèi)存和性能

在 JavaScript 中,添加到頁面上的事件處理程序數(shù)量將直接關(guān)系到頁面的整體運行性能续镇。導(dǎo)致這一問題的原因是多方面的美澳。首先,每個函數(shù)都是對象摸航,都會占用內(nèi)存制跟;內(nèi)存中的對象越多,性能就越差酱虎。其次雨膨,必須事先指定所有事件處理程序而導(dǎo)致的 DOM訪問次數(shù),會延遲整個頁面的交互就緒時間读串。

1聊记、事件委托

對“事件處理程序過多”問題的解決方案就是事件委托。事件委托利用了事件冒泡恢暖,只指定一個事件處理程序排监,就可以管理某一類型的所有事件。

<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>

使用事件委托杰捂,只需在DOM 樹中盡量最高的層次上添加一個事件處理程序:

var list = document.getElementById("myLinks");
EventUtil.addHandler(list, "click", function(event) {
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    switch (target.id) {
        case "doSomething":
            document.title = "I changed the document's title";
            break;
        case "goSomewhere":
            location.;
            break;
        case "sayHi":
            alert("hi");
            break;
    }
});

優(yōu)點:

  • 在頁面中設(shè)置事件處理程序所需的時間更少舆床。只添加一個事件處理程序所需的 DOM引用更少,所花的時間也更少嫁佳。
  • 整個頁面占用的內(nèi)存空間更少挨队,能夠提升整體性能。

最適合采用事件委托技術(shù)的事件包括 click 脱拼、 mousedown 瞒瘸、 mouseup 、 keydown 熄浓、 keyupkeypress 情臭。

2省撑、移除事件處理程序

每當(dāng)將事件處理程序指定給元素時,運行中的瀏覽器代碼與支持頁面交互的 JavaScript 代碼之間就會建立一個連接俯在。這種連接越多竟秫,頁面執(zhí)行起來就越慢。

內(nèi)存中留有那些過時不用的“空事件處理程序”(dangling event handler)跷乐,也是造成 Web 應(yīng)用程序內(nèi)存與性能問題的主要原因肥败。

如果你知道某個元素即將被移除,那么最好手工移除事件處理程序:

<div id="myDiv">
    <input type="button" value="Click Me" id="myBtn">
</div>
<script type="text/javascript">
var btn = document.getElementById("myBtn");
btn.onclick = function() {
    //先執(zhí)行某些操作
    btn.onclick = null; // 移除事件處理程序
    document.getElementById("myDiv").innerHTML = "Processing...";
};
</script>

注意愕提,在事件處理程序中刪除按鈕也能阻止事件冒泡馒稍。目標元素在文檔中是事件冒泡的前提。

在頁面卸載之前浅侨,先通過 onunload 事件處理程序移除所有事件處理程序纽谒。只要是通過 onload 事件處理程序添加的東西,最后都要通過 onunload 事件處理程序?qū)⑺鼈円瞥?/p>

六如输、模擬事件

事件鼓黔,就是網(wǎng)頁中某個特別值得關(guān)注的瞬間。事件經(jīng)常由用戶操作或通過其他瀏覽器功能來觸發(fā)不见。

1澳化、DOM中的事件模擬

可以在 document 對象上使用 createEvent() 方法創(chuàng)建 event 對象。這個方法接收一個參數(shù)稳吮,即表示要創(chuàng)建的事件類型的字符串缎谷。

在創(chuàng)建了 event 對象之后,還需要使用與事件有關(guān)的信息對其進行初始化盖高。每種類型的 event 對象都有一個特殊的方法慎陵,為它傳入適當(dāng)?shù)臄?shù)據(jù)就可以初始化該 event 對象。不同類型的這個方法的名字也不相同喻奥,具體要取決于 createEvent() 中使用的參數(shù)席纽。

模擬事件的最后一步就是觸發(fā)事件。這一步需要使用 dispatchEvent() 方法撞蚕,所有支持事件的DOM 節(jié)點都支持這個方法润梯。調(diào)用 dispatchEvent() 方法時,需要傳入一個參數(shù)甥厦,即表示要觸發(fā)事件的 event 對象纺铭。觸發(fā)事件之后,該事件就躋身“官方事件”之列了刀疙,因而能夠照樣冒泡并引發(fā)相應(yīng)事
件處理程序的執(zhí)行舶赔。

模擬鼠標事件

創(chuàng)建新的鼠標事件對象并為其指定必要的信息,就可以模擬鼠標事件谦秧。創(chuàng)建鼠標事件對象的方法是為 createEvent() 傳入字符串 "MouseEvents" 竟纳。返回的對象有一個名為 initMouseEvent() 方法撵溃,用于指定與該鼠標事件有關(guān)的信息。這個方法接收 15 個參數(shù)锥累,分別與鼠標事件中每個典型的屬性一一對應(yīng)缘挑;

  • type (字符串):表示要觸發(fā)的事件類型,例如 "click" 桶略。
  • bubbles (布爾值):表示事件是否應(yīng)該冒泡语淘。為精確地模擬鼠標事件,應(yīng)該把這個參數(shù)設(shè)置為true 际歼。
  • cancelable (布爾值):表示事件是否可以取消惶翻。為精確地模擬鼠標事件,應(yīng)該把這個參數(shù)設(shè)置為 true 鹅心。
  • view (AbstractView):與事件關(guān)聯(lián)的視圖维贺。這個參數(shù)幾乎總是要設(shè)置為 document.defaultView 。
  • detail (整數(shù)):與事件有關(guān)的詳細信息巴帮。這個值一般只有事件處理程序使用,但通常都設(shè)置為 0 虐秋。
  • screenX (整數(shù)):事件相對于屏幕的 X 坐標榕茧。
  • screenY (整數(shù)):事件相對于屏幕的 Y 坐標。
  • clientX (整數(shù)):事件相對于視口的 X 坐標客给。
  • clientY (整數(shù)):事件想對于視口的 Y 坐標用押。
  • ctrlKey (布爾值):表示是否按下了 Ctrl 鍵。默認值為 false 靶剑。
  • altKey (布爾值):表示是否按下了 Alt 鍵蜻拨。默認值為 false 。
  • shiftKey (布爾值):表示是否按下了 Shift 鍵桩引。默認值為 false 缎讼。
  • metaKey (布爾值):表示是否按下了 Meta 鍵。默認值為 false 坑匠。
  • button (整數(shù)):表示按下了哪一個鼠標鍵血崭。默認值為 0 。
  • relatedTarget (對象):表示與事件相關(guān)的對象厘灼。這個參數(shù)只在模擬 mouseover 或 mouseout時使用

前 4 個參數(shù)對正確地激發(fā)事件至關(guān)重要夹纫,因為瀏覽器要用到這些參數(shù);

var btn = document.getElementById("myBtn");
//創(chuàng)建事件對象
var event = document.createEvent("MouseEvents");
//初始化事件對象
event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0,
false, false, false, false, 0, null);
//觸發(fā)事件
btn.dispatchEvent(event);

同樣设凹,對于鍵盤事件舰讹、IE中的事件模擬和其他事件也可以模擬,也可以自定義 DOM 事件闪朱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末月匣,一起剝皮案震驚了整個濱河市钻洒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桶错,老刑警劉巖航唆,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異院刁,居然都是意外死亡糯钙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門退腥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來任岸,“玉大人,你說我怎么就攤上這事狡刘∠砬保” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵嗅蔬,是天一觀的道長剑按。 經(jīng)常有香客問我,道長澜术,這世上最難降的妖魔是什么艺蝴? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮鸟废,結(jié)果婚禮上猜敢,老公的妹妹穿的比我還像新娘。我一直安慰自己盒延,他們只是感情好缩擂,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著添寺,像睡著了一般胯盯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音淘正,去河邊找鬼蚂子。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼君账,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沈善?” 一聲冷哼從身側(cè)響起乡数,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤椭蹄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后净赴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绳矩,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年玖翅,在試婚紗的時候發(fā)現(xiàn)自己被綠了翼馆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡金度,死狀恐怖应媚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猜极,我是刑警寧澤中姜,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站跟伏,受9級特大地震影響丢胚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜受扳,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一嗜桌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辞色,春花似錦、人聲如沸浮定。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桦卒。三九已至立美,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間方灾,已是汗流浹背建蹄。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留裕偿,地道東北人洞慎。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像嘿棘,于是被迫代替她去往敵國和親劲腿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • ??JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的挥吵。 ??事件,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,473評論 1 11
  • 事件是什么花椭,可以用來做什么忽匈,什么時候用到它? 事件矿辽,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬間丹允。JavaScr...
    茂茂愛吃魚閱讀 1,506評論 0 16
  • 第13章 事件 1. 事件流 事件流描述的是從頁面中接收事件的順序。 (1) 事件冒泡 IE 的事件流叫做事件冒泡...
    yinxmm閱讀 939評論 0 17
  • JavaScript 程序采用了異步事件驅(qū)動編程模型嗦锐。在這種程序設(shè)計風(fēng)格下嫌松,當(dāng)文檔、瀏覽器奕污、元素或與之相關(guān)的對象發(fā)...
    劼哥stone閱讀 1,250評論 3 11
  • JavaScript和HTML的交互是通過事件實現(xiàn)的萎羔。 事件就是用戶或瀏覽器自身執(zhí)行的某種動作。 舉幾個可能發(fā)生的...
    饑人谷_Tom閱讀 375評論 0 0