[學習筆記] 事件(上篇)

今天看書又看到事件,遂決定小總結一下~html

JavaScript與HTML之間的交互是經過事件實現的。事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。可使用監聽器(事件處理程序)來監聽事件,以便事件發生時執行相應的代碼。前端

本文主要講事件流事件處理程序。後續會再寫一篇文章,總結事件類型瀏覽器


事件流

你們都舉同心圓的栗子,我在這裏也說說咯~想象畫在一張紙上的一組同心圓,若是把手指放在圓心上,那麼手指碰觸的不是一個圓,而是紙上全部的圓。這和點擊HTML頁面上的元素原理是同樣的,若是頁面上有個按鈕,你點擊按鈕的同時,也點擊了按鈕的容器元素,點擊了容器元素的容器元素……點擊了整個頁面。函數

事件流描述的是從頁面中接收事件的順序。現存兩種事件流:this

  • 事件冒泡流——IE
  • 事件捕獲流——Netscape

事件冒泡

接收事件的順序是:事件由最具體的元素接收,逐級向上傳播到較爲不具體的節點。spa

若是頁面中有個div,點擊了div以後,事件的傳播順序以下:
(1) div
(2) body
(3) html
(4) document設計

事件冒泡的過程以下圖:
事件冒泡的過程code

全部現代瀏覽器都支持事件冒泡,並且IE9,Firefox、Chrome、Safari都將事件一直冒泡到window對象。htm

事件捕獲

接收事件的順序是:事件由最不具體的元素接收,逐級向下傳播到最具體的節點。對象

若是頁面中有個div,點擊了div以後,事件的傳播順序以下:
(1) document
(2) html
(3) body
(4) div

事件捕獲的過程以下圖:
事件捕獲的過程

IE9,Firefox、Chrome、Safari都支持這種事件流模型,並且是從window對象開始捕獲事件的。

DOM事件流

「DOM2級事件」規定事件流包含3個階段:事件捕獲階段處於目標階段事件冒泡階段
以下圖:
DOM2級執行順序

在DOM事件流中,捕獲階段包括1,2,3。冒泡階段包括4,5,6,7。其中4是處理目標階段,在事件處理中被當作是冒泡階段的一部分。

雖然「DOM級事件」規範明確規定捕獲階段不涉及事件目標,可是IE9,Firefox、Chrome、Safari和Opera9.5及以上版本都會在捕獲階段觸發事件對象上的事件,這就意味着有兩個機會在目標對象上面操做事件

IE9,Firefox、Chrome、Safari、Opera都支持DOM事件流,IE8及更早版本不支持DOM事件流。即IE9,Firefox、Chrome、Safari、Opera及支持冒泡又支持捕獲,而IE8及之前版本僅支持冒泡。

須要說明的是,在實際中,大多數都使用冒泡事件,僅在有特殊須要時才使用捕獲事件。

事件處理程序

在說事件處理程序以前,先簡單介紹下事件對象。什麼是事件對象呢?
在觸發DOM上的某個事件時,會產生一個對象,這個對象中包含全部與事件有關的信息,如致使事件的元素、事件的類型以及其餘與特定事件相關的信息,這個對象就是事件對象。全部的事件處理程序均可以訪問事件對象,可是訪問的方式不盡相同,一種是將事件對象做爲事件處理程序的參數,另外一種是事件對象做爲window的屬性,經過window.event來訪問。這裏就介紹到這裏,先有個概念~

迴歸正題,clickloadmouseover都是事件的名字,而響應某個事件的函數就叫作事件處理程序,事件處理程序的名字以「on」開頭。

那麼該怎樣爲事件指定處理程序呢?主要有兩類方法:HTML事件處理程序JavaScript指定事件處理程序

HTML特性指定事件處理程序

某個HTML元素支持的每種事件,均可以使用一個與相應事件處理程序同名的HTML特性來指定。這個特性的值是可以執行的JavaScript代碼

具體指定方式

<input type="button" value="Click" onclick="console.log('hello')" />  <!--直接是一句代碼-->



<script> function show () { console.log('hello'); } </script> <input type="button" value="Click" onclick="show()" /> <!--調用show()函數-->

這種方式中,事件處理程序中的代碼在執行時,有權訪問全局做用域中的任何代碼。

本質

這樣的事件處理程序在執行的時候,會建立一個封裝着元素屬性值的函數,而後執行這個函數。就是說,

<input type="button" value="Click" onclick="console.log('hello')" />

當單擊按鈕的時候,先建立一個函數function () { console.log('hello'); },而後當即執行該函數。

這個函數有如下3個特色
1) 函數中會有一個局部變量event,即事件對象。
注意:event是局部變量,能夠在函數中直接訪問,既不用本身定義,也不是從參數列表中讀取。這點與後面其餘指定事件處理程序的方式有所不一樣,記得區分。
2) 函數內部,this值等於事件的目標元素。
3)這個動態建立的函數使用with擴展做用域:

function () {
    with(document) {
        with(this) {
          // 元素屬性值,如 console.log('hello');
        }
    }
}

從而該函數能夠訪問document及該元素自己的任何成員。這也是爲何「採用這種方式指定的事件處理程序中的代碼在執行時,有權訪問全局做用域中的任何代碼。」,由於做用域中有document

取消事件默認行爲的方法

實質上就是讓動態建立的函數return false,因此要這樣寫:

<a href="http://www.baidu.com" onclick="console.log('hello');  return false;">baidu</a>



<script> function show () { console.log('hello'); } </script> <a href="http://www.baidu.com" onclick="show(); return false;">baidu</a>

第二種方式必定要注意,return false是加在onclick屬性值裏面的,若是放到show()裏是不起做用的哦,由於是要「讓動態建立的函數return false

3個缺點

  • 時差問題
    用戶可能會在HTML元素一出現就在頁面上觸發相應的事件,但當時的事件處理程序有可能尚不具有執行的條件,從而引起錯誤。好比上面的show()函數,若是用戶單擊按鈕的時候,function show(){}尚未執行,就會致使錯誤。
  • 這樣擴展事件處理程序的做用域鏈在不一樣的瀏覽器中會致使不一樣的結果。
  • HTML與JavaScript代碼緊密耦合,因此實際中這種方法使用的比較少。

JavaScript指定事件處理程序

JavaScript指定事件處理程序主要有兩種方式:DOM0 級事件處理程序DOM2 級事件處理程序。使用JavaScript指定事件處理程序,首先必須取得對一個操做對象的引用。

DOM0 級事件處理程序

每一個元素(包括windowdocument)都有本身的事件處理程序屬性,這些屬性一般所有小寫,如onclick,將這種屬性賦值爲一個函數,就是DOM0 級事件處理程序的指定方式。以下:

var btn = document.getElementById('myBtn'); // 取得對操做對象的引用
btn.onclick = function () { // 爲onclick賦值一個函數,指定事件處理程序
    console.log('hello');
}

移除事件處理程序:

btn.onclick = null; // 只需將屬性置空

特色:

1)添加事件處理程序的代碼只有在運行完以後纔會爲元素綁定事件處理程序,所以若是該段代碼未執行單擊按鈕是沒有反應的。
2)事件處理程序中的this引用當前元素,即綁定了該事件的元素。
3)以這種方式添加的事件處理程序會在事件流的冒泡階段被處理。
4)全部的瀏覽器都支持DOM0 級事件處理程序。

DOM2 級事件處理程序

「DOM2 級事件處理程序」定義了兩個方法,用來指定和刪除事件處理程序:addEventListener()removeEventListener()。這兩個方法都接受3個參數:要處理的事件名做爲事件處理程序的函數一個布爾值,這個布爾值參數若是爲true表示在捕獲階段調用事件處理程序,爲false表示在冒泡階段調用事件處理程序,通常設爲false

添加事件處理程序:

var btn = document.getElementById('myBtn'); // 取得對操做對象的引用
btn.addEventListener('click', function(){ // 注意是 click 哦~
    console.log('hello');
}, false);

function show() {
    console.log('hello');
}

var btn = document.getElementById('myBtn'); // 取得對操做對象的引用
btn.addEventListener('click', show, false);

移除事件處理程序:

移除事件處理程序有一個要求就是:移除時傳入的參數必須與添加事件處理程序時使用相同的參數。從而經過addEventListener()添加的匿名函數將沒法移除。

移除上面第二種方式綁定的事件處理程序代碼:

var btn = document.getElementById('myBtn'); // 取得對操做對象的引用
btn.removeEventListener('click', show, false);

要說明的是:經過addEventListener添加的事件處理程序只能經過removeEventListener()來移除。

特色

1)只有運行完addEventListener()才爲元素綁定了事件處理程序。
2)事件處理程序中的this引用當前元素,即綁定了該事件的元素。
3)經過設置第三個參數爲truefalse能夠設定在捕獲階段仍是冒泡階段調用事件處理程序。
4)能夠爲同一個元素綁定多個事件處理程序,綁定的事件處理程序會按照它們添加的前後順序依次觸發。這是DOM0 級事件處理程序不支持的哦~ DOM0 級只能添加一個事件處理程序,後添加的會覆蓋先添加的。
5)IE9、Firefox、Safari、Chrome和Opera都支持DOM2 級事件處理程序。

IE事件處理程序

上面特意加粗了IE9,那麼IE8及之前版本怎麼辦呢?IE實現了與DOM中相似的兩個方法:attachEvent()detachEvent()。這兩個方法僅接受兩個參數:事件處理程序名稱事件處理程序函數。IE8及以前版本僅支持事件冒泡,因此經過attachEvent()添加的事件處理程序會在冒泡階段調用。

注:IE10及以前版本支持attachEvent()detachEvent()方法,IE11再也不支持,通常IE9及以上都使用DOM方法,這兩個方法僅對IE8及如下版本使用。

添加事件處理程序:

var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', function () { // 注意這裏是 onclick 哦~
    console.log('hello');
});

function show() {
    console.log('hello');
}

var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', show);

移除事件處理程序:

使用detachEvent移除事件處理程序的條件與DOM方法相同——必須提供相同的參數,從而添加的匿名函數也沒法被移除。

var btn = document.getElementById('myBtn');
btn.detachEvent('onclick', show);
特色:

1)只有運行完attachEvent()才爲元素綁定了事件處理程序。
2)事件處理程序會在全局做用域中運行,從而事件處理程序中的this指向全局對象window。這與以前的都不一樣哦~因此不能認爲this始終等於事件目標,當使用attachEvent()的時候,thiswindow,不過其餘時候,this是等於事件目標的~
3)能夠爲同一個元素綁定多個事件處理程序,綁定的事件處理程序會按照它們添加順序的相反次序被觸發,即先添加後執行,後添加的先執行。

是否是以爲IE太奇葩呢,若是沒有IE,前端世界會和諧不少吧~還好IE9以上都是按照DOM標準來了,再也不我行我素。

事件對象event

這裏順便簡單說下事件對象,對比來看,會更清晰~
前面介紹過事件對象,這裏再也不贅述。

事件處理程序中訪問事件對象的方式

  • HTML特性指定事件處理程序
    事件處理程序中會有個局部變量event,這個event就是事件對象。
  • JavaScript指定事件處理程序
    不管指定事件處理程序採用的是DOM0 級仍是DOM2 級方法,事件對象都會做爲事件處理程序的參數傳入到事件處理程序中。
  • 奇葩的IE
    在使用DOM0 級方法添加事件處理程序時,事件對象做爲window對象的屬性存在,經過window.event訪問。
    若是使用attachEvent()添加事件處理程序,事件對象會做爲參數傳入到事件處理程序中。

取消默認行爲和阻止事件冒泡

  • DOM中的事件對象(兩個方法)
    preventDefault()方法用來取消事件默認行爲,stopPropagation()方法用來阻止事件進一步捕獲或冒泡

    // 假設事件對象傳入給參數event
    event.preventDefault(); // 取消事件默認行爲
    event.stopPropagation(); // 阻止事件進一步捕獲或冒泡
  • IE8及如下中的事件對象(兩個屬性)
    returnValue屬性用來設置事件的默認行爲。默認值爲true,當設置爲false時,就會取消事件的默認行爲。
    cancelBubble屬性用來取消事件冒泡。默認值爲false,當設置爲true時,就會阻止事件冒泡。

event.returnValue = false; // 取消事件的默認行爲
event.cancelBubble = true; // 阻止事件冒泡

注:這兩個屬性僅IE8及如下版本支持,IE9+使用這兩個屬性會報錯。


終於寫完了,這篇寫得時間好長……這樣整理完知識脈絡會清晰一點吧~

本文主要參考《js高級程序設計》事件一章。若有不妥之處,還請指正。謝謝!

相關文章
相關標籤/搜索