久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

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

 看見(jiàn)就非常 2015-03-20

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

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

下面就來(lái)介紹一下事件。

基礎(chǔ)事件操作

監(jiān)聽(tīng)事件

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

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

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

例如:

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

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

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

DOM 屬性綁定

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

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

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

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

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

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

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

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

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

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

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

Demo:

移除事件監(jiān)聽(tīng)

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

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

需要注意的是,,綁定事件時(shí)的回調(diào)函數(shù)不能是匿名函數(shù),,必須是一個(gè)聲明的函數(shù),因?yàn)榻獬录壎〞r(shí)需要傳遞這個(gè)回調(diào)函數(shù)的引用,,才可以斷開(kāi)綁定,。例如:

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

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

Demo:

事件觸發(fā)過(guò)程

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

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

捕獲階段(Capture Phase)

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

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

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

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

但一般使用時(shí)我們往往傳遞 false,,會(huì)在后面說(shuō)明原因。

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

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

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

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

冒泡階段(Bubbling Phase)

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

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

針對(duì)這三個(gè)階段,,wilsonpage 做了一個(gè)非常棒的 Demo,,可以看下:

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

介紹完上面三個(gè)事件觸發(fā)階段,我們來(lái)看下這個(gè)問(wèn)題,。

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

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

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

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

因?yàn)槭录忻芭輽C(jī)制,所有子節(jié)點(diǎn)的事件都會(huì)順著父級(jí)節(jié)點(diǎn)跑回去,,所以我們可以通過(guò)監(jiān)聽(tīng)父級(jí)節(jié)點(diǎn)來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)子節(jié)點(diǎn)的功能,,這就是事件代理。

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

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

看一個(gè)例子:

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

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

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

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

停止事件冒泡(stopPropagation)

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

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

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

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

JS Bin

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

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

事件的 Event 對(duì)象

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

<button>打印 Event Object</button>

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

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

事件屬性列表

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

type(string)

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

target(node)

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

bubbles (boolean)

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

preventDefault (function)

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

stopPropagation (function)

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

stopImmediatePropagation (function)

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

cancelable (boolean)

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

eventPhase (number)

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

pageX 和 pageY (number)

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

isTrusted (boolean)

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

jQuery 中的事件

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

下面簡(jiǎn)單的來(lái)說(shuō)一下 jQuery 中事件的基礎(chǔ)操作,。

綁定事件和事件代理

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

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

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

處理過(guò)兼容性的事件對(duì)象(Event Object)

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

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

觸發(fā)事件 trigger 方法

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

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

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

來(lái)觸發(fā) elementA 節(jié)點(diǎn)的單擊監(jiān)聽(tīng)回調(diào)函數(shù),。詳情請(qǐng)看文檔 .trigger(),。

事件進(jìn)階話題

IE 瀏覽器的差異和兼容性問(wèn)題

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

IE 下綁定事件

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

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

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

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

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

IE 中 Event 對(duì)象需要注意的地方

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

event = event || window.event

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

node = event.srcElement || event.target;

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

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

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

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。這是因?yàn)槭录壎ê瘮?shù)時(shí),,該函數(shù)會(huì)以當(dāng)前元素為作用域執(zhí)行,。為了證明這一點(diǎn),我們可以為當(dāng)前 element 添加屬性:

element.firstname = 'jiangshui';

再次點(diǎn)擊,,可以正確彈出 My name is jiangshui,。那么我們來(lái)解決一下這個(gè)問(wèn)題。

使用匿名函數(shù)

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

Events in JavaScript: Removing event listeners

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

使用 bind 方法

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

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

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

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

常用事件和技巧

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

load

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

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

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

beforeunload

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

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

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

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

resize

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

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

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

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

error

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

需要注意的是,,對(duì)于該事件,,你可以使用 addEventListener 等進(jìn)行監(jiān)聽(tīng),但是有時(shí)候會(huì)出現(xiàn)失效情況(看這個(gè)例子),,這是因?yàn)?nbsp;error 事件都觸發(fā)過(guò)了,,你的 JS 監(jiān)聽(tīng)處理代碼還沒(méi)有加載進(jìn)來(lái)執(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");
  }
}

可以看這個(gè) Demo 來(lái)了解更多,。

自定義事件

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

與自定義事件的函數(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)建一個(gè)更高度自定義事件,,還可以附帶一些數(shù)據(jù),具體用法如下:

var myEvent = new CustomEvent(eventname, options);

其中 options 可以是:

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

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

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

element.dispatchEvent(customEvent);

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

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

使用自定義事件需要注意兼容性問(wèn)題,,而使用 jQuery 就簡(jiǎn)單多了:

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

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

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

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

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

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

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

用事件解耦

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

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

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

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

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

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

具體實(shí)現(xiàn),這里推薦 cowboy 開(kāi)發(fā)的 Tiny Pub Sub,,通過(guò) jQuery 實(shí)現(xiàn),,非常簡(jiǎ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));

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

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

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

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

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多