介紹Javascript中對Event處理,包括執行順序、自定義事件、事件觸發

通常經過以下的方法對元素添加監聽事件html

target.addEventListener(type, listener[, options]);
target.addEventListener(type, listener[, useCapture]);

當第3個參數是object時,能夠有下面的參數瀏覽器

  • capture Boolean值,代表監聽函數在捕獲階段觸發
  • once Boolean值,代表監聽函數在觸發一次以後,就會自動從監聽事件的列表中移除(至關於removeEventListener)
  • passive Boolean值,代表在監聽函數內部不會使用 preventDefault() 來取消瀏覽器的默認行爲。即便調用 preventDefault() 也沒有任何效果

關於 passive 的介紹能夠看 這篇文章異步

執行順序

首先,如今的瀏覽器基本都支持 冒泡+捕獲 的事件流模型,這裏就不在多加介紹函數

執行順序按照下面的規則(我的摸索出來的規則)

對事件對象來講: window > document > body > 父類元素 > 事件目標網站

對註冊類型來講: 內聯事件 優先於 經過代碼註冊的事件(內聯事件也就是寫在html元素標籤屬性上的事件,如 <a onclick='...'></a>)spa

對事件觸發階段來講: 捕獲 優先於 冒泡線程

對註冊事件的順序來講: 先註冊的先調用code

經過實際例子檢驗一下:htm

<div id='div1'>
	<div id='div2'>
		<div id='div3'>
			<button type='button' onclick='console.log("inline onclick")'>Test</button>
			<p>11111111111111</p>
		</div>
	</div>
</div>
function x(e){
	//用 eventPhase 來檢測當前事件的處理階段
	return  e.eventPhase==3 ? 'bubbles':'capture '
}

window.addEventListener('click',(e)=>{
	console.log('window click 0 in '+ x(e),e)
});

window.addEventListener('click',(e)=>{
	console.log('window click 1 in '+ x(e),e)
});

window.addEventListener('click',(e)=>{
	console.log('window click 2 in '+ x(e),e)
},true);

document.addEventListener('click',(e)=>{
	console.log('document click in ' + x(e) ,e)
})

document.addEventListener('click',(e)=>{
	console.log('document click in ' + x(e) ,e)
},true)

document.querySelector('#div2').addEventListener('click',(e)=>{
	console.log('----start------')
	console.log('event in ' + x(e),e)
	console.log('target',e.target)
	console.log('current target',e.currentTarget)
	console.log('----end------')
})

document.querySelector('button').addEventListener('click',(e)=>{
	console.log('----start------')
	console.log('event in ' + x(e),e)
	console.log('target',e.target)
	console.log('current target',e.currentTarget)
	console.log('----end------')
},true)

點擊按鈕,咱們能夠看一下控制檯是如何輸出的對象

通常是使用下方兩個方法中斷事件流的傳播(注意,阻止事件傳播不是銷燬event對象,event依然會返回給瀏覽器)

event.stopPropagation() // 阻止event往其餘事件目標傳播,可是當前事件目標其餘的監聽函數會依次調用
event.stopImmediatePropagation() // 當即阻止event傳播,不會調用其餘的監聽函數

阻止事件執行瀏覽器的默認行爲(調用以後沒法恢復)

event.preventDefault()

有些事件的觸發機制是連續性的,好比 click 事件須要 mousedownmouseup 這兩個前置事件的觸發(鼠標按鍵按下而後彈起纔算是一個完整的點擊),若是在mouseup裏用event.preventDefault() 阻止mouseup的默認行爲,click 事件也所以沒法觸發。 相似的有 keypress 須要 keydownkeyup,還有其餘。

這裏有一個簡單的應用:好比一個網站使用這樣的代碼document.addEventListener('copy',(e)=>{e.preventDefault()}) 讓用戶不能複製頁面上的內容。 要突破複製限制,通常的解決方法是用 removeEventListener,但是這裏行不通,由於監聽函數是一個匿名函數,而你又沒法獲得這個函數的引用。 替代的方案是window.addEventListener('copy',(e)=>{e.stopImmediatePropagation()})

這裏利用了監聽函數執行的優先順序,提早終止了事件的傳播,讓網站阻止複製的代碼沒法執行。

自定義事件

Event 接口表明了DOM中全部事件,是全部事件的父類。在DOM中事件的種類很是多(連接),他們的初始化參數也比較複雜,不可能一一列舉,這裏只介紹幾種用代碼的方式自動義事件的例子(不推薦使用 document.createEvent的方式構建事件)

Event 構造函數 (初始化參數請參考文檔

event = new Event(typeArg, eventInit);

typeArg表示這個事件的名稱,能夠按照本身的需求定義。若是名稱定義爲瀏覽器內置事件的名稱,這個事件是沒有實際效果的,由於這些事件都有屬於他們本身的構造函數,如 click 對應 new MouseEvent

eventInit對象有3個參數

  • bubbles Boolean值,代表事件是否須要冒泡階段
  • cancelable Boolean值,代表事件是否能夠取消
  • composed Boolean值,跟 Shadow DOM 有關,暫時不瞭解做用

eventInit只有3個參數,基本沒什麼用處,也不能傳遞數據,因此經過Event構造函數建立的事件只有簡單的通知做用,告訴你有某個事件被觸發了,而後就沒了。

MouseEvent 構造函數(初始化參數請參考文檔

event = new MouseEvent(typeArg, mouseEventInit);

mouseEventInit 可用的參數比較多,選擇幾個重要的說明一下

mouseEventInit = {
	button: 0, // 鼠標事件的按鈕,0是左鍵,1是中鍵,2是右鍵
	ctrlKey: false,
	shiftKey: false,
	altKey: false, // 對應於鍵盤上的三個控制鍵是否被按下
	screenX: 0,
	screenY: 0, // 對應事件觸發時鼠標在用戶屏幕上的座標
	clientX: 0,
	clientY:0, // 對應事件觸發時鼠標在用戶瀏覽器界面上的座標
}

建立一個鼠標事件能夠這樣

var event = new MouseEvent('click',{}) // 對,什麼都不要,由於參數都有默認值

或者正規一點

var event = new MouseEvent('click', {
	button: 1,
	view: window,
	bubbles: true,
	cancelable: true
});

CustomEvent 構造函數

event = new CustomEvent(typeArg, customEventInit);

CustomEvent顧名思義,就是自定義事件,它可讓咱們把數據放在事件對象裏面,而且隨着事件的傳播而傳播(終於有一個實用的了),而上面兩個事件通常都由瀏覽器根據用戶的行爲觸發

var event = new CustomEvent('cat', {
	detail: { // 數據放在 detail 下面,支持任何類型的數據,好比這裏的數據類型是一個對象
		data: 'balabalalalala',
		getMySalary: function(){
			return 20000;
		}
	}
});

要建立瀏覽器自帶事件,並用代碼來模擬事件的觸發,須要選擇正確構造函數和正確的構造參數來建立事件。這些參數都在 MDN 上有詳細的介紹,想對事件深刻理解的話不妨認真閱讀。

事件觸發

我我的瞭解到的總共有這3種觸發方式

  • 用戶事件觸發,瀏覽器本身觸發事件
  • 由於事件的傳播而被動觸發,好比事件委託
  • 用 element.dispatchEvent(event) 或 用 click()、focus()、blur()等 代碼的方式主動觸發

這三種方法的觸發是相互影響的,並不能徹底獨立開來。

「原生」事件如瀏覽器本身觸發事件在執行監聽函數時是異步執行的,可是 element.dispatchEvent(event) 是同步執行的,也就是說,dispatchEvent調用後,當前的JS運行線程會阻塞在這裏,直到全部的監聽函數被執行並結束。

鑑別是瀏覽器派發的事件仍是經過JS代碼的方式手動觸發的事件,只需檢測 event裏的isTrused 屬性,爲true就是瀏覽器觸發的,false則是代碼觸發的,這個屬性沒法用初始化參數進行初始化

相關文章
相關標籤/搜索