深刻理解javascript事件機制

Created By JishuBao on 2019-04-17 12:38:22
Recently revised in 2019-04-17 17:38:22javascript

 

  歡迎你們來到技術寶的掘金世界,您的star是我寫文章最大的動力!GitHub地址      html

不知道各位小夥伴在平時擼碼中會不會常常看到諸如event.preventDefault()、event.stopPropagation()之類的方法,可能一開始看到只是隨便的百度了下,大概瞭解這是幹嗎的,可是時間長了你只記得要點擊事件的時候加上一下,可是這對於咱們對代碼的掌控仍是遠遠不夠,因此技術寶深入百度,帶你們理一理這個javascript大頭:事件機制java

1、瞭解事件

什麼是事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。js與html的交互就是經過事件實現的。可使用偵聽器(或處理程序)來預訂事件,以便事件發生時執行相應的代碼。git

事件最先出現於IE3Netscape Navigator2,IE九、Firefox、Opera、Safari、Chrome所有已經實現DOM2事件.IE8是最後一個仍然使用其專有事件系統的瀏覽器。github


2、瞭解事件流

事件流描述的是從頁面中接收事件的順序。可是,IE和Netscape提出了差很少徹底相反的概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕獲流。瀏覽器

IE9/Opera/Firefox/Chrome/Safari都支持DOM事件流;IE8以及更早版本不支持DOM事件流。bash

1.事件冒泡

IE的事件流叫作冒泡事件,即事件開始時由最具體的元素接收,而後逐級向上傳播到較爲不具體的節點。click事件沿dom樹向上傳播,在每一級節點上都會發生,直至傳播到document對象。dom

IE5.5以及更早版本中的事件冒泡會跳過<html>元素,從body直接跳到document,IE9/Firefox/Chrome/Safari則將事件一直冒泡到window對象函數

2.事件捕獲

Netscape提出了另外一種事件流叫作事件捕獲。事件捕獲的用意在於在事件達到預約目標以前捕獲它。post

IE9/Safari/Chrome/Opera和Firefox目前也都支持這種事件流模型。儘管"DOM2級事件"規範要求事件應該從document對象開始傳播,可是這些瀏覽器都是從window對象開始捕獲事件的。

3.dom事件流

"dom2級事件"規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。首先發生的是事件捕獲,爲截獲事件提供了機會。而後實際的目標接受到事件。最後一個階段是冒泡階段,能夠在這個階段對事件做出相應。

在dom事件流中,實際的目標在捕獲階段不會接收到事件。這意味着在捕獲階段,事件從document到html再到body後就中止了,下一個階段是處於目標階段,因而事件再div上發生,並在事件處理中被當作冒泡階段的一部分。而後,冒泡階段發生,事件又傳播迴文檔。

雖然「dom2級事件」規範明確要求捕獲階段不會涉及到事件目標,可是IE九、Safari、Chrome、Safari及更高版本都會在捕獲階段觸發事件對象上的事件。結果,就有2個機會在目標對象上操做事件。


3、事件處理程序

事件就是用戶或瀏覽器自身執行的某種動做。諸如click、load、mouseover,都是事件的名字。而相應某個事件的函數就叫作事件處理程序(事件偵聽器)。事件處理程序以「on」開頭,所以click的事件處理程序就是onclick。爲事件指定處理程序的方式有好幾種:

1.HTML事件處理程序

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

<input type="button" value="click me" onclick="alert('clicked')"/>
複製代碼

因爲值是js,因此不能使用未轉義的HTML語法字符,如:&、""、>、<,爲了不使用HTML實體,這裏使用了單引號。若是想使用雙引號:

<input type="button" value="click me" onclick="alert(&quot;clicked me&quot;)" />
複製代碼

除了包含具體的執行動做外,還能夠調用其餘地方定義的腳本(括號不可不加)

<input type="button" value="click me" onclick="showMessage()" />
<script type="text/javascript"> function showMessage(){ alert('Hello world') } </script>
複製代碼

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

這樣指定事件處理程序會建立一個封裝着元素屬性值的函數。這個函數中有一個局部變量event,也就是事件對象

<input type="button" value="click me" onclick="alert(event.type)"/>//click
複製代碼

在這個函數內部,this的值等於事件的目標元素。

<input type="button" value="click me" onclick="alert(this.value)"/>//click me
複製代碼

在這個函數內部,能夠像訪問局部變量同樣訪問document以及該元素自己的成員。這個函數使用with像下面這樣擴展做用域:

function(){
    with(document){
        with(this){
            //元素屬性值
        }
    }
}
複製代碼

這樣以來,訪問本身的屬性簡單的多

<input type="button" value="click me 5" onclick="alert(type)"/>//button
複製代碼

若是當前元素是一個表單輸入元素,則:

function(){
    with(document){
        with(this.form){
            with(this){
                //元素屬性值
            }
        }
    }
}
複製代碼

則不須要引用表單元素就能訪問其餘表單字段

<form method="post">
	<input type="text" name="username" value="jsbao"> <input type="button" value="Echo" onclick="alert(username.value)"> //jsbao </form> 複製代碼

這樣存在3個缺點:

時差問題,用戶可能會在html元素一出現就觸發相應的事件,但當時的事件處理程序有可能尚不具有執行條件。爲避免沒必要要的錯誤,咱們通常會用try-catch塊包住:

<input type="button" value="click me" onclick="try{showMessage();}catch(ex){}"/>
複製代碼

擴展事件處理程序的做用域鏈在不一樣瀏覽器中會致使不一樣結果,由於不一樣Js引擎遵循的標識符解析規則略有差別!

html與js代碼耦合度過高!

2.DOM0級事件處理程序

經過js指定事件處理程序的傳統方式,就是將一個函數賦值給一個事件處理程序屬性。每一個元素都是本身的事件處理程序屬性,這些屬性一般所有小寫,例如onclick。將這種屬性的值設置爲一個函數,就能夠指定事件處理程序。

<input type="button" value="click me 5" id="button1"/>
var button1=document.getElementById('button1')
button1.onclick=function(){
	alert('hello world')
}
複製代碼

要注意,在這些代碼運行以前不會指定事件處理程序,所以若是這些代碼在頁面中處於按鈕後面,就有可能在一段時間內怎麼單擊都沒反應。

使用dom0級方法指定的事件處理程序被認爲是元素的方法。所以,這時候的事件處理程序是在元素的做用域中進行,程序中的this引用當前元素。

<input type="button" value="click me 5" id="button2"/>
var button2=document.getElementById('button2');
	button2.onclick=function(){
	alert(this.id)//button2
}
複製代碼

這種方式添加的事件處理程序會在事件流的冒泡階段被處理

也能夠經過dom0級方法指定的事件處理程序,只要像下面這樣將事件處理程序屬性的值設置爲null便可:

btn.onclick=null;//刪除事件處理程序
複製代碼

設置爲null之後,再單擊按鈕將不會有任何動做發生

3.DOM2級事件處理程序

dom2級事件定義了倆個方法:addEventListener()removeEventListener(),分別用於處理指定和刪除事件處理程序。全部dom節點都包含這2個方法,而且他們都接受3個參數:要處理的事件名、做爲事件處理程序的函數、一個布爾值,最後這個參數若是是true,表示在捕獲階段調用事件處理程序;若是是false,表示在冒泡階段調用事件處理程序。

<input type="button" value="click me 5" id="button3"/>
var button3=document.getElementById('button3');
button3.addEventListener("click",function(){
		alert(this.id)//button3
},false)
複製代碼

與dom0級事件同樣,這裏添加的事件處理程序也是在其依附的元素的做用域中運行,使用dom2級的好處是能夠添加多個事件處理程序。

<input type="button" value="click me 5" id="button3"/>
var button3=document.getElementById('button3');
button3.addEventListener("click",function(){
	alert(this.id)//button3
},false)
button3.addEventListener("click",function(){
	alert(this.value)//click me 5
},false)
//會按次序執行
複製代碼

經過addEventListener添加的事件處理程序只能經過removeEventListener來移除,而傳入的參數與添加處理程序使用的參數相同,這意味着經過addEventListener添加的匿名函數將沒法移除;

button3.addEventListener("click",function(){
	alert(this.value)
},false)
button3.removeEventListener("click",function(){
	alert(this.value)
},false)
//無效,由於傳入的匿名函數不同
複製代碼
var handler=function(){
	alert(this.value);
}
button3.addEventListener("click",handler,false);
button3.removeEventListener("click",handler,false);//有效!
複製代碼

大多數狀況下,都是將事件處理程序添加到事件流的冒泡階段,這樣能夠最大限度的兼容各類瀏覽器

IE、Firefox、Safari、Chrome、Opera支持dom2級事件處理程序

4.IE事件處理程序

IE實現了倆個方法:attachEvent()/detachEvent()。第一個參數爲事件程序處理名稱,第二個參數爲事件處理函數名稱。因爲IE8以及更早版本只支持冒泡,因此經過attachEvent()添加的事件處理程序都會被添加到冒泡階段。

以下爲給按鈕添加事件處理程序:

var btn=document.getElementById('myBtn');
btn.attachEvent("onclick",function(){
    alert('clicked')
})
複製代碼

與dom0事件的主要區別在於attachEvent的做用域會在全局做用域內執行,即this===window

var btn=document.getElementById('myBtn');
btn.attachEvent("onclick",function(){
    alert(this===window);//true
})
複製代碼

與addEventListener相似,也能夠爲一個元素添加多個事件處理程序,可是不一樣的是,他是按照相反的順序被觸發,detachEvent用法和removeEventListener用法同樣,再也不贅述。

var btn=document.getElementById('myBtn');
btn.attachEvent("onclick",function(){
    alert("clicked")
})
btn.attachEvent("onclick",function(){
    alert("hello world!")
})
複製代碼

支持IE事件處理程序的瀏覽器有IE和Opera

5.跨瀏覽器的事件處理程序

要保證處理事件的代碼能帶大多數瀏覽器下保持一致的運行,只需關注冒泡階段

1.建立addHandler函數

這個方法的做用是視狀況分別使用DOM0級方法、DOM2級方法、或者IE方法來添加事件。三個參數依次是:要操做的對象、事件名稱、事件處理程序函數

addHandler:function(element,type,handler){
    if(element.addEventListener){
        element.addEventListener(type,handler,false);
    }else if(element.attachEvent){
        element.attachEvent("on"+type,handler);
    }else{
        element["on"+type]=handler;
    }
}
複製代碼

2.建立removeHandler函數

這個方法的做用是移除以前添加的事件處理程序,參數和addHandler的參數一一對應

removeHandler:function(element,type,handler){
    if(element.removeEventListener){
        element.removeEventListener(type,handler,false)
    }else if(element.detachEvent){
        element.detachEvent("on"+type,handler);
    }else{
        element["on"+type]=null
    }
}
複製代碼

4、事件對象

在觸發dom上的某個事件時,會產生一個事件對象event。這個對象包含着全部與事件有關的信息。全部瀏覽器都支持event對象,但支持方式不一樣。

1.dom中的事件對象

兼容dom的瀏覽器會將一個event對象傳入事件處理程序中。event對象包含與建立它的特定事件有關的屬性和方法,不過,全部event對象包含下表所列的成員。

屬性/方法 類型 讀/寫 說明
bubbles Boolean 只讀 代表事件是否冒泡
cancelable Boolean 只讀 表面是否能夠取消事件的默認行爲
currentTarget Element 只讀 其事件處理程序當前正在處理事件的那個元素
defaultPrevented Boolean 只讀 爲true表示以及調用了preventDefault()(DOM3級事件新增)
detail Integer 只讀 與事件相關的細節信息
eventPhase Integer 只讀 調用事件處理程序的階段 1.捕獲2.處於目標3.冒泡
preventDefault Function 只讀 取消事件的默認行爲。若是cancelable是true,則可使用這個方法
stopImmediatePropagation Function 只讀 取消事件的進一步冒泡或捕獲,同時阻止任何事件處理程序被調用(dom3級事件中新增)
stopPropagation Function 只讀 取消事件的進一步冒泡或捕獲.若是bubbles是true,則可使用這個方法
target Element 只讀 事件的目標
trusted Boolean 只讀 爲true表示事件是瀏覽器生成的,爲false表示事件是由開發人員經過js建立的(dom3級事件中新增)
type String 只讀 被觸發的事件的類型
view AbstractView 只讀 與事件關聯的抽象視圖。等同與發生事件的window對象
相關文章
相關標籤/搜索