由來
當我還很菜的時候(好像就是現在~),參加過一次面試,面試官問我,給一個元素繫結事件有幾種方法,雖然我JS不熟,但覺得,沒吃過豬肉還沒見過豬跑嗎?張口就來。
行內onclick啊,bind啊,還有那個什麼Event啊...
我努力在腦海中搜尋見過的“豬”,他一邊聽一邊微微點頭,好像是懂了,可我自己都不確定自己說的是什麼(捂臉),他沒有笑出聲來就算很nice了。
那次過後,我決定把沒弄清楚的給好好學習下,就是這篇文的由來,算個總結,也給仍一團糊塗的朋友一點幫助。
既然是撥開“迷霧”,就不再主動製造障礙,像歷史、深層次原理、某種事件型別的細節等就不再羅列,以免增加理解和記憶負擔,內容較基礎,高手可繞道~
事件
事件,即發生了某種行為,日常瀏覽網頁的時候,不論是使用者操作,還是開發者主動控制,都會有很多事件發生,比如:載入、點選、滾動、觸控等。所以它牽扯到幾個方面因素:
在哪裡發生——繫結 在何時發生——監聽 發生了什麼——型別 發生的過程——流程
繫結
俗話說,在哪裡跌倒,就從哪裡爬起來,先聊繫結。
直接上碼:
<button onclick="alert(你點到我了!)"></button>
複製程式碼
這行程式碼給button繫結了一個click事件,彈出“你點到我了!”,這是最直接的方式,也是我們最初接觸的方式,但顯然這種結構和行為混在一起的方式是不太好的,來一點優化。
繼續上碼:
<button onclick="tips()"></button>
<script type="text/javascript">
function tips(){
alert("你點到我了!");
}
</script>
複製程式碼
有了什麼變化?我們把所要執行的事件內容提了出來,單獨封裝,這樣以來,當執行內容較多時,不會讓HTML顯得很雜亂,而且可複用,易維護,還能怎樣優化?
以上兩種方式,都需要HTML的參與,即結構和行為相耦合,下面介紹不動HTML的DOM事件處理程式。
一、DOM0級事件
接著上碼:
//新增
<button id=“btn”></button>
<script type="text/javascript">
var btn=document.getElementById("btn");
btn.onclick=function(){
alert("你點到我了!");
}
</script>
//刪除
btn.onclick=null;
複製程式碼
這裡是將一個函式賦值給一個事件處理程式的屬性,這種方法,是DOM0級事件處理程式,出現的時間較早。
它的優點是:簡單、跨瀏覽器,當使用更高階的方式而瀏覽器不支援的時候,可以回退到這種方式。
二、DOM2級事件
你沒看錯,不是沒有DOM1級,而是DOM1級沒有定義事件處理相關的東西,所以這裡直接跳到2級。
So,我們來看看DOM2級的標準之下,上面的程式碼怎麼寫:
<button id=“btn”></button>
<script type="text/javascript">
var btn=document.getElementById("btn");
btn.addEventListener("click",function(){
alert("你點到我了!");
},false);
</script>
複製程式碼
發現了什麼?它們之間只有一處不同,即事件處理程式的繫結方法——addEventListener。其標準用法如下:
addEventListener("事件名",事件處理程式,布林值)
複製程式碼
前面兩個很直觀,但第三個布林值是什麼意思?其實它決定了事件處理程式什麼時候呼叫,true是捕獲階段,false是冒泡階段。多數情況下都會在冒泡階段呼叫,這樣能最大限度相容瀏覽器,至於捕獲和冒泡是什麼以及有什麼用處,後續單獨聊。
使用DOM2級方法的主要好處,是可以新增多個事件處理程式。比如:
btn.addEventListener("click",function(){
alert(this.id);
},false);
btn.addEventListener("click",function(){
alert("你點到我了!");
},false);
複製程式碼
它們會依次按順序執行。
既然有新增,當然要有移除,移除事件的方法就是removeEventListener()。和新增時傳入的引數相同,這意味著,這種方法新增的匿名函式將無法移除。
相容?
我們聊很多技術的時候都會做這樣的區分:現代瀏覽器、老版瀏覽器。這是事物發展的必然規律,事件也不例外,那麼除了上面提到的還有什麼不同?——IE。
大家知道,IE通常都是特立獨行的,它新增和刪除事件處理程式的方法分別是:
attachEvent() 和 detachEvent()
複製程式碼
同樣需要有“事件名”和處理函式兩個引數,但跟addEventListener()的區別是:
- 事件名稱需要加“on”,比如“onclick”;
- 沒了第三個布林值,IE8及更早版本只支援事件冒泡;
- 仍可新增多個處理程式,但觸發順序相反。
還有一點需要注意,DOM0和DOM2級的方法,其作用域都是在其所依附的元素當中,attachEvent()則是全域性,即如果像之前一樣使用this.id,訪問到的就不是button元素,而是window,就得不到正確的結果。
那麼,跨瀏覽器新增事件怎麼寫呢?這類程式碼通常具有通用性,所以可以將它封裝成一個物件以備多人多專案複用,程式碼如下:
var EventUtil={
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;
}
},
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;
}
}
}
複製程式碼
可明顯看出,其使用了條件判斷來檢測瀏覽器是否具備那個方法,如果有,就用,沒有,向更古老和通用的方法回溯。
怎麼使用它呢?就像這樣
EventUtil.addHandler(btn,"click",handler);
EventUtil.removeHandler(btn,"click",handler);
複製程式碼
小結
至此,JS事件的新增方法以及怎樣做相容就大概介紹完了,這只是事件的一個方面,也已有不少內容。
在現今JS框架滿天飛的時代,從較早的JQ到較近的React、Vue,我們似乎很少需要再手動書寫原生方法,它們本身封裝的方法替我們省去了很多重複繁雜的勞動,但去了解和學習這些知識仍是有好處的。
JS總體的知識點可謂相當龐雜,但再龐雜的東西都是由小的點組成,把它的要點和脈絡抽出,進行個個擊破,學習的過程就會顯得輕鬆愉快,最終繪成一張大的版圖。
欲知後事如何,我們下回繼續。