事件

JavaScriptHTML 之間的交互是通過事件實現(xiàn)的寸宵。事件崖面,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬間元咙。可以使用偵聽器(或處理程序)來預(yù)訂事件巫员,以便事件發(fā)生時執(zhí)行相應(yīng)的代碼庶香。這種在傳統(tǒng)軟件工程中被稱為觀察員模式的模型,支持頁面的行為(JavaScript代碼)與頁面的外觀(HTMLCSS代碼)之間的松散耦合简识。

事件流

事件流描述的是從頁面中接收事件的順序赶掖。但有意思的是, IENetscape 開發(fā)團(tuán)隊居然提出了差不多是完全相反的事件流的概念七扰。 IE 的事件流是事件冒泡流奢赂,而 Netscape Communicator 的事件流是事件捕獲流。

  • IE 的事件流叫做事件冒泡(event bubbling)颈走,即事件開始時由最具體的元素(文檔中嵌套層次最深的那節(jié)點)接收膳灶,然后逐級向上傳播到較為不具體的節(jié)點(文檔)。
<!DOCTYPE html>
<html>
<head>
    <title>Event Bubbling Example</title>
</head>
<body>
    <div id="myDiv">Click Me</div>
</body>
</html>

click 點擊的順序:div => body => html => document

  • 事件捕獲的思想是不太具體的節(jié)點應(yīng)該更早接收到事件立由,而最具體的節(jié)點應(yīng)該最后接收到事件轧钓。事件捕獲的用意在于在事件到達(dá)預(yù)定目標(biāo)之前捕獲它富岳。
<!DOCTYPE html>
<html>
<head>
    <title>Event Bubbling Example</title>
</head>
<body>
    <div id="myDiv">Click Me</div>
</body>
</html>

click 點擊的順序: document => html => body => div

DOM事件流

DOM2級事件規(guī)定的事件流包括三個階段:事件捕獲階段苔货、處于目標(biāo)階段和事件冒泡階段太惠。首先發(fā)生的是事件捕獲侵歇,為截獲事件提供了機(jī)會。然后是實際的目標(biāo)接收到事件柑蛇。最后一個階段是冒泡階段苟耻,可以在這個階段對事件做出響應(yīng)吱瘩。

IE9荷逞、Opera媒咳、FirefoxChromeSafari都支持 DOM事件流种远;IE8及更早版本不支持DOM 事件流伟葫。

事件處理程序

事件就是用戶和瀏覽器自身執(zhí)行的某種動作,事件處理程序就是響應(yīng)某個事件的函數(shù)就叫做事件處理函數(shù)院促,事件處理程序以on 開頭。click 事件的事件處理程序就是 onclick斧抱,load事件的事件處理程序就是onload常拓。

HTML 事件處理程序

<input type="button" value="Click Me" onclick="alert('Clicked')" />

當(dāng)單擊這個按鈕時,就會顯示一個警告框辉浦。這個操作是通過指定 onclick 特性并將一些 JavaScript代碼作為它的值來定義的弄抬。

Dom0 及事件處理程序

通過 JavaScript指定事件處理程序的傳統(tǒng)方式,就是將一個函數(shù)賦值給一個事件處理程序?qū)傩韵芙肌_@種為事件處理程序賦值的方法是在第四代Web瀏覽器中出現(xiàn)的掂恕,而且至今仍然為所有現(xiàn)代瀏覽器所支持拖陆。原因一是簡單,二是具有跨瀏覽器的優(yōu)勢懊亡。

每個元素(包括 windowdocument)都有自己的事件處理程序?qū)傩砸绬@些屬性通常全部小寫,例如onclick店枣。將這種屬性的值設(shè)置為一個函數(shù)速警,就可以指定事件處理程序,如下所示:

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

使用 DOM0 級方法指定的事件處理程序被認(rèn)為是元素的方法鸯两。因此闷旧,這時候的事件處理程序是在元素的作用域中運行;換句話說钧唐,程序中的 this 引用當(dāng)前元素忙灼。來看一個例子。

var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(this.id); //"myBtn"
};
  • 以這種方式添加的事件處理程序會在事件流的冒泡階段被處理钝侠。

  • 也可以刪除通過DOM0級方法指定的事件處理程序该园,只要像下面這樣將事件處理程序?qū)傩缘闹翟O(shè)置為 null 即可:

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

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

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

這里為按鈕添加了兩個事件處理程序哑诊。這兩個事件處理程序會按照添加它們的順序觸發(fā),因此首先會顯示元素的 ID及刻,其次會顯示"Hello world!"消息镀裤。

btn.addEventListener("click", function(){
    alert("Hello world!");
}, false);

因為在 addEventListener()removeEventListener()中使
用了相同的函數(shù)〗煞梗可以考慮將事件處理函數(shù)單獨抽取出來暑劝。

var btn = document.getElementById("myBtn");
var handler = function(){
    alert(this.id);
};
btn.addEventListener("click", handler, false);
//這里省略了其他代碼
btn.removeEventListener("click", handler, false); //有效!

IE9颗搂、Firefox担猛、SafariChromeOpera支持 DOM2 級事件處理程序。

IE事件處理程序

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

由于IE8及更早版本只支持事件冒泡,所以通過 attachEvent()添加的事件處理程序都會被添加到冒泡階段蒸走。

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

注意仇奶,attachEvent()的第一個參數(shù)是onclick,而非DOMaddEventListener()方法中的click载碌。

IE中使用 attachEvent()與使用DOM0級方法的主要區(qū)別在于事件處理程序的作用域猜嘱。在使用 DOM0 級方法的情況下,事件處理程序會在其所屬元素的作用域內(nèi)運行嫁艇;

在使用 attachEvent()方法的情況下朗伶,事件處理程序會在全局作用域中運行,因此this 等于window步咪!

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

不過论皆,與 DOM方法不同的是,這些事件處理程序不是以添加它們的順序執(zhí)行猾漫,而是以相反的順序被觸發(fā)点晴。

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

這里調(diào)用了兩次 attachEvent(),為同一個按鈕添加了兩個不同的事件處理程序悯周。單擊這個例子中的按鈕粒督,首先看到的是"Hello world!",然后才是"Clicked"禽翼。

跨瀏覽器的事件處理程序

// EventUtil 的用法如下所示屠橄。
  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;
      }
    }
  };
var btn = document.getElementById("myBtn");
var handler = function(){
    alert("Clicked");
};
EventUtil.addHandler(btn, "click", handler);
//這里省略了其他代碼
EventUtil.removeHandler(btn, "click", handler);

這兩個方法首先都會檢測傳入的元素中是否存在DOM2級方法。如果存在 DOM2 級方法闰挡,則使用該方法:傳入事件類型锐墙、事件處理程序函數(shù)和第三個參數(shù) false(表示冒泡階段)。

如果存在的是IE 的方法长酗,則采取第二種方案溪北。注意,為了在 IE8及更早版本中運行夺脾,此時的事件類型必須加上"on"前綴之拨。

最后一種可能就是使用DOM0級方法(在現(xiàn)代瀏覽器中,應(yīng)該不會執(zhí)行這里的代碼)咧叭。此時敦锌,我們使用的是方括號語法來將屬性名指定為事件處理程序,或者將屬性設(shè)置為 null佳簸。

事件對象

在觸發(fā) DOM上的某個事件時,會產(chǎn)生一個事件對象event,這個對象中包含著所有與事件有關(guān)的信息生均。包括導(dǎo)致事件的元素听想、事件的類型以及其他與特定事件相關(guān)的信息。例如马胧,鼠標(biāo)操作導(dǎo)致的事件對象中汉买,會包含鼠標(biāo)位置的信息,而鍵盤操作導(dǎo)致的事件對象中佩脊,會包含與按下的鍵有關(guān)的信息蛙粘。

DOM中的事件對象

兼容DOM的瀏覽器會將一個 event對象傳入到事件處理程序中。無論指定事件處理程序時使用什么方法(DOM0 級DOM2 級)威彰,都會傳入event 對象出牧。來看下面的例子。

var btn = document.getElementById("myBtn");
btn.onclick = function(event){
    alert(event.type); //"click"
};
btn.addEventListener("click", function(event){
    alert(event.type); //"click"
}, false);
屬性/方法 類 型 讀/寫 說 明
bubbles Boolean 只讀 表明事件是否冒泡
cancelable Boolean 只讀 表明是否可以取消事件的默認(rèn)行為
currentTarget Element 只讀 其事件處理程序當(dāng)前正在處理事件的那個元素
defaultPrevented Boolean 只讀 true 表 示 已 經(jīng) 調(diào) 用 了 preventDefault()DOM3級事件中新增)
detail Integer 只讀 與事件相關(guān)的細(xì)節(jié)信息
eventPhase Integer 只讀 調(diào)用事件處理程序的階段: 1 表示捕獲階段歇盼, 2 表示“處于目標(biāo)”舔痕, 3 表示冒泡階段
preventDefault() Function 只讀 取消事件的默認(rèn)行為 。 如 果 cancelabletrue豹缀,則可以使用這個方法
stopImmediatePropagation() Function 只讀 取消事件的進(jìn)一步捕獲或冒泡伯复,同時阻止任何事件處理程序被調(diào)用(DOM3級事件中新增)
stopPropagation() Function 只讀 取消事件的進(jìn)一步捕獲或冒泡。如果bubblestrue邢笙,則可以使用這個方法
target Element 只讀 事件的目標(biāo)
trusted Boolean 只讀 true表示事件是瀏覽器生成的啸如。為false表示事件是由開發(fā)人員通過 JavaScript 創(chuàng)建的(DOM3級事件中新增)
type String 只讀 被觸發(fā)的事件的類型
view AbstractView 只讀 與事件關(guān)聯(lián)的抽象視圖。等同于發(fā)生事件的 window對象

在需要通過一個函數(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;

這個例子定義了一個名為 handler的函數(shù),用于處理 3 種事件:click筐骇、mouseovermouseout债鸡。

當(dāng)單擊按鈕時,會出現(xiàn)一個與前面例子中一樣的警告框铛纬。當(dāng)按鈕移動到按鈕上面時厌均,背景顏色應(yīng)該會變成紅色,而當(dāng)鼠標(biāo)移動出按鈕的范圍時告唆,背景顏色應(yīng)該會恢復(fù)為默認(rèn)值棺弊。這里通過檢測 event.type屬性,讓函數(shù)能夠確定發(fā)生了什么事件擒悬,并執(zhí)行相應(yīng)的操作模她。

IE 中的事件對象

在使用 DOM0 級方法添加事件處理程序時,event對象作為 window 對象的一個屬性存在懂牧。來看下面的例子侈净。

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

我們通過 window.event取得了 event對象尊勿,并檢測了被觸發(fā)事件的類型(IE中的 type 屬性與DOM 中的 type 屬性是相同的)。

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

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

因為事件處理程序的作用域是根據(jù)指定它的方式來確定的旋膳,所以不能認(rèn)為 this會始終等于事件目標(biāo)澎语。故而,最好還是使用 event.srcElement比較保險验懊。例如:

屬性/方法 類 型 讀/寫 說 明
cancelBubble Boolean 讀/寫 默認(rèn)值為false擅羞,但將其設(shè)置為true就可以取消事件冒泡(與DOM中的stopPropagation()方法的作用相同)
returnValue Boolean 讀/寫 默認(rèn)值為true,但將其設(shè)置為false就可以取消事件的默認(rèn)行為(與DOM中的preventDefault()方法的作用相同)
srcElement Element 只讀 事件的目標(biāo)(與DOM中的target屬性相同)
type String 只讀 被觸發(fā)的事件的類型
var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(window.event.srcElement === this); //true
};
var btn = document.getElementById("myBtn");
btn.onclick = function(){
    alert(window.event.srcElement === this); //true
};
btn.attachEvent("onclick", function(event){
    alert(event.srcElement === this); //false
    // this => window
});

returnValue 屬性相當(dāng)于 DOM中的preventDefault()方法义图,它們的作用都是取消給定事件的默認(rèn)行為减俏。只要將 returnValue 設(shè)置為false,就可以阻止默認(rèn)行為歌溉。來看下面的例子垄懂。

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

跨瀏覽器的事件對象

雖然 DOMIE中的event 對象不同,但基于它們之間的相似性依舊可以拿出跨瀏覽器的方案來痛垛。IEevent 對象的全部信息和方法 DOM對象中都有草慧,只不過實現(xiàn)方式不一樣。不過匙头,這種對應(yīng)關(guān)系讓實現(xiàn)兩種事件模型之間的映射非常容易漫谷。可以對前面介紹的 EventUtil 對象加以增強(qiáng)蹂析,添加如下方法以求同存異舔示。

  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.stopPropagation();
      } else {
        event.cancelBubble = true;
      }
    }
  };

事件類型

UI事件

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>元素中包含所加載頁面的滾動條唇撬。

鼠標(biāo)與滾輪事件

click:在用戶單擊主鼠標(biāo)按鈕(一般是左邊的按鈕)或者按下回車鍵時觸發(fā)。這一點對確保易訪問性很重要展融,意味著onclick事件處理程序既可以通過鍵盤也可以通過鼠標(biāo)執(zhí)行。

dblclick:在用戶雙擊主鼠標(biāo)按鈕(一般是左邊的按鈕)時觸發(fā)豫柬。從技術(shù)上說告希,這個事件并不是DOM2 級事件規(guī)范中規(guī)定的,但鑒于它得到了廣泛支持烧给,所以 DOM3 級事件將其納入了標(biāo)準(zhǔn)燕偶。

mousedown:在用戶按下了任意鼠標(biāo)按鈕時觸發(fā)。不能通過鍵盤觸發(fā)這個事件础嫡。

mouseenter:在鼠標(biāo)光標(biāo)從元素外部首次移動到元素范圍之內(nèi)時觸發(fā)指么。這個事件不冒泡,而且在光標(biāo)移動到后代元素上不會觸發(fā)榴鼎。 DOM2 級事件并沒有定義這個事件伯诬,但 DOM3 級事件將它納入了規(guī)范。 IE巫财、 Firefox 9+Opera支持這個事件盗似。

mouseleave:在位于元素上方的鼠標(biāo)光標(biāo)移動到元素范圍之外時觸發(fā)。這個事件不冒泡平项,而且在光標(biāo)移動到后代元素上不會觸發(fā)赫舒。 DOM2 級事件并沒有定義這個事件,但DOM3 級事件將它納入了規(guī)范闽瓢。IE接癌、 Firefox 9+Opera支持這個事件。

mousemove:當(dāng)鼠標(biāo)指針在元素內(nèi)部移動時重復(fù)地觸發(fā)扣讼。不能通過鍵盤觸發(fā)這個事件缺猛。

mouseout:在鼠標(biāo)指針位于一個元素上方,然后用戶將其移入另一個元素時觸發(fā)届谈。又移入的另一個元素可能位于前一個元素的外部枯夜,也可能是這個元素的子元素。不能通過鍵盤觸發(fā)這個事件艰山。

mouseover:在鼠標(biāo)指針位于一個元素外部湖雹,然后用戶將其首次移入另一個元素邊界之內(nèi)時觸發(fā)。不能通過鍵盤觸發(fā)這個事件曙搬。

mouseup:在用戶釋放鼠標(biāo)按鈕時觸發(fā)摔吏。不能通過鍵盤觸發(fā)這個事件鸽嫂。

頁面上的所有元素都支持鼠標(biāo)事件。除了 mouseentermouseleave征讲,所有鼠標(biāo)事件都會冒泡据某,也可以被取消,而取消鼠標(biāo)事件將會影響瀏覽器的默認(rèn)行為诗箍。取消鼠標(biāo)事件的默認(rèn)行為還會影響其他事件癣籽,因為鼠標(biāo)事件與其他事件是密不可分的關(guān)系。

只有在同一個元素上相繼觸發(fā) mousedownmouseup事件滤祖,才會觸發(fā) click 事件筷狼;如果 mousedownmouseup 中的一個被取消,就不會觸發(fā)click 事件匠童。類似地埂材,只有觸發(fā)兩次 click 事件,才會觸發(fā)一次dblclick 事件汤求。如果有代碼阻止了連續(xù)兩次觸發(fā) click 事件(可能是直接取消click 事件俏险,也可能通過取消 mousedownmouseup間接實現(xiàn)),那么就不會觸發(fā) dblclick事件了扬绪。

這 4 個事件觸發(fā)的順序始終如下:

(1)mousedown

(2) mouseup

(3)click

(4) mousedown

(5) mouseup

(6)click

(7) dblclick

客戶端的坐標(biāo)位置

鼠標(biāo)事件都是在瀏覽器視口中的特定位置上發(fā)生的竖独。這個位置信息保存在事件對象的 clientXclientY屬性中。所有瀏覽器都支持這兩個屬性勒奇,它們的值表示事件發(fā)生時鼠標(biāo)指針在視口中的水平
和垂直坐標(biāo)预鬓。

頁面的坐標(biāo)位置

通過客戶區(qū)坐標(biāo)能夠知道鼠標(biāo)是在視口中什么位置發(fā)生的,而頁面坐標(biāo)通過事件對象的pageXpageY屬性赊颠,能告訴你事件是在頁面中的什么位置發(fā)生的格二。換句話說,這兩個屬性表示鼠標(biāo)光標(biāo)在頁面中的位置竣蹦,因此坐標(biāo)是從頁面本身而非視口的左邊和頂邊計算的顶猜。

屏幕坐標(biāo)位置

鼠標(biāo)事件發(fā)生時,不僅會有相對于瀏覽器窗口的位置痘括,還有一個相對于整個電腦屏幕的位置长窄。而通過 screenXscreenY 屬性就可以確定鼠標(biāo)事件發(fā)生時鼠標(biāo)指針相對于整個屏幕的坐標(biāo)信息。

HTML5 事件

很多瀏覽器出于不同的目的——滿足用戶需求或解決特殊問題纲菌,還實現(xiàn)了一些自定義的事件挠日。HTML5詳盡列出了瀏覽器應(yīng)該支持的所有事件。

contextmenu 事件

為了實現(xiàn)上下文菜單翰舌,開發(fā)人員面臨的主要問題是如何確定應(yīng)該顯示上下文菜單(在Windows 中嚣潜,是右鍵單擊;在 Mac中椅贱,是 Ctrl+單擊)懂算,以及如何屏蔽與該操作關(guān)聯(lián)的默認(rèn)上下文菜單只冻。為解決這個問題,就出現(xiàn)了contextmenu 這個事件计技,用以表示何時應(yīng)該顯示上下文菜單喜德,以便開發(fā)人員取消默認(rèn)的上下文菜單而提供自定義的菜單。

由于contextmenu 事件是冒泡的垮媒,因此可以為document指定一個事件處理程序舍悯,用以處理頁面中發(fā)生的所有此類事件。這個事件的目標(biāo)是發(fā)生用戶操作的元素睡雇。在所有瀏覽器中都可以取消這個事件:在兼容DOM的瀏覽器中贱呐,使用 event.preventDefalut();在IE中入桂,將 event.returnValue的值設(shè)置為false

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

DOMContentLoaded 事件

如前所述驳阎, windowload事件會在頁面中的一切都加載完畢時觸發(fā)抗愁,但這個過程可能會因為要加載的外部資源過多而頗費周折。而DOMContentLoaded事件則在形成完整的DOM樹之后就會觸發(fā)呵晚,不理會圖像蜘腌、 JavaScript 文件、CSS文件或其他資源是否已經(jīng)下載完畢饵隙。

要處理 DOMContentLoaded 事件撮珠,可以為 documentwindow添加相應(yīng)的事件處理程序(盡管這個事件會冒泡到 window,但它的目標(biāo)實際上是document)金矛。來看下面的例子芯急。

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

readystatechange 事件

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

uninitialized(未初始化):對象存在但尚未初始化榕酒。
loading(正在加載):對象正在加載數(shù)據(jù)。
loaded(加載完畢):對象加載數(shù)據(jù)完成故俐。
interactive(交互):可以操作對象了想鹰,但還沒有完全加載。
complete(完成):對象已經(jīng)加載完畢药版。

對于 document 而言辑舷,值為"interactive"readyState會在與 DOMContentLoaded 大致相同的時刻觸發(fā)readystatechange 事件。此時刚陡,DOM 樹已經(jīng)加載完畢惩妇,可以安全地操作它了株汉,因此就會進(jìn)入交互(interactive)階段。但與此同時歌殃,圖像及其他外部文件不一定可用乔妈。

ventUtil.addHandler(document, "readystatechange", function(event){
    if (document.readyState == "interactive"){
        alert("Content loaded");
    }
});

交互階段可能會早于也可能會晚于完成階段出現(xiàn),無法確保順序氓皱。在包含較多外部資源的頁面中路召,交互階段更有可能早于完成階段出現(xiàn);而在頁面中包含較少外部資源的情況下波材,完成階段先于交互階段出現(xiàn)的可能性更大股淡。因此,為了盡可能搶到先機(jī)廷区,有必要同時檢測交互和完成階段唯灵,如下面的例子所示。

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

支持 readystatechange事件的瀏覽器有IE隙轻、Firfox 4+Opera埠帕。

雖然使用 readystatechange可以十分近似地模擬 DOMContentLoaded事件,但它們本質(zhì)上還是不同的玖绿。在不同頁面中敛瓷,load事件與 readystatechange 事件并不能保證以相同的順序觸發(fā)。

內(nèi)存和性能

由于事件處理程序可以為現(xiàn)代 Web應(yīng)用程序提供交互能力斑匪,因此許多開發(fā)人員會不分青紅皂白地向頁面中添加大量的處理程序呐籽。在創(chuàng)建GUI 的語言(如C#)中,為GUI中的每個按鈕添加一個 onclick事件處理程序是司空見慣的事蚀瘸,而且這樣做也不會導(dǎo)致什么問題狡蝶。可是在 JavaScript中苍姜,添加到頁面上的事件處理程序數(shù)量將直接關(guān)系到頁面的整體運行性能牢酵。導(dǎo)致這一問題的原因是多方面的。

首先衙猪,每個函數(shù)都是對象馍乙,都會占用內(nèi)存;內(nèi)存中的對象越多垫释,性能就越差丝格。其次,必須事先指定所有事件處理程序而導(dǎo)致的 DOM 訪問次數(shù)棵譬,會延遲整個頁面的交互就緒時間显蝌。事實上,從如何利用好事件處理程序的
角度出發(fā),還是有一些方法能夠提升性能的曼尊。

事件委托

如果在一個復(fù)雜的Web 應(yīng)用程序中酬诀,對所有可單擊的元素都采用這種方式,那么結(jié)果就會有數(shù)不清的代碼用于添加事件處理程序骆撇。此時瞒御,可以利用事件委托技術(shù)解決這個問題。使用事件委托神郊,只需在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;
    }
});

移除事件處理程序

每當(dāng)將事件處理程序指定給元素時涌乳,運行中的瀏覽器代碼與支持頁面交互的JavaScript代碼之間就會建立一個連接蜻懦。這種連接越多,頁面執(zhí)行起來就越慢夕晓。如前所述宛乃,可以采用事件委托技術(shù),限制建立的連接數(shù)量蒸辆。另外烤惊,在不需要的時候移除事件處理程序,也是解決這個問題的一種方案吁朦。內(nèi)存中留有那些過時不用的“空事件處理程序”(dangling event handler),也是造成Web應(yīng)用程序內(nèi)存與性能問題的主要原因渡贾。

如果你知道某個元素即將被移除逗宜,那么最好手工移除事件處理程序,如下面的例子所示空骚。

btn.onclick = function(){
    //先執(zhí)行某些操作
    btn.onclick = null; //移除事件處理程序
    document.getElementById("myDiv").innerHTML = "Processing...";
}

在此纺讲,我們在設(shè)置<div>innerHTML 屬性之前,先移除了按鈕的事件處理程序囤屹。這樣就確保了內(nèi)存可以被再次利用熬甚,而從 DOM中移除按鈕也做到了干凈利索。注意肋坚,在事件處理程序中刪除按鈕也能阻止事件冒泡乡括。目標(biāo)元素在文檔中是事件冒泡的前提。

參考文獻(xiàn)

《javascript 高級程序設(shè)計3》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末智厌,一起剝皮案震驚了整個濱河市诲泌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌铣鹏,老刑警劉巖敷扫,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異诚卸,居然都是意外死亡葵第,警方通過查閱死者的電腦和手機(jī)绘迁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卒密,“玉大人缀台,你說我怎么就攤上這事≌な埽” “怎么了将硝?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長屏镊。 經(jīng)常有香客問我依疼,道長,這世上最難降的妖魔是什么而芥? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任律罢,我火速辦了婚禮,結(jié)果婚禮上棍丐,老公的妹妹穿的比我還像新娘误辑。我一直安慰自己,他們只是感情好歌逢,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布巾钉。 她就那樣靜靜地躺著,像睡著了一般秘案。 火紅的嫁衣襯著肌膚如雪砰苍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天阱高,我揣著相機(jī)與錄音赚导,去河邊找鬼。 笑死赤惊,一個胖子當(dāng)著我的面吹牛吼旧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播未舟,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼圈暗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了裕膀?” 一聲冷哼從身側(cè)響起厂置,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎魂角,沒想到半個月后昵济,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年访忿,在試婚紗的時候發(fā)現(xiàn)自己被綠了瞧栗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡海铆,死狀恐怖迹恐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卧斟,我是刑警寧澤殴边,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站珍语,受9級特大地震影響锤岸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜板乙,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一是偷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧募逞,春花似錦蛋铆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纠脾,卻和暖如春洪燥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乳乌。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留市咆,地道東北人汉操。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像蒙兰,于是被迫代替她去往敵國和親磷瘤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

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

  • ??JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的。 ??事件挠他,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,495評論 1 11
  • 第13章 事件 1. 事件流 事件流描述的是從頁面中接收事件的順序扳抽。 (1) 事件冒泡 IE 的事件流叫做事件冒泡...
    yinxmm閱讀 949評論 0 17
  • 事件是什么,可以用來做什么,什么時候用到它贸呢? 事件镰烧,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬間。JavaScr...
    茂茂愛吃魚閱讀 1,519評論 0 16
  • JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的楞陷。事件怔鳖,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬...
    LemonnYan閱讀 681評論 0 4
  • 事件流 JavaScript與HTML之間的交互是通過事件實現(xiàn)的。事件固蛾,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互...
    DHFE閱讀 833評論 0 3