国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

DOM 事件詳解

tianhang / 2070人閱讀

摘要:與此同時(shí),我們獲得了回調(diào)函數(shù)的句柄,從而可以隨時(shí)從元素上移除相應(yīng)的事件監(jiān)聽。對象會(huì)被作為第一個(gè)參數(shù)傳遞給事件監(jiān)聽的回調(diào)函數(shù)。

Click、touch、load、drag、change、input、error、risize — 這些都是冗長的DOM(文檔對象模型)事件列表的一部分。事件可以在文檔(Document)結(jié)構(gòu)的任何部分被觸發(fā),觸發(fā)者可以是用戶操作,也可以是瀏覽器本身。事件并不是只是在一處被觸發(fā)和終止;他們在整個(gè)document中流動(dòng),擁有它們自己的生命周期。而這個(gè)生命周期讓DOM事件有更多的用途和可擴(kuò)展性。

作為一個(gè)開發(fā)人員,我們必須要理解DOM事件是如何工作的,然后才能更好的駕馭它,利用它們潛在的優(yōu)勢,開發(fā)出更高交互性的參與體驗(yàn)(engaging experiences)。

反觀我做前端開發(fā)的這么長時(shí)間里,我覺得我從來沒有看到過一個(gè)關(guān)于DOM事件是如何工作的較為直接準(zhǔn)確的解釋。今天我的目標(biāo)就是在這個(gè)課題上給大家一個(gè)清晰的介紹,讓大家能夠更快速的了解它。 我首先會(huì)介紹DOM事件的基本使用方式,然后會(huì)深入挖掘事件內(nèi)部的工作機(jī)制,解釋我們?nèi)绾问褂眠@些機(jī)制來解決一些常見的問題。


監(jiān)聽事件

在過去,主流瀏覽器之間對于如何給DOM節(jié)點(diǎn)添加事件監(jiān)聽有著很大的不一致性。jQuery這樣的前端庫為我們封裝和抽象了這些差異行為,為事件處理帶來了極大的便利。

如今,我們正一步步走向一個(gè)標(biāo)準(zhǔn)化的瀏覽器時(shí)代,我們可以更加安全地使用官方規(guī)范的接口。為了簡單起見,這篇文章將主要介紹在現(xiàn)代瀏覽器中如何管理事件。如果你在為IE8或者更低版本寫JavaScript,我會(huì)推薦你使用 polyfill 或者一些框架(如jQuery)來管理事件監(jiān)聽。

在JavaScript中,我們使用如下的方式為元素添加事件監(jiān)聽:

element.addEventListener(, , );

event-name(string)
這是你想監(jiān)聽的事件的名稱或類型。它可以是任何的標(biāo)準(zhǔn)DOM事件(click, mousedown, touchstart, transitionEnd,等等),當(dāng)然也可以是你自己定義的事件名稱(我們會(huì)在后面介紹自定義事件相關(guān)內(nèi)容)。

callback(function)(回調(diào)函數(shù))
這個(gè)函數(shù)會(huì)在事件觸發(fā)的時(shí)候被調(diào)用。相應(yīng)的事件(event)對象,以及事件的數(shù)據(jù),會(huì)被作為第一個(gè)參數(shù)傳入這個(gè)函數(shù)。

use-capture(boolean)
這個(gè)參數(shù)決定了回調(diào)函數(shù)(callback)是否在“捕獲(capture)”階段被觸發(fā)。不用擔(dān)心,我們稍后會(huì)對此做詳細(xì)的解釋。

var element = document.getElementById("element");
function callback() {
  alert("Hello");
}

// Add listener
element.addEventListener("click", callback);

Demo: addEventListener


移除監(jiān)聽

移除不再使用的事件監(jiān)聽是一個(gè)最佳實(shí)踐(尤其對于長時(shí)間運(yùn)行的Web應(yīng)用)。我們使用element.removeEventListener()方法來移除事件監(jiān)聽:

element.removeEventListener(, , );

但是removeElementListener有一點(diǎn)需要注意的是:你必須要有這個(gè)被綁定的回調(diào)函數(shù)的引用。簡單地調(diào)用element.removeEventListener("click");是不能達(dá)到想要的效果的。

本質(zhì)上來講,如果我們考慮要移除事件監(jiān)聽(我們在長時(shí)間運(yùn)行(long-lived)的應(yīng)用中需要用到),那么我們就需要保留回調(diào)函數(shù)的句柄。意思就是說,我們不能使用匿名函數(shù)作為回調(diào)函數(shù)。

var element = document.getElementById("element");

function callback() {
  alert("Hello once");
  element.removeEventListener("click", callback);
}

// Add listener
element.addEventListener("click", callback);

Demo: removeEventListener


維護(hù)回調(diào)函數(shù)上下文

一個(gè)很容易遇到的問題就是回調(diào)函數(shù)沒有在預(yù)想的運(yùn)行上下文被調(diào)用。讓我們看一個(gè)簡單的例子來解釋一下:

var element = document.getElementById("element");

var user = {
 firstname: "Wilson",
 greeting: function(){
   alert("My name is " + this.firstname);
 }
};

// Attach user.greeting as a callback
element.addEventListener("click", user.greeting);

// alert => "My name is undefined"

Demo: Incorrect callback context


使用匿名函數(shù)(Anonymous Functions)

我們希望回調(diào)函數(shù)中能夠正確的輸出”My name is Wilson”。事實(shí)上,結(jié)果確是”My name is undefined”。為了使得 this.firstName 能夠返回”Wilson”,user.greeting必須在user對象的上下文環(huán)境(context)中被執(zhí)行(這里的運(yùn)行上下文指的是.號(hào)左邊的對象)。

當(dāng)我們將greeting函數(shù)傳給addEventListener方法的時(shí)候,我們傳遞的是一個(gè)函數(shù)的引用;user相應(yīng)的上下文并沒有傳遞過去。運(yùn)行的時(shí)候,這個(gè)回調(diào)函數(shù)實(shí)際上是在element的上下文中被執(zhí)行了,也就是說,在運(yùn)行的時(shí)候,this指向的是element,而不是user。所以this.firstName是undefined。

有兩種方式可以避免這種上下文錯(cuò)誤的問題。第一種方法,我們可以在一個(gè)匿名函數(shù)內(nèi)部調(diào)用user.greeting()方法,從而獲得正確的函數(shù)執(zhí)行上下文(user)。

element.addEventListener("click", function() {
  user.greeting();
  // alert => "My name is Wilson"
});

Demo:Anonymouse functions


使用Function.prototype.bind

上一種方式并不是非常好,因?yàn)槲覀儾荒塬@得回調(diào)函數(shù)的句柄以便后面通過.removeEventListener()移除事件監(jiān)聽。另外,這種方式也比較丑陋。。我更喜歡使用.bind()方法(做為ECMAScript 5的標(biāo)準(zhǔn)內(nèi)建在所有的函數(shù)對象中)來生成一個(gè)新的函數(shù)(被綁定過的函數(shù)),這個(gè)函數(shù)會(huì)在指定的上下文中被執(zhí)行。然后我們將這個(gè)被綁定過的函數(shù)作為參數(shù)傳給.addEventListener()的回調(diào)函數(shù)。

// Overwrite the original function with
// one bound to the context of "user"
user.greeting = user.greeting.bind(user);

// Attach the bound user.greeting as a callback
button.addEventListener("click", user.greeting);
與此同時(shí),我們獲得了回調(diào)函數(shù)的句柄,從而可以隨時(shí)從元素上移除相應(yīng)的事件監(jiān)聽。
```javascrips
button.removeEventListener("click", user.greeting);

DEMO:Function.ptototype.bind
想獲取Function.prototype.bind的更多信息,請點(diǎn)擊的瀏覽器支持頁面,以及polyfill的介紹。


Event 對象

Event對象在event第一次觸發(fā)的時(shí)候被創(chuàng)建出來,并且一直伴隨著事件在DOM結(jié)構(gòu)中流轉(zhuǎn)的整個(gè)生命周期。event對象會(huì)被作為第一個(gè)參數(shù)傳遞給事件監(jiān)聽的回調(diào)函數(shù)。我們可以通過這個(gè)event對象來獲取到大量當(dāng)前事件相關(guān)的信息:
* type (String) — 事件的名稱
* target (node) — 事件起源的DOM節(jié)點(diǎn)
* currentTarget?(node) — 當(dāng)前回調(diào)函數(shù)被觸發(fā)的DOM節(jié)點(diǎn)(后面會(huì)做比較詳細(xì)的介紹)
* bubbles (boolean) — 指明這個(gè)事件是否是一個(gè)冒泡事件(接下來會(huì)做解釋)
* preventDefault(function) — 這個(gè)方法將阻止瀏覽器中用戶代理對當(dāng)前事件的相關(guān)默認(rèn)行為被觸發(fā)。比如阻止 < a > 元素的click事件加載一個(gè)新的頁面
* stopPropagation (function) — 這個(gè)方法將阻止當(dāng)前事件鏈上后面的元素的回調(diào)函數(shù)被觸發(fā),當(dāng)前節(jié)點(diǎn)上針對此事件的其他回調(diào)函數(shù)依然會(huì)被觸發(fā)。(我們稍后會(huì)詳細(xì)介紹。)
* stopImmediatePropagation (function) — 這個(gè)方法將阻止當(dāng)前事件鏈上所有的回調(diào)函數(shù)被觸發(fā),也包括當(dāng)前節(jié)點(diǎn)上針對此事件已綁定的其他回調(diào)函數(shù)。
* cancelable (boolean) — 這個(gè)變量指明這個(gè)事件的默認(rèn)行為是否可以通過調(diào)用event.preventDefault來阻止。也就是說,只有cancelable為true的時(shí)候,調(diào)用event.preventDefault才能生效。
* defaultPrevented (boolean) — 這個(gè)狀態(tài)變量表明當(dāng)前事件對象的preventDefault方法是否被調(diào)用過
* isTrusted (boolean) — 如果一個(gè)事件是由設(shè)備本身(如瀏覽器)觸發(fā)的,而不是通過JavaScript模擬合成的,那個(gè)這個(gè)事件被稱為可信任的(trusted)
*eventPhase (number) — 這個(gè)數(shù)字變量表示當(dāng)前這個(gè)事件所處的階段(phase):none(0), capture(1),target(2),bubbling(3)。我們會(huì)在下一個(gè)部分介紹事件的各個(gè)階段
*timestamp (number) — 事件發(fā)生的時(shí)間

此外事件對象還可能擁有很多其他的屬性,但是他們都是針對特定的event的。比如,鼠標(biāo)事件包含clientX和clientY屬性來表明鼠標(biāo)在當(dāng)前視窗的位置。

我們可以使用熟悉的瀏覽器的調(diào)試工具或者通過console.log在控制臺(tái)輸出來更具體地查看事件對象以及它的屬性。


事件階段(Event Phases)

當(dāng)一個(gè)DOM事件被觸發(fā)的時(shí)候,它并不只是在它的起源對象上觸發(fā)一次,而是會(huì)經(jīng)歷三個(gè)不同的階段。簡而言之:事件一開始從文檔的根節(jié)點(diǎn)流向目標(biāo)對象(捕獲階段),然后在目標(biāo)對向上被觸發(fā)(目標(biāo)階段),之后再回溯到文檔的根節(jié)點(diǎn)(冒泡階段)。

(圖片來源:W3C)

Demo: Slow motion event path

事件捕獲階段(Capture Phase)

事件的第一個(gè)階段是捕獲階段。事件從文檔的根節(jié)點(diǎn)出發(fā),隨著DOM樹的結(jié)構(gòu)向事件的目標(biāo)節(jié)點(diǎn)流去。途中經(jīng)過各個(gè)層次的DOM節(jié)點(diǎn),并在各節(jié)點(diǎn)上觸發(fā)捕獲事件,直到到達(dá)事件的目標(biāo)節(jié)點(diǎn)。捕獲階段的主要任務(wù)是建立傳播路徑,在冒泡階段,事件會(huì)通過這個(gè)路徑回溯到文檔跟節(jié)點(diǎn)。

正如文章一開始的地方提到,我們可以通過將addEventListener的第三個(gè)參數(shù)設(shè)置成true來為事件的捕獲階段添加監(jiān)聽回調(diào)函數(shù)。在實(shí)際應(yīng)用中,我們并沒有太多使用捕獲階段監(jiān)聽的用例,但是通過在捕獲階段對事件的處理,我們可以阻止類似clicks事件在某個(gè)特定元素上被觸發(fā)。

var form = document.querySelector("form");

form.addEventListener("click", function(event) {
  event.stopPropagation();
}, true); // Note: "true"

如果你對這種用法不是很了解的話,最好還是將useCapture設(shè)置為false或者undefined,從而在冒泡階段對事件進(jìn)行監(jiān)聽。


目標(biāo)階段(Target Phase)

當(dāng)事件到達(dá)目標(biāo)節(jié)點(diǎn)的,事件就進(jìn)入了目標(biāo)階段。事件在目標(biāo)節(jié)點(diǎn)上被觸發(fā),然后會(huì)逆向回流,直到傳播至最外層的文檔節(jié)點(diǎn)。

對于多層嵌套的節(jié)點(diǎn),鼠標(biāo)和指針事件經(jīng)常會(huì)被定位到最里層的元素上。假設(shè),你在一個(gè)< div >元素上設(shè)置了click事件的監(jiān)聽函數(shù),而用戶點(diǎn)擊在了這個(gè)< div >元素內(nèi)部的< p >元素上,那么< p >元素就是這個(gè)事件的目標(biāo)元素。事件冒泡讓我們可以在這個(gè)< div >(或者更上層的)元素上監(jiān)聽click事件,并且事件傳播過程中觸發(fā)回調(diào)函數(shù)。


冒泡階段(Bubble Phase)

事件在目標(biāo)元素上觸發(fā)后,并不在這個(gè)元素上終止。它會(huì)隨著DOM樹一層層向上冒泡,直到到達(dá)最外層的根節(jié)點(diǎn)。也就是說,同一個(gè)事件會(huì)依次在目標(biāo)節(jié)點(diǎn)的父節(jié)點(diǎn),父節(jié)點(diǎn)的父節(jié)點(diǎn)。。。直到最外層的節(jié)點(diǎn)上被觸發(fā)。

將DOM結(jié)構(gòu)想象成一個(gè)洋蔥,事件目標(biāo)是這個(gè)洋蔥的中心。在捕獲階段,事件從最外層鉆入洋蔥,穿過途徑的每一層。在到達(dá)中心后,事件被觸發(fā)(目標(biāo)階段)。然后事件開始回溯,再次經(jīng)過每一層返回(冒泡階段)。當(dāng)?shù)竭_(dá)洋蔥表面的時(shí)候,這次旅程就結(jié)束了。

冒泡過程非常有用。它將我們從對特定元素的事件監(jiān)聽中釋放出來,相反,我們可以監(jiān)聽DOM樹上更上層的元素,等待事件冒泡的到達(dá)。如果沒有事件冒泡,在某些情況下,我們需要監(jiān)聽很多不同的元素來確保捕獲到想要的事件。

Demo: Identifying event phases

絕大多數(shù)事件會(huì)冒泡,但并非所有的。當(dāng)你發(fā)現(xiàn)有些事件不冒泡的時(shí)候,它肯定是有原因的。不相信?你可以查看一下相應(yīng)的規(guī)范說明。


停止傳播(Stopping Propagation)

可以通過調(diào)用事件對象的stopPropagation方法,在任何階段(捕獲階段或者冒泡階段)中斷事件的傳播。此后,事件不會(huì)在后面?zhèn)鞑ミ^程中的經(jīng)過的節(jié)點(diǎn)上調(diào)用任何的監(jiān)聽函數(shù)。

child.addEventListener("click", function(event) {
 event.stopPropagation();
});

parent.addEventListener("click", function(event) {
 // If the child element is clicked
 // this callback will not fire
});

調(diào)用event.stopPropagation()不會(huì)阻止當(dāng)前節(jié)點(diǎn)上此事件其他的監(jiān)聽函數(shù)被調(diào)用。如果你希望阻止當(dāng)前節(jié)點(diǎn)上的其他回調(diào)函數(shù)被調(diào)用的話,你可以使用更激進(jìn)的event.stopImmediatePropagation()方法。

child.addEventListener("click", function(event) {
 event.stopImmediatePropagation();
});

child.addEventListener("click", function(event) {
 // If the child element is clicked
 // this callback will not fire
});

Demo:Stopping propagation


阻止瀏覽器默認(rèn)行為

當(dāng)特定事件發(fā)生的時(shí)候,瀏覽器會(huì)有一些默認(rèn)的行為作為反應(yīng)。最常見的事件不過于link被點(diǎn)擊。當(dāng)一個(gè)click事件在一個(gè)< a >元素上被觸發(fā)時(shí),它會(huì)向上冒泡直到DOM結(jié)構(gòu)的最外層document,瀏覽器會(huì)解釋href屬性,并且在窗口中加載新地址的內(nèi)容。

在web應(yīng)用中,開發(fā)人員經(jīng)常希望能夠自行管理導(dǎo)航(navigation)信息,而不是通過刷新頁面。為了實(shí)現(xiàn)這個(gè)目的,我們需要阻止瀏覽器針對點(diǎn)擊事件的默認(rèn)行為,而使用我們自己的處理方式。這時(shí),我們就需要調(diào)用event.preventDefault().

anchor.addEventListener("click", function(event) {
  event.preventDefault();
  // Do our own thing
});
我們可以阻止瀏覽器的很多其他默認(rèn)行為。比如,我們可以在HTML5游戲中阻止敲擊空格時(shí)的頁面滾動(dòng)行為,或者阻止文本選擇框的點(diǎn)擊行為。

調(diào)用event.stopPropagation()只會(huì)阻止傳播鏈中后續(xù)的回調(diào)函數(shù)被觸發(fā)。它不會(huì)阻止瀏覽器的自身的行為。

[Demo:Preventing default vehaviour](http://jsbin.com/ibotap/1/edit)

---

###自定義事件
瀏覽器并不是唯一能觸發(fā)DOM事件的載體。我們可以創(chuàng)建自定義的事件并把它們分派給你文檔中的任意節(jié)點(diǎn)。這些自定義的事件和通常的DOM事件有相同的行為。
```javascrips
var myEvent = new CustomEvent("myevent", {
  detail: {
    name: "Wilson"
  },
  bubbles: true,
  cancelable: false
});

// Listen for "myevent" on an element
myElement.addEventListener("myevent", function(event) {
  alert("Hello " + event.detail.name);
});

// Trigger the "myevent"
myElement.dispatchEvent(myEvent);

在元素上合成不可信任的(untrusted)DOM事件(如click)來模擬用戶操作也是可行的。這個(gè)在對DOM相關(guān)的代碼庫進(jìn)行測試的時(shí)候特別有用。如果你對此感興趣的話,在Mozilla Developer Network上有一篇相關(guān)的文章。

幾個(gè)注意點(diǎn):

CustomEvent接口在IE 8以及IE更低版本不可用

來自Twitter的Flight框架使用了自定義事件進(jìn)行模塊間通信。它強(qiáng)調(diào)了一種高度解耦的模塊化架構(gòu)。
Demo:Custom events


代理事件監(jiān)聽

代理事件監(jiān)聽可以讓你使用一個(gè)事件監(jiān)聽器去監(jiān)聽大量的DOM節(jié)點(diǎn)的事件,在這種情況下,它是一種更加方便并且高性能的事件監(jiān)聽方法。舉例來說,如果有一個(gè)列表< ul >包含了100個(gè)子元素< li >,它們都需要對click事件做出相似的響應(yīng),那么我們可能需要查詢這100個(gè)子元素,并分別為他們添加上事件監(jiān)聽器。這樣的話,我們就會(huì)產(chǎn)生100個(gè)獨(dú)立的事件監(jiān)聽器。如果有一個(gè)新的元素被添加進(jìn)去,我們也需要為它添加同樣的監(jiān)聽器。這種方式不但代價(jià)比較大,維護(hù)起來也比較麻煩。

代理事件監(jiān)聽可以讓我們更簡單的處理這種情況。我們不去監(jiān)聽所有的子元素的click事件,相反,我們監(jiān)聽他們的父元素< ul >。當(dāng)一個(gè)< li >元素被點(diǎn)擊的時(shí)候,這個(gè)事件會(huì)向上冒泡至< ul >,觸發(fā)回調(diào)函數(shù)。我們可以通過檢查事件的event.target屬性來判斷具體是哪一個(gè)< li >被點(diǎn)擊了。下面我們舉個(gè)簡單的例子來說明:

var list = document.querySelector("ul");

list.addEventListener("click", function(event) {
  var target = event.target;

  while (target.tagName !== "LI") {
    target = target.parentNode;
    if (target === list) return;
  }

  // Do stuff here
});

這樣就好多了,我們僅僅使用了一個(gè)上層的事件監(jiān)聽器,并且我們不需要在為添加元素而考慮它的事件監(jiān)聽問題。這個(gè)概念很簡單,但是非常有用。

但是我并不建議你在你的項(xiàng)目中使用上面的這個(gè)粗糙的實(shí)現(xiàn)。相反,使用一個(gè)事件代理的JavaScript庫是更好的選擇,比如 FT Lab的ftdomdelegate。如果你在使用jQuery,你可以在調(diào)用.on()方法的時(shí)候,將一個(gè)選擇器作為第二個(gè)參數(shù)的方式來輕松的實(shí)現(xiàn)事件代理。

// Not using event delegation
$("li").on("click", function(){});

// Using event delegation
$("ul").on("click", "li", function(){});

Demo: Delegate event listeners


一些有用的事件

load
load事件可以在任何資源(包括被依賴的資源)被加載完成時(shí)被觸發(fā),這些資源可以是圖片,css,腳本,視頻,音頻等文件,也可以是document或者window。

image.addEventListener("load", function(event) {
  image.classList.add("has-loaded");
});

Demo:Image load event

onbeforeunload
window.onbeforeunload讓開發(fā)人員可以在想用戶離開一個(gè)頁面的時(shí)候進(jìn)行確認(rèn)。這個(gè)在有些應(yīng)用中非常有用,比如用戶不小心關(guān)閉瀏覽器的tab,我們可以要求用戶保存他的修改和數(shù)據(jù),否則將會(huì)丟失他這次的操作。

window.onbeforeunload = function() {
  if (textarea.value != textarea.defaultValue) {
    return "Do you want to leave the page and discard changes?";
  }
};

需要注意的是,對頁面添加onbeforeunload處理會(huì)導(dǎo)致瀏覽器不對頁面進(jìn)行緩存?,這樣會(huì)影響頁面的訪問響應(yīng)時(shí)間。 同時(shí),onbeforeunload的處理函數(shù)必須是同步的(synchronous)。

Demo: onbeforeunload


在手機(jī)Safari上阻止窗口抖動(dòng)

在Financial Times中,我們使用了一個(gè)簡單的event.preventDefault相關(guān)的技巧防止了Safari在滾動(dòng)的時(shí)候出現(xiàn)的抖動(dòng)。(手機(jī)端開發(fā)接觸的不多,所以可能有所誤解,如果錯(cuò)誤,請了解的同學(xué)提點(diǎn)一下。)

document.body.addEventListener("touchmove", function(event) {
 event.preventDefault();
});

需要提醒的是這個(gè)操作同時(shí)也會(huì)阻礙正常的原生滾動(dòng)條的功能(比如使用overflow:scroll)。為了使得內(nèi)部的子元素在需要的時(shí)候能夠使用滾動(dòng)條的功能,我們在支持滾動(dòng)的元素上監(jiān)聽這個(gè)事件,并且在事件對象上設(shè)置一個(gè)標(biāo)識(shí)屬性。在回調(diào)函數(shù)中,在document這一層,我們通過對這個(gè)擴(kuò)展的isScrollable標(biāo)識(shí)屬性來判斷是否對觸摸事件阻止默認(rèn)的滾動(dòng)行為。

// Lower down in the DOM we set a flag
scrollableElement.addEventListener("touchmove", function(event) {
 event.isScrollable = true;
});

// Higher up the DOM we check for this flag to decide
// whether to let the browser handle the scroll
document.addEventListener("touchmove", function(event) {
 if (!event.isScrollable) event.preventDefault();
});

在IE8即一下的版本中,我們是不能操作事件對象的。作為一個(gè)變通方案,我們將一些擴(kuò)展的屬性設(shè)置在event.target節(jié)點(diǎn)對向上。


resize

在一些復(fù)雜的響應(yīng)式布局中,對window對象監(jiān)聽resize事件是非常常用的一個(gè)技巧。僅僅通過css來達(dá)到想要的布局效果比較困難。很多時(shí)候,我們需要使用JavaScript來計(jì)算并設(shè)置一個(gè)元素的大小。

window.addEventListener("resize", function() {
  // update the layout
});

我推薦使用防抖動(dòng)的回調(diào)函數(shù)來統(tǒng)一調(diào)整回調(diào)的頻率,從而防止布局上極端抖動(dòng)的情況出現(xiàn)。

Demo: Window resizing


transitionend

現(xiàn)在在項(xiàng)目中,我們經(jīng)常使用CSS來執(zhí)行一些轉(zhuǎn)換和動(dòng)畫的效果。有些時(shí)候,我們還是需要知道一個(gè)特定動(dòng)畫的結(jié)束時(shí)間。

el.addEventListener("transitionEnd", function() {
 // Do stuff
});

一些注意點(diǎn):

如果你使用@keyframe動(dòng)畫,那么使用animationEnd事件,而不是transitionEnd。
跟很多事件一樣,transitionEnd也向上冒泡。記得在子節(jié)點(diǎn)上調(diào)用event.stopPropagation()或者檢查event.target來防止回調(diào)函數(shù)在不該被調(diào)用的時(shí)候被調(diào)用。
事件名目前還是被各種供應(yīng)商添加了不同的前綴(比如webkitTransitionEnd, msTransitionEnd等等)。使用類似于Modernizr的庫來獲取正確的事件前綴。
Demo:Transition end


animtioniteration

animationiteration事件會(huì)在當(dāng)前的動(dòng)畫元素完成一個(gè)動(dòng)畫迭代的時(shí)候被觸發(fā)。這個(gè)事件非常有用,特別是當(dāng)我們想在某個(gè)迭代完成后停止一個(gè)動(dòng)畫,但又不是在動(dòng)畫過程中打斷它。

function start() {
  div.classList.add("spin");
}

function stop() {
  div.addEventListener("animationiteration", callback);

  function callback() {
    div.classList.remove("spin");
    div.removeEventListener("animationiteration", callback);
  }
}

如果你感興趣的話,我在博客中有另一篇關(guān)于animationiteration事件的文章。

Demo:Animation iteration


error

當(dāng)我們的應(yīng)用在加載資源的時(shí)候發(fā)生了錯(cuò)誤,我們很多時(shí)候需要去做點(diǎn)什么,尤其當(dāng)用戶處于一個(gè)不穩(wěn)定的網(wǎng)絡(luò)情況下。Financial Times中,我們使用error事件來監(jiān)測文章中的某些圖片加載失敗,從而立刻隱藏它。由于“DOM Leven 3 Event”規(guī)定重新定義了error事件不再冒泡,我們可以使用如下的兩種方式來處理這個(gè)事件。

imageNode.addEventListener("error", function(event) {
  image.style.display = "none";
});

不幸的是,addEventListener并不能處理所有的情況。我的同事Kornel給了我一個(gè)很好的例子,說明確保圖片加載錯(cuò)誤回調(diào)函數(shù)被執(zhí)行的唯一方式是使用讓人詬病內(nèi)聯(lián)事件處理函數(shù)(inline event handlers)。



原因是你不能確定綁定error事件處理函數(shù)的代碼會(huì)在error事件發(fā)生之前被執(zhí)行。而使用內(nèi)聯(lián)處理函數(shù)意味著在標(biāo)簽被解析并且請求圖片的時(shí)候,error監(jiān)聽器也將并綁定。

Demo:Image error


從事件模型中學(xué)到

從事件模型的成功上,我們可以學(xué)到很多。我們可以在我們的項(xiàng)目中使用類似的解耦的概念。應(yīng)用中的模塊可以有很高的很復(fù)雜度,只要它的復(fù)雜度被封裝隱藏在一套簡單的接口背后。很多前端框架(比如Backbone.js)都是重度基于事件的,使用發(fā)布-訂閱(publish and subscribe)的方式來處理跨模塊間的通信,這點(diǎn)跟DOM非常相似。

基于事件的架構(gòu)是極好的。它提供給我們一套非常簡單通用的接口,通過針對這套接口的開發(fā),我們能完成適應(yīng)成千上萬不同設(shè)備的應(yīng)用。通過事件,設(shè)備們能準(zhǔn)確地告訴我們正在發(fā)生的事情以及發(fā)生的時(shí)間,讓我們隨心所欲地做出響應(yīng)。我們不再顧慮場景背后具體發(fā)生的事情,而是通過一個(gè)更高層次的抽象來寫出更加令人驚艷的應(yīng)用。


進(jìn)一步閱讀

“Document Object Model Level 3 Events Specification," W3C

“Graphical representation of an event dispatched in a DOM tree using the DOM event flow” (image) W3C

“Event,” Mozilla Developer Network

“DOM Design Tricks II,” J. David Eisenberg, A List Apart

“Event compatibility tables,” Quirksmode
特別感謝 Kornel對這篇文章做出的精彩的技術(shù)審查。


原文鏈接: smashingmagazine
轉(zhuǎn)載自: 伯樂在線 - Owen Chen

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/77961.html

相關(guān)文章

  • Dom 事件詳解

    摘要:級(jí)事件規(guī)定事件流包括三個(gè)階段事件捕獲目標(biāo)事件事件冒泡。返回布爾值,指示事件是否可擁可取消的默認(rèn)動(dòng)作。返回其事件監(jiān)聽器觸發(fā)該事件的元素。返回當(dāng)前對象表示的事件的名稱。不再派發(fā)事件常用于阻止事件冒泡。 事件是 JavaScript 與 HTML 交互的基礎(chǔ)。要實(shí)現(xiàn)用戶與頁面的交互,先要對目標(biāo)元素綁定特定的事件、設(shè)置事件處理函數(shù),然后用戶觸發(fā)事件,事件處理函數(shù)執(zhí)行,產(chǎn)生交互效果。 DOM 事...

    xiaokai 評(píng)論0 收藏0
  • Dom 事件詳解

    摘要:級(jí)事件規(guī)定事件流包括三個(gè)階段事件捕獲目標(biāo)事件事件冒泡。返回布爾值,指示事件是否可擁可取消的默認(rèn)動(dòng)作。返回其事件監(jiān)聽器觸發(fā)該事件的元素。返回當(dāng)前對象表示的事件的名稱。不再派發(fā)事件常用于阻止事件冒泡。 事件是 JavaScript 與 HTML 交互的基礎(chǔ)。要實(shí)現(xiàn)用戶與頁面的交互,先要對目標(biāo)元素綁定特定的事件、設(shè)置事件處理函數(shù),然后用戶觸發(fā)事件,事件處理函數(shù)執(zhí)行,產(chǎn)生交互效果。 DOM 事...

    CodeSheep 評(píng)論0 收藏0
  • Dom 事件詳解

    摘要:級(jí)事件規(guī)定事件流包括三個(gè)階段事件捕獲目標(biāo)事件事件冒泡。返回布爾值,指示事件是否可擁可取消的默認(rèn)動(dòng)作。返回其事件監(jiān)聽器觸發(fā)該事件的元素。返回當(dāng)前對象表示的事件的名稱。不再派發(fā)事件常用于阻止事件冒泡。 事件是 JavaScript 與 HTML 交互的基礎(chǔ)。要實(shí)現(xiàn)用戶與頁面的交互,先要對目標(biāo)元素綁定特定的事件、設(shè)置事件處理函數(shù),然后用戶觸發(fā)事件,事件處理函數(shù)執(zhí)行,產(chǎn)生交互效果。 DOM 事...

    DevTalking 評(píng)論0 收藏0
  • JavaScript學(xué)習(xí)總結(jié)(九)事件詳解

    摘要:布爾值表示捕獲階段調(diào)用事件處理程序,表示冒泡階段通過對象的方法,也可以定義事件的回調(diào)函數(shù)。對象會(huì)被作為第一個(gè)參數(shù)傳遞給事件監(jiān)聽的回調(diào)函數(shù)。布爾默認(rèn)值是,當(dāng)設(shè)置成時(shí)用以取消事件的默認(rèn)行為與中的相同。 其實(shí)這篇文章挺早之前就寫了,但是由于sf保存方面的bug,所以當(dāng)時(shí)寫了一大堆,結(jié)果沒保存,覺得這個(gè)沒寫完是個(gè)不小的遺憾,今天正好有空,就給補(bǔ)充下了,也正好給我的javascript學(xué)習(xí)總結(jié)做...

    LiveVideoStack 評(píng)論0 收藏0
  • JavaScript系列之事件詳解

    摘要:響應(yīng)某個(gè)事件的函數(shù)就叫事件處理程序或事件偵聽器。為事件指定事件處理程序的方法主要有種。事件處理程序事件直接加在元素上。事件委托利用冒泡的原理,把事件加到父元素或祖先元素上,觸發(fā)執(zhí)行效果,解決事件處理程序過多問題。事件委托優(yōu)點(diǎn)提高性能。 JavaScript簡單入門可以看看我丑丑的Github博客JavaScript簡單入門 事件 JavaScript與HTML之間的交互是通過事件實(shí)現(xiàn)的...

    pakolagij 評(píng)論0 收藏0
  • js事件詳解

    摘要:使用級(jí)方法指定的事件處理程序被認(rèn)為是元素的方法。用于立即停止事件在中的傳播,取消進(jìn)一步的事件捕獲或冒泡。捕獲事件目標(biāo)對象冒泡只有在事件處理程序執(zhí)行期間,對象才會(huì)存在,執(zhí)行完成后,對象就會(huì)被銷毀。 引用 事件是我認(rèn)為前端最特別的地方,這是唯一其他語言不一樣的地方,我們通過它與頁面進(jìn)行交互。 事件流 事件流描述的是從頁面中接收事件的順序。IE和網(wǎng)景團(tuán)隊(duì)提出流相反的事件流概念。IE事件流是事...

    AlienZHOU 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<