一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

最詳細(xì)的JavaScript和事件解讀 – 碼農(nóng)網(wǎng)

 看見就非常 2015-03-20

與瀏覽器進(jìn)行交互的時候瀏覽器就會觸發(fā)各種事件。比如當(dāng)我們打開某一個網(wǎng)頁的時候,瀏覽器加載完成了這個網(wǎng)頁,就會觸發(fā)一個 load 事件;當(dāng)我們點擊頁面中的某一個“地方”,瀏覽器就會在那個“地方”觸發(fā)一個 click 事件。

這樣,我們就可以編寫 JavaScript,通過監(jiān)聽某一個事件,來實現(xiàn)某些功能擴(kuò)展。例如監(jiān)聽 load 事件,顯示歡迎信息,那么當(dāng)瀏覽器加載完一個網(wǎng)頁之后,就會顯示歡迎信息。

下面就來介紹一下事件。

基礎(chǔ)事件操作

監(jiān)聽事件

瀏覽器會根據(jù)某些操作觸發(fā)對應(yīng)事件,如果我們需要針對某種事件進(jìn)行處理,則需要監(jiān)聽這個事件。監(jiān)聽事件的方法主要有以下幾種:

HTML 內(nèi)聯(lián)屬性(避免使用)

HTML 元素里面直接填寫事件有關(guān)屬性,屬性值為 JavaScript 代碼,即可在觸發(fā)該事件的時候,執(zhí)行屬性值的內(nèi)容。

例如:

<button onclick="alert('你點擊了這個按鈕');">點擊這個按鈕</button>

onclick 屬性表示觸發(fā) click,屬性值的內(nèi)容(JavaScript 代碼)會在單擊該 HTML 節(jié)點時執(zhí)行。

顯而易見,使用這種方法,JavaScript 代碼與 HTML 代碼耦合在了一起,不便于維護(hù)和開發(fā)。所以除非在必須使用的情況(例如統(tǒng)計鏈接點擊數(shù)據(jù))下,盡量避免使用這種方法。

DOM 屬性綁定

也可以直接設(shè)置 DOM 屬性來指定某個事件對應(yīng)的處理函數(shù),這個方法比較簡單:

element.onclick = function(event){
    alert('你點擊了這個按鈕');
};

上面代碼就是監(jiān)聽 element 節(jié)點的 click 事件。它比較簡單易懂,而且有較好的兼容性。但是也有缺陷,因為直接賦值給對應(yīng)屬性,如果你在后面代碼中再次為 element 綁定一個回調(diào)函數(shù),會覆蓋掉之前回調(diào)函數(shù)的內(nèi)容。

雖然也可以用一些方法實現(xiàn)多個綁定,但還是推薦下面的標(biāo)準(zhǔn)事件監(jiān)聽函數(shù)。

使用事件監(jiān)聽函數(shù)

標(biāo)準(zhǔn)的事件監(jiān)聽函數(shù)如下:

element.addEventListener(<event-name>, <callback>, <use-capture>);

表示在 element 這個對象上面添加一個事件監(jiān)聽器,當(dāng)監(jiān)聽到有 <event-name> 事件發(fā)生的時候,調(diào)用 <callback> 這個回調(diào)函數(shù)。至于 <use-capture> 這個參數(shù),表示該事件監(jiān)聽是在“捕獲”階段中監(jiān)聽(設(shè)置為 true)還是在“冒泡”階段中監(jiān)聽(設(shè)置為 false)。關(guān)于捕獲和冒泡,我們會在下面講解。

用標(biāo)準(zhǔn)事件監(jiān)聽函數(shù)改寫上面的例子:

var btn = document.getElementsByTagName('button');
btn[0].addEventListener('click', function() {
    alert('你點擊了這個按鈕');
}, false);

這里最好是為 HTML 結(jié)構(gòu)定義個 id 或者 class 屬性,方便選擇,在這里只作為演示使用。

Demo:

移除事件監(jiān)聽

當(dāng)我們?yōu)槟硞€元素綁定了一個事件,每次觸發(fā)這個事件的時候,都會執(zhí)行事件綁定的回調(diào)函數(shù)。如果我們想解除綁定,需要使用 removeEventListener 方法:

element.removeEventListener(<event-name>, <callback>, <use-capture>);

需要注意的是,綁定事件時的回調(diào)函數(shù)不能是匿名函數(shù),必須是一個聲明的函數(shù),因為解除事件綁定時需要傳遞這個回調(diào)函數(shù)的引用,才可以斷開綁定。例如:

var fun = function() {
    // function logic
};

element.addEventListener('click', fun, false);
element.removeEventListener('click', fun, false);

Demo:

事件觸發(fā)過程

在上面大體了解了事件是什么、如何監(jiān)聽并執(zhí)行某些操作,但我們對事件觸發(fā)整個過程還不夠了解。

下圖就是事件的觸發(fā)過程,借用了 W3C 的圖片

捕獲階段(Capture Phase)

當(dāng)我們在 DOM 樹的某個節(jié)點發(fā)生了一些操作(例如單擊、鼠標(biāo)移動上去),就會有一個事件發(fā)射過去。這個事件從 Window 發(fā)出,不斷經(jīng)過下級節(jié)點直到目標(biāo)節(jié)點。在到達(dá)目標(biāo)節(jié)點之前的過程,就是捕獲階段(Capture Phase)。

所有經(jīng)過的節(jié)點,都會觸發(fā)這個事件。捕獲階段的任務(wù)就是建立這個事件傳遞路線,以便后面冒泡階段順著這條路線返回 Window。

監(jiān)聽某個在捕獲階段觸發(fā)的事件,需要在事件監(jiān)聽函數(shù)傳遞第三個參數(shù) true

element.addEventListener(<event-name>, <callback>, true);

但一般使用時我們往往傳遞 false,會在后面說明原因。

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

當(dāng)事件跑啊跑,跑到了事件觸發(fā)目標(biāo)節(jié)點那里,最終在目標(biāo)節(jié)點上觸發(fā)這個事件,就是目標(biāo)階段。

需要注意的時,事件觸發(fā)的目標(biāo)總是最底層的節(jié)點。比如你點擊一段文字,你以為你的事件目標(biāo)節(jié)點在 div 上,但實際上觸發(fā)在 <p>、<span> 等子節(jié)點上。例如:

在 Demo 中,我監(jiān)聽單擊事件,將目標(biāo)節(jié)點的 tag name 彈出。當(dāng)你點擊加粗字體時,事件的目標(biāo)節(jié)點就為最底層的<strong> 節(jié)點。

冒泡階段(Bubbling Phase)

當(dāng)事件達(dá)到目標(biāo)節(jié)點之后,就會沿著原路返回,由于這個過程類似水泡從底部浮到頂部,所以稱作冒泡階段。

在實際使用中,你并不需要把事件監(jiān)聽函數(shù)準(zhǔn)確綁定到最底層的節(jié)點也可以正常工作。比如在上例,你想為這個<div> 綁定單擊時的回調(diào)函數(shù),你無須為這個 <div> 下面的所有子節(jié)點全部綁定單擊事件,只需要為 <div>這一個節(jié)點綁定即可。因為發(fā)生它子節(jié)點的單擊事件,都會冒泡上去,發(fā)生在 <div> 上面。

針對這三個階段,wilsonpage 做了一個非常棒的 Demo,可以看下:

為什么不用第三個參數(shù) true

介紹完上面三個事件觸發(fā)階段,我們來看下這個問題。

所有介紹事件的文章都會說,在使用 addEventListener 函數(shù)來監(jiān)聽事件時,第三個參數(shù)設(shè)置為 false,這樣監(jiān)聽事件時只會監(jiān)聽冒泡階段發(fā)生的事件。

這是因為 IE 瀏覽器不支持在捕獲階段監(jiān)聽事件,為了統(tǒng)一而設(shè)置的,畢竟 IE 瀏覽器的份額是不可忽略的。

IE 瀏覽器在事件這方面與標(biāo)準(zhǔn)還有一些其他的差異,我們會在后面集中介紹。

使用事件代理(Event Delegate)提升性能

因為事件有冒泡機(jī)制,所有子節(jié)點的事件都會順著父級節(jié)點跑回去,所以我們可以通過監(jiān)聽父級節(jié)點來實現(xiàn)監(jiān)聽子節(jié)點的功能,這就是事件代理。

使用事件代理主要有兩個優(yōu)勢:

  1. 減少事件綁定,提升性能。之前你需要綁定一堆子節(jié)點,而現(xiàn)在你只需要綁定一個父節(jié)點即可。減少了綁定事件監(jiān)聽函數(shù)的數(shù)量。
  2. 動態(tài)變化的 DOM 結(jié)構(gòu),仍然可以監(jiān)聽。當(dāng)一個 DOM 動態(tài)創(chuàng)建之后,不會帶有任何事件監(jiān)聽,除非你重新執(zhí)行事件監(jiān)聽函數(shù),而使用事件監(jiān)聽無須擔(dān)憂這個問題。

看一個例子:

上面例子中,為了簡便,我使用 jQuery 來實現(xiàn)普通事件綁定和事件代理。我的目標(biāo)是監(jiān)聽所有 a 鏈接的單擊事件,.ul1 是常規(guī)的事件綁定方法,jQuery 會循環(huán)每一個 .ul > a 結(jié)構(gòu)并綁定事件監(jiān)聽函數(shù)。.ul2 則是事件監(jiān)聽的方法,jQuery 只為 .ul2 結(jié)構(gòu)綁定事件監(jiān)聽函數(shù),因為 .ul2 下面可能會有很多無關(guān)節(jié)點也會觸發(fā)click 事件,所以我在 on 函數(shù)里傳遞了第二個參數(shù),表示只監(jiān)聽 a 子節(jié)點的事件。

它們都可以正常工作,但是當(dāng)我動態(tài)創(chuàng)建新 DOM 結(jié)構(gòu)的時候,第一個 ul 問題就出現(xiàn)了,新創(chuàng)建結(jié)構(gòu)雖然還是.ul1 > a,但是沒有綁定事件,所以無法執(zhí)行回調(diào)函數(shù)。而第二個 ul 工作的很好,因為點擊新創(chuàng)建的 DOM ,它的事件會冒泡到父級節(jié)點進(jìn)行處理。

如果使用原生的方式實現(xiàn)事件代理,需要注意過濾非目標(biāo)節(jié)點,可以通過 id、class 或者 tagname 等等,例如:

element.addEventListener('click', function(event) {
    // 判斷是否是 a 節(jié)點
    if ( event.target.tagName == 'A' ) {
        // a 的一些交互操作
    }
}, false);

停止事件冒泡(stopPropagation)

所有的事情都會有對立面,事件的冒泡階段雖然看起來很好,也會有不適合的場所。比較復(fù)雜的應(yīng)用,由于事件監(jiān)聽比較復(fù)雜,可能會希望只監(jiān)聽發(fā)生在具體節(jié)點的事件。這個時候就需要停止事件冒泡。

停止事件冒泡需要使用事件對象的 stopPropagation 方法,具體代碼如下:

element.addEventListener('click', function(event) {
    event.stopPropagation();
}, false);

在事件監(jiān)聽的回調(diào)函數(shù)里,會傳遞一個參數(shù),這就是 Event 對象,在這個對象上調(diào)用 stopPropagation 方法即可停止事件冒泡。舉個停止事件冒泡的應(yīng)用實例:

JS Bin

在上面例子中,有一個彈出層,我們可以在彈出層上做任何操作,例如 click 等。當(dāng)我們想關(guān)掉這個彈出層,在彈出層外面的任意結(jié)構(gòu)中點擊即可關(guān)掉。它首先對 document 節(jié)點進(jìn)行 click 事件監(jiān)聽,所有的 click 事件,都會讓彈出層隱藏掉。同樣的,我們在彈出層上面的單擊操作也會導(dǎo)致彈出層隱藏。之后我們對彈出層使用停止事件冒泡,掐斷了單擊事件返回 document 的冒泡路線,這樣在彈出層的操作就不會被 document 的事件處理函數(shù)監(jiān)聽到。

更多關(guān)于 Event 對象的事情,我們會在下面介紹。

事件的 Event 對象

當(dāng)一個事件被觸發(fā)的時候,會創(chuàng)建一個事件對象(Event Object),這個對象里面包含了一些有用的屬性或者方法。事件對象會作為第一個參數(shù),傳遞給我們的毀掉函數(shù)。我們可以使用下面代碼,在瀏覽器中打印出這個事件對象:

<button>打印 Event Object</button>

<script>
    var btn = document.getElementsByTagName('button');
    btn[0].addEventListener('click', function(event) {
        console.log(event);
    }, false);
</script>

就可以看到一堆屬性列表:

事件屬性列表

事件對象包括很多有用的信息,比如事件觸發(fā)時,鼠標(biāo)在屏幕上的坐標(biāo)、被觸發(fā)的 DOM 詳細(xì)信息、以及上圖最下面繼承過來的停止冒泡方法(stopPropagation)。下面介紹一下比較常用的幾個屬性和方法:

type(string)

事件的名稱,比如 “click”。

target(node)

事件要觸發(fā)的目標(biāo)節(jié)點。

bubbles (boolean)

表明該事件是否是在冒泡階段觸發(fā)的。

preventDefault (function)

這個方法可以禁止一切默認(rèn)的行為,例如點擊 a 標(biāo)簽時,會打開一個新頁面,如果為 a 標(biāo)簽監(jiān)聽事件 click同時調(diào)用該方法,則不會打開新頁面。

stopPropagation (function)

停止冒泡,上面有提到,不再贅述。

stopImmediatePropagation (function)

與 stopPropagation 類似,就是阻止觸發(fā)其他監(jiān)聽函數(shù)。但是與 stopPropagation 不同的是,它更加 “強(qiáng)力”,阻止除了目標(biāo)之外的事件觸發(fā),甚至阻止針對同一個目標(biāo)節(jié)點的相同事件,Demo:http:///yujiangshui/ju2ujmzp/2/。

cancelable (boolean)

這個屬性表明該事件是否可以通過調(diào)用 event.preventDefault 方法來禁用默認(rèn)行為。

eventPhase (number)

這個屬性的數(shù)字表示當(dāng)前事件觸發(fā)在什么階段。none:0;捕獲:1;目標(biāo):2;冒泡:3。

pageX 和 pageY (number)

這兩個屬性表示觸發(fā)事件時,鼠標(biāo)相對于頁面的坐標(biāo)。Demo:http://api./event.pagex/。

isTrusted (boolean)

表明該事件是瀏覽器觸發(fā)(用戶真實操作觸發(fā)),還是 JavaScript 代碼觸發(fā)的。

jQuery 中的事件

如果你在寫文章或者 Demo,為了簡單,你當(dāng)然可以用上面的事件監(jiān)聽函數(shù),以及那些事件對象提供的方法等。但在實際中,有一些方法和屬性是有兼容性問題的,所以我們會使用 jQuery 來消除兼容性問題。

下面簡單的來說一下 jQuery 中事件的基礎(chǔ)操作。

綁定事件和事件代理

在 jQuery 中,提供了諸如 click() 這樣的語法糖來綁定對應(yīng)事件,但是這里推薦統(tǒng)一使用 on() 來綁定事件。語法:

.on( events [, selector ] [, data ], handler )

events 即為事件的名稱,你可以傳遞第二個參數(shù)來實現(xiàn)事件代理,具體文檔.on() 這里不再贅述。

處理過兼容性的事件對象(Event Object)

事件對象有些方法等也有兼容性差異,jQuery 將其封裝處理,并提供跟標(biāo)準(zhǔn)一直的命名。

如果你想在 jQuery 事件回調(diào)函數(shù)中訪問原來的事件對象,需要使用 event.originalEvent,它指向原生的事件對象。

觸發(fā)事件 trigger 方法

點擊某個綁定了 click 事件的節(jié)點,自然會觸發(fā)該節(jié)點的 click 事件,從而執(zhí)行對應(yīng)回調(diào)函數(shù)。

trigger 方法可以模擬觸發(fā)事件,我們單擊另一個節(jié)點 elementB,可以使用:

$(elementB).on('click', function(){
    $(elementA).trigger( "click" );
});

來觸發(fā) elementA 節(jié)點的單擊監(jiān)聽回調(diào)函數(shù)。詳情請看文檔 .trigger()。

事件進(jìn)階話題

IE 瀏覽器的差異和兼容性問題

IE 瀏覽器就是特立獨(dú)行,它對于事件的操作與標(biāo)準(zhǔn)有一些差異。不過 IE 瀏覽器現(xiàn)在也開始慢慢努力改造,讓瀏覽器變得更加標(biāo)準(zhǔn)。

IE 下綁定事件

在 IE 下面綁定一個事件監(jiān)聽,在 IE9- 無法使用標(biāo)準(zhǔn)的 addEventListener 函數(shù),而是使用自家的attachEvent,具體用法:

element.attachEvent(<event-name>, <callback>);

其中 <event-name> 參數(shù)需要注意,它需要為事件名稱添加 on 前綴,比如有個事件叫 click,標(biāo)準(zhǔn)事件監(jiān)聽函數(shù)監(jiān)聽 click,IE 這里需要監(jiān)聽 onclick

另一個,它沒有第三個參數(shù),也就是說它只支持監(jiān)聽在冒泡階段觸發(fā)的事件,所以為了統(tǒng)一,在使用標(biāo)準(zhǔn)事件監(jiān)聽函數(shù)的時候,第三參數(shù)傳遞 false。

當(dāng)然,這個方法在 IE9 已經(jīng)被拋棄,在 IE11 已經(jīng)被移除了,IE 也在慢慢變好。

IE 中 Event 對象需要注意的地方

IE 中往回調(diào)函數(shù)中傳遞的事件對象與標(biāo)準(zhǔn)也有一些差異,你需要使用 window.event 來獲取事件對象。所以你通常會寫出下面代碼來獲取事件對象:

event = event || window.event

此外還有一些事件屬性有差別,比如比較常用的 event.target 屬性,IE 中沒有,而是使用 event.srcElement來代替。如果你的回調(diào)函數(shù)需要處理觸發(fā)事件的節(jié)點,那么需要寫:

node = event.srcElement || event.target;

常見的就是這點,更細(xì)節(jié)的不再多說。在概念學(xué)習(xí)中,我們沒必要為不標(biāo)準(zhǔn)的東西支付學(xué)習(xí)成本;在實際應(yīng)用中,類庫已經(jīng)幫我們封裝好這些兼容性問題。可喜的是 IE 瀏覽器現(xiàn)在也開始不斷向標(biāo)準(zhǔn)進(jìn)步。

事件回調(diào)函數(shù)的作用域問題

與事件綁定在一起的回調(diào)函數(shù)作用域會有問題,我們來看個例子:

Events in JavaScript: Removing event listeners

回調(diào)函數(shù)調(diào)用的 user.greeting 函數(shù)作用域應(yīng)該是在 user 下的,本期望輸出 My name is Bob 結(jié)果卻輸出了My name is undefined。這是因為事件綁定函數(shù)時,該函數(shù)會以當(dāng)前元素為作用域執(zhí)行。為了證明這一點,我們可以為當(dāng)前 element 添加屬性:

element.firstname = 'jiangshui';

再次點擊,可以正確彈出 My name is jiangshui。那么我們來解決一下這個問題。

使用匿名函數(shù)

我們?yōu)榛卣{(diào)函數(shù)包裹一層匿名函數(shù)。

Events in JavaScript: Removing event listeners

包裹之后,雖然匿名函數(shù)的作用域被指向事件觸發(fā)元素,但執(zhí)行的內(nèi)容就像直接調(diào)用一樣,不會影響其作用域。

使用 bind 方法

使用匿名函數(shù)是有缺陷的,每次調(diào)用都包裹進(jìn)匿名函數(shù)里面,增加了冗余代碼等,此外如果想使用removeEventListener 解除綁定,還需要再創(chuàng)建一個函數(shù)引用。Function 類型提供了 bind 方法,可以為函數(shù)綁定作用域,無論函數(shù)在哪里調(diào)用,都不會改變它的作用域。通過如下語句綁定作用域:

user.greeting = user.greeting.bind(user);

這樣我們就可以直接使用:

element.addEventListener('click', user.greeting);

常用事件和技巧

用戶的操作有很多種,所以有很多事件。為了開發(fā)方便,瀏覽器又提供了一些事件,所以有很多很多的事件。這里只介紹幾種常用的事件和使用技巧。

load

load 事件在資源加載完成時觸發(fā)。這個資源可以是圖片、CSS 文件、JS 文件、視頻、document 和 window 等等。

比較常用的就是監(jiān)聽 window 的 load 事件,當(dāng)頁面內(nèi)所有資源全部加載完成之后就會觸發(fā)。比如用 JS 對圖片以及其他資源處理,我們在 load 事件中觸發(fā),可以保證 JS 不會在資源未加載完成就開始處理資源導(dǎo)致報錯。

同樣的,也可以監(jiān)聽圖片等其他資源加載情況。

beforeunload

當(dāng)瀏覽者在頁面上的輸入框輸入一些內(nèi)容時,未保存、誤操作關(guān)掉網(wǎng)頁可能會導(dǎo)致輸入信息丟失。

當(dāng)瀏覽者輸入信息但未保存時關(guān)掉網(wǎng)頁,我們就可以開始監(jiān)聽這個事件,例如:

window.addEventListener("beforeunload", function( event ) {
    event.returnValue = "放棄當(dāng)前未保存內(nèi)容而關(guān)閉頁面?";
});

這時候試圖關(guān)閉網(wǎng)頁的時候,會彈窗阻止操作,點擊確認(rèn)之后才會關(guān)閉。當(dāng)然,如果沒有必要,就不要監(jiān)聽,不要以為使用它可以為你留住瀏覽者。

resize

當(dāng)節(jié)點尺寸發(fā)生變化時,觸發(fā)這個事件。通常用在 window 上,這樣可以監(jiān)聽瀏覽器窗口的變化。通常用在復(fù)雜布局和響應(yīng)式上。

常見的視差滾動效果網(wǎng)站以及同類比較復(fù)雜的布局網(wǎng)站,往往使用 JavaScript 來計算尺寸、位置。如果用戶調(diào)整瀏覽器大小,尺寸、位置不隨著改變則會出現(xiàn)錯位情況。在 window 上監(jiān)聽該事件,觸發(fā)時調(diào)用計算尺寸、位置的函數(shù),可以根據(jù)瀏覽器的大小來重新計算。

但需要注意一點,當(dāng)瀏覽器發(fā)生任意變化都會觸發(fā) resize 事件,哪怕是縮小 1px 的瀏覽器寬度,這樣調(diào)整瀏覽器時會觸發(fā)大量的 resize 事件,你的回調(diào)函數(shù)就會被大量的執(zhí)行,導(dǎo)致變卡、崩潰等。

你可以使用函數(shù) throttle 或者 debounce 技巧來進(jìn)行優(yōu)化,throttle 方法大體思路就是在某一段時間內(nèi)無論多次調(diào)用,只執(zhí)行一次函數(shù),到達(dá)時間就執(zhí)行;debounce 方法大體思路就是在某一段時間內(nèi)等待是否還會重復(fù)調(diào)用,如果不會再調(diào)用,就執(zhí)行函數(shù),如果還有重復(fù)調(diào)用,則不執(zhí)行繼續(xù)等待。關(guān)于它們更詳細(xì)的信息,我后面會介紹一下發(fā)表在我的博客上,這里不再贅述。

error

當(dāng)我們加載資源失敗或者加載成功但是只加載一部分而無法使用時,就會觸發(fā) error 事件,我們可以通過監(jiān)聽該事件來提示一個友好的報錯或者進(jìn)行其他處理。比如 JS 資源加載失敗,則提示嘗試刷新;圖片資源加載失敗,在圖片下面提示圖片加載失敗等。該事件不會冒泡。因為子節(jié)點加載失敗,并不意味著父節(jié)點加載失敗,所以你的處理函數(shù)必須精確綁定到目標(biāo)節(jié)點。

需要注意的是,對于該事件,你可以使用 addEventListener 等進(jìn)行監(jiān)聽,但是有時候會出現(xiàn)失效情況(看這個例子),這是因為 error 事件都觸發(fā)過了,你的 JS 監(jiān)聽處理代碼還沒有加載進(jìn)來執(zhí)行。為了避免這種情況,用內(nèi)聯(lián)法更好一些:

<img src="not-found.jpg" onerror="doSomething" />

如果還有其他常用事件,歡迎留言補(bǔ)充。

用 JavaScript 模擬觸發(fā)內(nèi)置事件

內(nèi)置的事件也可以被 JavaScript 模擬觸發(fā),比如下面函數(shù)模擬觸發(fā)單擊事件:

function simulateClick() {
  var event = new MouseEvent('click', {
    'view': window,
    'bubbles': true,
    'cancelable': true
  });
  var cb = document.getElementById('checkbox'); 
  var canceled = !cb.dispatchEvent(event);
  if (canceled) {
    // A handler called preventDefault.
    alert("canceled");
  } else {
    // None of the handlers called preventDefault.
    alert("not canceled");
  }
}

可以看這個 Demo 來了解更多。

自定義事件

我們可以自定義事件來實現(xiàn)更靈活的開發(fā),事件用好了可以是一件很強(qiáng)大的工具,基于事件的開發(fā)有很多優(yōu)勢(后面介紹)。

與自定義事件的函數(shù)有 Event、CustomEvent 和 dispatchEvent。

直接自定義事件,使用 Event 構(gòu)造函數(shù):

var event = new Event('build');

// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);

// Dispatch the event.
elem.dispatchEvent(event);

CustomEvent 可以創(chuàng)建一個更高度自定義事件,還可以附帶一些數(shù)據(jù),具體用法如下:

var myEvent = new CustomEvent(eventname, options);

其中 options 可以是:

{
    detail: {
        ...
    },
    bubbles: true,
    cancelable: false
}

其中 detail 可以存放一些初始化的信息,可以在觸發(fā)的時候調(diào)用。其他屬性就是定義該事件是否具有冒泡等等功能。

內(nèi)置的事件會由瀏覽器根據(jù)某些操作進(jìn)行觸發(fā),自定義的事件就需要人工觸發(fā)。dispatchEvent 函數(shù)就是用來觸發(fā)某個事件:

element.dispatchEvent(customEvent);

上面代碼表示,在 element 上面觸發(fā) customEvent 這個事件。結(jié)合起來用就是:

// add an appropriate event listener
obj.addEventListener("cat", function(e) { process(e.detail) });

// create and dispatch the event
var event = new CustomEvent("cat", {"detail":{"hazcheeseburger":true}});
obj.dispatchEvent(event);

使用自定義事件需要注意兼容性問題,而使用 jQuery 就簡單多了:

// 綁定自定義事件
$(element).on('myCustomEvent', function(){});

// 觸發(fā)事件
$(element).trigger('myCustomEvent');

此外,你還可以在觸發(fā)自定義事件時傳遞更多參數(shù)信息:

$( "p" ).on( "myCustomEvent", function( event, myName ) {
  $( this ).text( myName + ", hi there!" );
});
$( "button" ).click(function () {
  $( "p" ).trigger( "myCustomEvent", [ "John" ] );
});

更詳細(xì)的用法請看 Introducing Custom Events,這里不再贅述。

在開發(fā)中應(yīng)用事件

當(dāng)我們操作某一個 DOM,發(fā)出一個事件,我們可以在另一個地方寫代碼捕獲這個事件執(zhí)行處理邏輯。觸發(fā)操作和捕獲處理操作是分開的。我們可以根據(jù)這個特性來對程序解耦。

用事件解耦

我們可以將一個整個的功能,分割成獨(dú)立的小功能,每個小功能綁定一個事件,由一個“控制器”負(fù)責(zé)根據(jù)條件觸發(fā)某個事件。這樣,在外面觸發(fā)這個事件,也可以調(diào)用對應(yīng)功能,使其更加靈活。

應(yīng)用事件對程序解耦

在《基于 MVC 的 JavaScript Web 富應(yīng)用開發(fā)》一書中,有更加具體的實例,有興趣的朋友可以買本看看。

發(fā)布(Publish)和訂閱(Subscribe)模式

針對上面這種用法,繼續(xù)抽象一下,就是發(fā)布和訂閱開發(fā)模式。正如其名,這種模式有兩個角色:發(fā)布者和訂閱者,此外有一條信道,發(fā)布者被觸發(fā)往這個信道里面發(fā)信,訂閱者從這個信道里面收信,如果收到特定信件則執(zhí)行某個對應(yīng)的邏輯。這樣,發(fā)布者和訂閱者之間是完全解耦的,只有一條信道連接。這樣就非常容易擴(kuò)展,也不會引入額外的依賴。

這樣如果需要添加新功能,只需要添加一個新的訂閱者(及其執(zhí)行邏輯),監(jiān)聽信道中某一類新的信件。再在應(yīng)用中通過發(fā)布者發(fā)送一類新的信件即可。

具體實現(xiàn),這里推薦 cowboy 開發(fā)的 Tiny Pub Sub,通過 jQuery 實現(xiàn),非常簡潔直觀,jQuery 太贊。代碼就這幾行:

(function($) {

  var o = $({});

  $.subscribe = function() {
    o.on.apply(o, arguments);
  };

  $.unsubscribe = function() {
    o.off.apply(o, arguments);
  };

  $.publish = function() {
    o.trigger.apply(o, arguments);
  };

}(jQuery));

定義一個對象作為信道,然后提供了三個方法,訂閱者、取消訂閱、發(fā)布者。

總結(jié)和擴(kuò)展閱讀

事件有關(guān)的基礎(chǔ)知識基本就這些,更多的還有待你繼續(xù)挖掘。本文資料參考和推薦擴(kuò)展閱讀如下(感謝他們):

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    丁香六月啪啪激情综合区| 欧美人妻少妇精品久久性色| 亚洲视频一区自拍偷拍另类| 大香蕉精品视频一区二区| 99久热只有精品视频免费看| 国产精品超碰在线观看| 亚洲品质一区二区三区| 亚洲欧洲一区二区中文字幕| 五月婷婷六月丁香在线观看| 麻豆欧美精品国产综合久久| 国产亚州欧美一区二区| 深夜视频在线观看免费你懂| 欧美日韩成人在线一区| 成人你懂的在线免费视频| 亚洲最新中文字幕在线视频| 亚洲av日韩av高潮无打码| 亚洲中文字幕视频在线播放| 国产成人国产精品国产三级| 国产精品一级香蕉一区| 亚洲av熟女国产一区二区三区站 | 爱在午夜降临前在线观看| 日本一本不卡免费视频| 国产精品欧美一区二区三区| 亚洲国产成人久久99精品 | 少妇视频一区二区三区| 欧美韩日在线观看一区| 91精品国产综合久久不卡| 国产乱人伦精品一区二区三区四区 | 国产欧美精品对白性色| 亚洲一区二区三区一区| 熟女高潮一区二区三区| 夜夜躁狠狠躁日日躁视频黑人| 在线观看国产午夜福利| 免费一级欧美大片免费看| 色涩一区二区三区四区| 亚洲天堂男人在线观看| 久久精品国产亚洲av麻豆尤物| 国产精品欧美激情在线观看| 91亚洲精品亚洲国产| 福利在线午夜绝顶三级| 国产激情国产精品久久源|