撥開JS事件的迷霧(一)

靈感愛學習發表於2018-09-01

由來

當我還很菜的時候(好像就是現在~),參加過一次面試,面試官問我,給一個元素繫結事件有幾種方法,雖然我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總體的知識點可謂相當龐雜,但再龐雜的東西都是由小的點組成,把它的要點和脈絡抽出,進行個個擊破,學習的過程就會顯得輕鬆愉快,最終繪成一張大的版圖。

欲知後事如何,我們下回繼續。

相關文章