Javascript事件模型系列(一)事件及事件的三種模型

1、開篇

         在學習javascript之初,就在網上看過很多介紹javascript事件的文章,畢竟是js基礎中的基礎,文章零零散散有很多,但遺憾的是沒有看到比較全面的系列文章。猶記得去年這個時候,參加百度的實習生面試,被問到事件模型,當時被問的一頭霧水,平時敲onclick敲的挺爽,卻沒有關注到事件模型的總體概念。這個週末可貴悠閒,決定就javascript中的事件模型寫個系列,算是對知識點的一個總結,也是對本身的一個交代。javascript

         初步計劃分爲如下幾個部分:前端

    ①   javascript事件的基本概念及基於原始、IE、DOM2的三種模型的異同點java

    ②   javascript事件流介紹,捕獲-冒泡機制及事件委託機制jquery

    ③   jquery中的事件監聽方式(bind、live、attachEvent、on)及異同點web

    ④   javascript自定義事件面試

2、event簡介

         什麼是事件呢?直觀的說就是網頁上發生的事情,大部分是指用戶的鼠標動做和鍵盤動做,如點擊、移動鼠標、按下某個鍵。爲何說大部分呢,由於事件不僅僅只有這兩部分,還有其餘的例如document的load和unloaded。只不過咱們更加關注的是用戶的操做。事件被封裝成一個event對象,包含了該事件發生時的全部相關信息(event的屬性)以及能夠對事件進行的操做(event的方法)。chrome

         event長啥樣呢,來直觀的看一下,好比我點擊頁面上一個按鈕,產生的event對象以下:編程

 

         能夠看到是一個MouseEvent對象,包含了一系列屬性,如鼠標點擊的位置等。那麼敲擊鍵盤時產生的event對象和它同樣嗎?看看就知道:瀏覽器

    能夠看到是一個KeyboardEvent對象,而且屬性跟上面的也不太同樣,如沒有clientX/Y,那是理所固然的啦,敲鍵盤怎麼能獲取到鼠標的位置呢。框架

           若你有一點面向對象編程的基礎,看到這兩個類名應該會有所思考,MouseEvent、KeyboardEvent會不會是繼承自一個叫Event的類呢?恭喜你猜對了,確實如此。來看一下,我在window.onload監聽函數中打印出event對象以下:

    屬性少了不少,畢竟是父類嘛。若你想了解更多關於事件類型的內容,能夠參考這裏,本文就不作更深的介紹。

3、event對象經常使用屬性、方法

1. 事件定位相關屬性

    這部分屬性平時用的仍是挺多的,因此得着重介紹。若是你細細看了MouseEvent對象裏的屬性,必定發現了有不少帶X/Y的屬性,它們都和事件的位置相關。具體包括:x/y、clientX/clientY、pageX/pageY、screenX/screenY、layerX/layerY、offsetX/offset 六對。有點亂了吧,一個點擊事件能有多少位置啊?不要着急,其實並不複雜,之因此能有這麼可能是由於各瀏覽器廠商在版本更迭的時候產生了不少不一致。看下面的例子就明白各自的含義了:

在這裏移動鼠標
x:    y:
clientX:    clientY:
screenX:    screenY:
offsetX:    offsetY:
pageX:    pageY:
layerX:    layerY:

    得出的結論以下:

    x/y與clientX/clientY值同樣,表示距瀏覽器可視區域(工具欄除外區域)左/上的距離;

    pageX/pageY,距頁面左/上的距離,它與clientX/clientY的區別是不隨滾動條的位置變化;

    screenX/screenY,距計算機顯示器左/上的距離,拖動你的瀏覽器窗口位置能夠看到變化;

    layerX/layerY與offsetX/offsetY值同樣,表示距有定位屬性的父元素左/上的距離。

    之因此有那麼多值同樣的狀況,就是因爲瀏覽器兼容的緣由。那咱們平時該如何使用呢?請看下面的表格,列出了各屬性的瀏覽器支持狀況。(+支持,-不支持)

    offsetX/offsetY:W3C- IE+ Firefox- Opera+ Safari+ chrome+

    x/y:W3C- IE+ Firefox- Opera+ Safari+ chrome+

    layerX/layerY:W3C- IE- Firefox+ Opera- Safari+ chrome+

    pageX/pageY:W3C- IE- Firefox+ Opera+ Safari+ chrome+

    clientX/clientY:W3C+ IE+ Firefox+ Opera+ Safari+ chrome+

    screenX/screenY:W3C+ IE+ Firefox+ Opera+ Safari+ chrome+​

        說明:該表摘自其餘文章,我未作所有驗證,但從最新版本的現代瀏覽器來看,這些屬性貌似是都支持了,爲了更好的兼容性,一般咱們選擇W3C支持的就能夠了。若你想看更加細緻的相關描述,請點擊這裏

2.其餘經常使用屬性

    target:發生事件的節點;

    currentTarget:當前正在處理的事件的節點,在事件捕獲或冒泡階段;

      timeStamp:事件發生的時間,時間戳。

    bubbles:事件是否冒泡。

    cancelable:事件是否能夠用preventDefault()方法來取消默認的動做;

    keyCode:按下的鍵的值;

3. event對象的方法

    event. preventDefault()//阻止元素默認的行爲,如連接的跳轉、表單的提交;

    event. stopPropagation()//阻止事件冒泡

    event.initEvent()//初始化新事件對象的屬性,自定義事件會用,不經常使用

    event. stopImmediatePropagation()//能夠阻止掉同一事件的其餘優先級較低的偵聽器的處理,(我沒有用過)

4、事件的三種模型

           因爲複雜的歷史緣由,事件模型是不統一的,固然做爲前端開發人員這種事情已經見怪不怪了。儘管W3C已經制定了DOM2標準來規範事件的定義,但因爲頑固的IE六、七、8存在,咱們仍是得清楚IE的那一套定義。那麼來看看三種模型都有哪些吧。

1.     原始事件模型

    在原始事件模型中(也有說DOM0級),事件發生後沒有傳播的概念,沒有事件流。事件發生,立刻處理,完事,就這麼簡單。監聽函數只是元素的一個屬性值,經過指定元素的屬性值來綁定監聽器。書寫方式有兩種:

    ①   HTML代碼中指定屬性值:<input type=」button」 onclick=」func1()」 />

    ②   在js代碼中指定屬性值:document.getElementsByTagName(‘input’)[0].onclick = func1

    優勢:全部瀏覽器都兼容

    缺點:1)邏輯與顯示沒有分離;2)相同事件的監聽函數只能綁定一個,後綁定的會覆蓋掉前面的,如:a.onclick = func1; a.onclick = func2;將只會執行func2中的內容。3)沒法經過事件的冒泡、委託等機制(後面系列會講到)完成更多事情。

    在當前web程序模塊化開發以及更加複雜的邏輯情況下,這種方式顯然已經落伍了,因此在真正項目中不推薦使用,平時寫點博客小例子啥的卻是能夠,速度比較快。

2.     IE事件模型

    在參考其餘資料時,我有看到這樣的一句話「IE不把該對象傳入事件處理函數,因爲在任意時刻只會存在一個事件,因此IE把它做爲全局對象window的一個屬性」,爲求證其真僞,我用IE8執行了代碼alert(window.event),結果彈出是null,說明該屬性已經定義,只是值爲null(與undefined不一樣)。我想難道這個全局對象的屬性是在監聽函數裏才加的?因而執行下面代碼:

    window.onload = function (){alert(window.event);}

    setTimeout(function(){alert(window.event);},2000);

    結果第一次彈出【object event】,兩秒後彈出依然是null。因而可知IE是將event對象在處理函數中設爲window的屬性,一旦函數執行結束,便被置爲null了。IE的事件模型只有兩步,先執行元素的監聽函數,而後事件沿着父節點一直冒泡到document。冒泡機制後面系列會講,此處暫記。IE模型下的事件監聽方式也挺獨特,綁定監聽函數的方法是:attachEvent( "eventType","handler"),其中evetType爲事件的類型,如onclick,注意要加’on’。解除事件監聽器的方法是 detachEvent("eventType","handler" )

    IE的事件模型已經能夠解決原始模型的三個缺點,但其本身的缺點就是兼容性,只有IE系列瀏覽器才能夠這樣寫。

3.     DOM2事件模型

    此模型是W3C制定的標準模型,既然是標準,那你們都得按這個來,咱們如今使用的現代瀏覽器(指IE6~8除外的瀏覽器)都已經遵循這個規範。W3C制定的事件模型中,一次事件的發生包含三個過程:

    (1)capturing phase:事件捕獲階段。事件被從document一直向下傳播到目標元素,在這過程當中依次檢查通過的節點是否註冊了該事件的監聽函數,如有則執行。

    (2)target phase:事件處理階段。事件到達目標元素,執行目標元素的事件處理函數.

    (3)bubbling phase:事件冒泡階段。事件從目標元素上升一直到達document,一樣依次檢查通過的節點是否註冊了該事件的監聽函數,有則執行。

    全部的事件類型都會經歷captruing phase可是隻有部分事件會經歷bubbling phase階段,例如submit事件就不會被冒泡。 

    你可能會有疑問,爲何是這個樣子的呢?流程有點太多了吧?事情的原因還得從網景公司與微軟爭霸開始提及。在W3C的規範尚未出生的時候,市場上已經有兩家強勁的瀏覽器廠商,產品分別是微軟的IE和網景的Netspace Navigator(後面簡稱NN),IE的事件模型上面已介紹,事件是能夠冒泡的。然而NN卻不這麼認爲,它的模型中,事件是從上往下走的,即只有捕獲階段。兩家都沒有誰對誰錯,由於按照他們的模型均可以完成事件的處理。而後W3C珊珊來遲,要制定標準,要統一,因此也就只能兩家的都採納,誰也不得罪,而後用標準制定者的口吻宣佈:W3C模型工做良好。今後天下太平。

    說遠了,趕忙來看看標準的事件監聽器該如何綁定:addEventListener("eventType","handler","true|false");其中eventType指事件類型,注意不要加‘on’前綴,與IE下不一樣。第二個參數是處理函數,第三個即用來指定是否在捕獲階段進行處理,通常設爲false來與IE保持一致,除非你有特殊的邏輯需求。監聽器的解除也相似:removeEventListner("eventType","handler","true!false");

        以上即是事件的三種模型,咱們在開發的時候須要兼顧IE與非IE瀏覽器,因此註冊一個監聽器應該這樣寫:

var a = document.getElementById('a');
if(a.attachEvent){
    a.attachEvent('onclick',func);
}
else{
    a.addEventListener('click',func,false);
}

        感受很麻煩吧?所以咱們通常會藉助現有框架或類庫已經封裝好的,好比jQuery,後面將會介紹jQuery中強大的事件監聽方式。

    系列一到此結束,做者本人技術水平有限,文章內容都是本身的理解寫出來的,歡迎各路高手指點糾錯。

相關文章
相關標籤/搜索