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
什麼是事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間。js與html的交互就是經過事件實現的。可使用偵聽器(或處理程序)來預訂事件,以便事件發生時執行相應的代碼。git
事件最先出現於IE3、Netscape Navigator2,IE九、Firefox、Opera、Safari、Chrome所有已經實現DOM2事件.IE8是最後一個仍然使用其專有事件系統的瀏覽器。github
事件流描述的是從頁面中接收事件的順序。可是,IE和Netscape提出了差很少徹底相反的概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕獲流。瀏覽器
IE9/Opera/Firefox/Chrome/Safari都支持DOM事件流;IE8以及更早版本不支持DOM事件流。bash
IE的事件流叫作冒泡事件,即事件開始時由最具體的元素接收,而後逐級向上傳播到較爲不具體的節點。click事件沿dom樹向上傳播,在每一級節點上都會發生,直至傳播到document對象。dom
IE5.5以及更早版本中的事件冒泡會跳過<html>元素,從body直接跳到document,
IE9/Firefox/Chrome/Safari則將事件一直冒泡到window對象
函數
Netscape提出了另外一種事件流叫作事件捕獲。事件捕獲的用意在於在事件達到預約目標以前捕獲它。post
IE9/Safari/Chrome/Opera和Firefox目前也都支持這種事件流模型。儘管"DOM2級事件"規範要求事件應該從document對象開始傳播,可是這些瀏覽器都是從window對象開始捕獲事件的。
"dom2級事件"規定的事件流包括三個階段:
事件捕獲階段、處於目標階段和事件冒泡階段
。首先發生的是事件捕獲,爲截獲事件提供了機會。而後實際的目標接受到事件。最後一個階段是冒泡階段,能夠在這個階段對事件做出相應。
在dom事件流中,實際的目標在捕獲階段不會接收到事件。這意味着在捕獲階段,事件從document到html再到body後就中止了,下一個階段是處於目標階段,因而事件再div上發生,並在事件處理中被當作冒泡階段的一部分。而後,冒泡階段發生,事件又傳播迴文檔。
雖然「dom2級事件」規範明確要求捕獲階段不會涉及到事件目標,可是IE九、Safari、Chrome、Safari及更高版本都會在捕獲階段觸發事件對象上的事件。結果,就有2個機會在目標對象上操做事件。
事件就是用戶或瀏覽器自身執行的某種動做。諸如click、load、mouseover,都是事件的名字。而相應某個事件的函數就叫作事件處理程序(事件偵聽器)。事件處理程序以「on」開頭,所以click的事件處理程序就是onclick。爲事件指定處理程序的方式有好幾種:
某個元素支持的每種事件,均可以使用一個與相應事件處理程序同名的Html特性來指定。這個特性的值應該是可以執行的js代碼。
<input type="button" value="click me" onclick="alert('clicked')"/>
複製代碼
因爲值是js,因此不能使用未轉義的HTML語法字符,如:&、""、>、<,爲了不使用HTML實體,這裏使用了單引號。若是想使用雙引號:
<input type="button" value="click me" onclick="alert("clicked me")" />
複製代碼
除了包含具體的執行動做外,還能夠調用其餘地方定義的腳本(括號不可不加)
<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代碼耦合度過高!
經過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之後,再單擊按鈕將不會有任何動做發生
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級事件處理程序
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
要保證處理事件的代碼能帶大多數瀏覽器下保持一致的運行,只需關注冒泡階段
這個方法的做用是視狀況分別使用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;
}
}
複製代碼
這個方法的做用是移除以前添加的事件處理程序,參數和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
}
}
複製代碼
在觸發dom上的某個事件時,會產生一個事件對象event
。這個對象包含着全部與事件有關的信息。全部瀏覽器都支持event對象,但支持方式不一樣。
兼容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對象 |