js事件詳解

引用

事件是我認爲前端最特別的地方,這是惟一其餘語言不同的地方,咱們經過它與頁面進行交互。html

事件流

事件流描述的是從頁面中接收事件的順序。IE和網景團隊提出流相反的事件流概念。IE事件流是事件冒泡流,而網景是事件捕獲流。前端

事件冒泡

IE的事件流叫作事件冒泡,即事件開始由最具體的元素接收,而後逐級向上傳播到較爲不具體的節點。web

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div>
        點我點我
    </div>
</body>
</html>

若是你單擊了div元素,那麼click事件會按照以下事件傳播瀏覽器

<div>
<body>
<html>
document
點擊元素會從最具體的元素向上傳播。全部現代瀏覽器都支持事件冒泡,可是具體實現仍是有些差異,IE5.5及更早版本的事件會跳過<html>元素。函數

事件捕獲

網景公司提出的另外一個事件流叫作事件捕獲,事件捕獲的思想上不太具體的節點應該更早接收到事件,而最具體的元素應該最後接收到事件,事件捕獲的目的的事件到達預期目標以前捕獲它。以前的例子來講就是this

document
html
body
divspa

因爲老版的瀏覽器不支持事件捕獲,因此咱們通常使用的都是事件冒泡。code

DOM事件流

DOM2級規定的事件流包含三個階段,事件捕獲階段、處於目標階段和事件冒泡階段。htm

clipboard.png

事件處理程序

事件就是用戶或瀏覽器自身執行的某種動做,click、load、mouseover,都是事件的名字。而響應某個事件的函數就叫作事件處理程序,click的事件處理程序就是onclick,load事件處理程序就是onload。對象

HTML事件處理程序

<input type="button" value="Click me" onclick="alert(clicked)" />

在HTML中指定事件處理程序有兩個缺點,一是存在時間差,若是頁面已經出現了HTML元素,可是js還沒加載完,這時候就會報錯。還有就是會形成js代碼與HTML代碼耦合。

DOM0級事件處理程序

經過js指定事件處理程序的傳統方式,就是將一個函數賦值給一個事件處理程序屬性。這種爲事件處理程序賦值的方法是在第四代web瀏覽器中出現的,如今瀏覽器還支持這種操做。
使用DOM0級方法指定的事件處理程序被認爲是元素的方法。所以,這時候的事件處理程序是在元素的做用域中運行;程序中的this指向當前元素。

var btn = document.getElementById('mybtn');
btn.onclick = function () {
    alert(this.id);    //mybtn
}

DOM0級添加的事件處理程序會在事件流的冒泡階段被處理。
也能夠經過刪除DOM0級方法指定的事件處理程序,只要像下面這樣將事件事件處理程序屬性設置爲null就能夠了。
將事件處理程序設置爲null以後,再單擊按鈕將不會有任何動做發生。

DOM2級事件處理程序

「DOM2級事件」定義了兩個方法,用於處理指定和刪除事件處理程序的操做:addEventListener()和 removeEventListener()。全部DOM節點都包含這兩個方法,而且它們都接收三個參數,要處理的事件名、事件處理函數、一個布爾值。最後一個參數若是是true,表示在捕獲階段處理程序,若是是false,表示在捕獲階段處理程序。
與DOM0級方法同樣,這裏的this指向是添加處理事件的元素中執行,使用DOM2級能夠爲元素添加多個事件處理程序,順序按添加事件的順序執行。經過addEventListener添加的事件處理程序只能經過removeEventListener移除,移除傳入的參數必須與添加時的參數相同,這就覺得添加的匿名函數沒法移除。

IE中事件處理程序

IE實現了兩個方法:attachEvent和detachEvent。這兩個方法,經過attachEvent添加的事件處理程序都會被添加到冒泡階段,注意attachEvent第一個參數是onclick,而非addEventListener方法中的click。在IE中使用attachEvent的this指向的是全局做用域。

var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', function () {
    console.log(this === window); // true
})

一樣的能夠添加多個事件,不過事件的執行順序是相反的。
咱們能夠封裝跨瀏覽器處理程序。

var EventUtil = {

        addHandler: function (ele, type, handler) {
            if (ele.addEventListener) {
                ele.addEventListener(type, handler, false)
            } else if (ele.attachEvent) {
                ele.attachEvent(`on${type}`, handler)
            } else {
                ele[`on${type}`] = handler
            }
        },
        removeHandler: function (ele, type, handler) {
            if (ele.removeEventListener) {
                ele.removeEventListener(type, handler, false)
            } else if (ele.detachEvent) {
                ele.detachEvent(`on${type}`, handler)
            } else {
                ele[`on${type}`] = null
            }
        },
    }

DOM中的事件對象

不管指定事件處理程序時用的什麼方法,都會傳入一個event對象。事件處理程序內部,對象this的值等於currentTarget的值,而target只包含事件的實際目標,若是直接將事件處理程序指定給了目標元素,this、currentTarget和target包含相同的值。

var bth = document.getElementById('myBth');

bth.onclick = function (e) {
    console.log(e.currentTarget === this); // true
    console.log(e.target === this); // true
}

能夠經過e.preventDefault()阻止事件的默認行爲,cancelable爲true的事件才能夠阻止。
stopPropagation()用於當即中止事件在DOM中的傳播,取消進一步的事件捕獲或冒泡。

var btn = document.getElementById('myBtn');

btn.onclick = function (e) {
    e.stopPropagation();
}

document.body.onclick = function (e) {
    //不執行
}

能夠用eventPhase來肯定事件處於哪一個事件流階段。
1:捕獲
2:事件目標對象
3:冒泡
只有在事件處理程序執行期間,event對象纔會存在,執行完成後,對象就會被銷燬。

IE中的事件對象

在使用DOM0級方法添加事件處理程序時,event 對象作爲window對象的一個屬性存在。

var btn = document.getElementById('myBtn');
btn.onclick = function () {
    var event = window.event;
    console.log(event.type)
}

var btn = document.getElementById('myBtn');
btn.onclick = function () {
    var event = window.event;
    console.log(event.srcElement === this)  //true
    //event.srcElement 至關於DOM中的target
}

var btn = document.getElementById('myBtn');
btn.attachEvent = function (event) {
    console.log(event.srcElement === this)  //false
    //this是window
}

returnValue屬性至關於preventDefault,cancelBubble屬性stopPropagation()方法類型,不過其只能阻止冒泡,由於低版本IE只支持冒泡。

跨瀏覽器的事件對象

var EventUtil = {
    addHandler: function (ele, type, handler) {

    },
    getEvent: function (event) {
        return event ? event : window.event
    },
    getTarget: function (event) {
        return event || event.srcElement
    },
    preventDefault: function (event) {
        if (event.preventDefault) {
            event.preventDefault()
        } else {
            event.returnValue = false
        }
    },
    stopPropagation: function (event) {
        if (event.stopPropagation) {
            event.stopPropagation()
        } else {
            event.cancelBubble = true
        }
    }
}
相關文章
相關標籤/搜索