js中關於DOM的操作很多,因此js事件機制也就尤為重要。 事件綁定形式: 一. 內聯形式 耦合度高,不利於維護 點擊這個按鈕 二. 屬性綁定(DOM0級事件) 只能綁定一個函數 button.onclick = function() {}; 三. 事件監聽函數(DOM2級事件) element.a... ...
js中關於DOM的操作很多,因此js事件機制也就尤為重要。
事件綁定形式:
一. 內聯形式
耦合度高,不利於維護
<button onclick="alert('你點擊了這個按鈕');">點擊這個按鈕</button>
二. 屬性綁定(DOM0級事件)
只能綁定一個函數
button.onclick = function() {};
三. 事件監聽函數(DOM2級事件)
element.addEventListener(<event-name>, <callback>, <use-capture>);
element.removeEventListener(<event-name>, <callback>, <use-capture>);
element.attachEvent(event, callback)(IE11以後用addEventLisener);
element.detachEvent(event, callback)(IE11以後用addEventLisener);
事件代理
在父元素上綁定事件,監聽子元素的事件。主要用於子元素是新建元素或者子元素個數很多的情況下。這種方法可以提高性能,同時避免提前綁定元素事件而導致新建元素的事件沒有生效的結果。
<ul> <li>11111111111111111111</li> </ul>
document.addEventListener('click', function (e) { console.log('document currentTarget: ' + e.currentTarget.nodeName); console.log('document target: ' + e.target.nodeName); if (e.target.nodeName === 'LI') { console.log('dom delegate: ' + e.eventPhase); } console.log(this); }, true);
事件觸發順序
- Event Capturing(事件捕獲): Netscape
- Event Bubbling(事件冒泡): IE
這兩種方式確定了事件執行的前後順序,只不過後來W3C對DOM2的事件模型給出了一個規範:首先進入事件捕獲階段->達到元素後->進入事件冒泡階段。
可以通過event.eventPhase查看事件觸發階段:
eventPhase (number): 這個屬性的數字表示當前事件觸發在什麼階段。
- 0: none
- 1: 捕獲
- 2: 目標
- 3: 冒泡
1. DOM0
在元素處於目標時觸發該事件。
2. DOM2
當addEventListener的最後參數為false時,是在冒泡階段觸發。如果是true的話是在捕獲階段出發。attachEvent始終是冒泡階段觸發。
允許捕獲機制的事件流觸發順序:
事件流的觸發順序是首先從document元素開始,觸發綁定在document上的捕獲事件,依次向下,直到目標元素上。然後觸發綁定在目標元素上的事件。最後依次向上,直到觸發document元素上綁定的捕獲事件。分別可以對於捕獲階段,處於目標階段,冒泡階段,捕獲過程和冒泡過程並不包含目標元素階段。
嘗試下麵的函數:
<ul> <li>11111111111111111111</li> </ul> <script> document.querySelector('li').addEventListener('click', function (e) { console.log('False currentTarget: ' + e.currentTarget.nodeName); console.log('False target: ' + e.target.nodeName); console.log(e.eventPhase); console.log(this); }, false); document.querySelector('li').addEventListener('click', function (e) { console.log('True currentTarget: ' + e.currentTarget.nodeName); console.log('True target: ' + e.target.nodeName); console.log(e.eventPhase); console.log(this); }, true); document.querySelector('ul').addEventListener('click', function (e) { console.log('ul false currentTarget: ' + e.currentTarget.nodeName); console.log('ul false target: ' + e.target.nodeName); console.log(e.eventPhase); console.log(this); }, false); document.querySelector('ul').addEventListener('click', function (e) { console.log('ul true currentTarget: ' + e.currentTarget.nodeName); console.log('ul true target: ' + e.target.nodeName); console.log(e.eventPhase); console.log(this); }, true); document.querySelector('li').onclick = function (e) { console.log('Onclick currentTarget: ' + e.currentTarget.nodeName); console.log('Onclick target: ' + e.target.nodeName); console.log(e.eventPhase); console.log(this); }; </script>
BeCareful: 可以看到,當點擊li的時候,前兩個綁定的事件是按照順序來執行的,並沒有先執行捕獲事件然後執行冒泡事件,而是按照綁定順序執行的。且event.eventPhase的值為2。
原因是:瀏覽器針對於事件的觸發機制是,執行每個階段會先設置在哪個階段,然後在node的事件數組裡執行相應階段的事件。如果處於目標階段是不區分捕獲或者冒泡階段的,直接按照註冊順序執行當前事件數組裡的函數。event.eventPhase是在調用這些方法的時候設置的。而true和false是在註冊的時候開發者傳入的。
註釋裡面的那段話的意思就是,在目標階段會觸發捕獲和冒泡事件的監聽函數,因此,它會按照註冊順序執行,跟我們平時理解的順序並不一樣。
自定義事件
DOM3級還定義了自定義事件,自定義事件不是由DOM原生觸發的,它的目的是讓開發人員創建自己的事件。要創建的自定義事件可以由createEvent("CustomEvent");
返回的對象有一個initCustomEvent()方法接收如下四個參數。
1)type:字元串,觸發的事件類型,自定義。例如 “keyDown”,“selectedChange”;
2)bubble(布爾值):標示事件是否應該冒泡;
3)cancelable(布爾值):標示事件是否可以取消;
4)detail(對象):任意值,保存在event對象的detail屬性中;
可以像分配其他事件一樣在DOM中分派創建的自定義事件對象。如:
var div = document.getElementById("myDiv"); EventUtil.addEventHandler(div,"myEvent", function () { alert("div myEvent!"); }); EventUtil.addEventHandler(document,"myEvent",function(){ alert("document myEvent!"); }); if(document.implementation.hasFeature("CustomEvents","3.0")){ var e = document.createEvent("CustomEvent"); e.initCustomEvent("myEvent",true,false,"hello world!"); div.dispatchEvent(e); }
事件對象及行為
1. 事件目標
event = event || window.event;
target = event.target || event.srcElement;
event.target 和event.currentTarget的區別:前者是觸發事件的最終目標,後者是綁定事件時的目標。
借用事件代理的例子:
<div class="btn" onclick="console.log('onclick')">
<span>sss</span>
</div>
document.querySelector('.btn').addEventListener('click', function (e) { console.log('False currentTarget: ' + e.currentTarget.nodeName); console.log('False target: ' + e.target.nodeName); console.log(e.eventPhase); console.log(this); }, false);
2. 取消預設行為 & 阻止事件冒泡
DOM: event.preventDefault(); event.stopPropagation(); IE8以下: event.returnValue = false; event.cancelBubble = true;
return false方式:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="http://cdn.bootcss.com/jquery/1.11.0/jquery.js"></script> </head> <body> <div onclick="alert('Outer clicked')"> Outer Div<br><br> <a id="inner" href="http://www.google.com" onclick="return false;">Google</a> <script> document.getElementById('inner').addEventListener('click', function(e){ alert(e.type); e.stopPropagation(); return false; }, false); document.getElementById('inner').onclick = function(e){ alert(e.type); e.stopPropagation(); return false; }; $('#inner').click(function (e) { return false; }); </script> </div> </body> </html>
在沒有使用jquery的情況下,return false這種方式只有在DOM0的方式時可以相當於阻止預設行為的方法,DOM2級事件是不起任何效果的。
如果在jquery環境,return false相當於阻止預設行為和冒泡行為的方法,因為jquery本身的定義。
參考資料: