一、事件流
事件流指從頁面中接收事件的順序。
1.事件冒泡(常用)
IE中采用的事件流是事件冒泡,先從具體的接收元素,然后逐步向上傳播到不具體的元素。
2.事件捕獲(少用)
Netscapte采用事件捕獲,先由不具體的元素接收事件,最具體的節點最后才接收到事件。
3.DOM事件流
DOM2級事件包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。
二、事件處理程序
事件處理程序就是響應某些事件的函數,如onclick等。
1. DOM0級事件處理程序
每個元素都有自己的事件處理程序屬性,如onclick等。可以通過js將一個函數賦值給元素的事件處理程序屬性。
在DOM0事件處理程序中,事件處理程序里的this指向當前元素。
var objlogo=document.getElementById("site_nav_top"); objlogo.onclick=function(){ alert(this.innerHTML);//代碼改變世界 }
刪除DOM0事件,只需將事件處理程序的值賦為null即可。
objlogo.onclick=null;
2. DOM2級事件處理程序
DOM2有兩個方法用來添加和移除事件處理程序:addEventListener()和removeEventListener()。它們都有三個參數:第一個參數是事件名(如click);第二個參數是事件處理程序函數;第三個參數如果是true則表示在捕獲階段調用,為false表示在冒泡階段調用。
- addEventListener():可以為元素添加多個事件處理程序,觸發時會按照添加順序依次調用。
- removeEventListener():不能移除匿名添加的函數。
//不能移除 var obj = document.getElementById('site_nav_top');
obj.addEventListener('click', function () {
alert(this.innerHTML);
}, false);
obj.removeEventListener('click', function () {
alert(this.innerHTML);
}, false);
//能移除 var obj=document.getElementById("site_nav_top"); var show=function(){ alert(this.innerHTML); } obj.addEventListener("click",show,false); obj.removeEventListener("click",show,false);
3.IE事件處理程序
IE的事件處理程序也有兩個類似的方法:attachEvent()和detachEvent()。它們有兩個參數:第一個是事件名(如onlick);第二個參數是事件處理程序的函數。
注意:
- 事件名前面有on前綴;
- 在事件處理程序的函數中,this不再指向當前元素,而是指向window對象。
var obj=document.getElementById("site_nav_top"); var show=function(){ alert(obj.innerHTML); } obj.attachEvent("onclick",show); obj.detachEvent("onclick",show);
4.跨瀏覽器的事件處理程序
綜合前面幾種情況,進行瀏覽器能力檢測,就可以寫出跨瀏覽器的事件處理程序了.
EventUtil = { addHandler: function (obj, event, handler) { if (obj.addEventListener) { obj.addEventListener(event, handler, false); } else if (obj.attachEvent) { obj.attachEvent('on' + event, handler); } else { obj['on' + event] = handler; } }, removeHandler: function (obj, event, handler) { if (obj.removeEventListener) { obj.removeEventListener(event, handler, false); } else if (obj.detachEvent) { obj.detachEvent('on' + event, handler); } else { obj['on' + event] = null; } } };
var obj = document.getElementById('site_nav_top'); var show = function () { alert(obj.innerHTML); } EventUtil.addHandler(obj, 'click', show); EventUtil.removeHandler(obj, 'click', show);
三、事件對象
當觸發DOM事件時,會產生一個事件對象event。不同瀏覽器對該對象的支持略有不同。
1.DOM中的事件對象
在支持DOM0或DOM2的瀏覽器中,會將event對象傳入到事件處理程序中。
var obj=document.getElementById("site_nav_top"); var show=function(event){ alert(event.type); } obj.addEventListener("click",show,false);//點擊運行結果 click
event對象包含於創建它的事件對象相關的屬性和方法。幾種常用屬性和方法:
① this, currentTarget 與 target:
event.this與event.currentTarget始終相等,表示事件處理程序當前正在處理的元素;
event.target表示事件的實際目標元素。
var obj=document.getElementById("site_nav_top"); var show=function(event){ console.log(this); console.log(event.currentTarget); console.log(event.target); } obj.addEventListener("click",show,false); //點擊頭部“代碼改變世界”結果 <div id="site_nav_top">,<div id="site_nav_top">,<div id="site_nav_top"> var show=function(event){ console.log(this); console.log(event.currentTarget); console.log(event.target); } document.body.addEventListener("click",show,false); //點擊頭部“代碼改變世界”結果: <body>, <body>, <div id="site_nav_top">
②event.type:被觸發的事件類型,如click。通過判斷該屬性,可以用一個函數中處理多個事件。
var obj=document.getElementById("nav_menu"); var handler=function(event){ switch(event.type){ case "mouseover": event.target.style.backgroundColor="red"; break; case "mouseout": event.target.style.backgroundColor=""; break; case "click": alert("click menu"); break; } } obj.addEventListener("click",handler,false); obj.addEventListener("mouseover",handler,false); obj.addEventListener("mouseout",handler,false);
③event.preventDefault():阻止事件的默認行為。
例如在上面的例子中,單擊站點導航條在彈出“click menu”后,仍然打開了鏈接。如果想阻止其默認行為,可以這樣處理:
case "click": alert('click menu') event.preventDefault(); break;
④event.stopPropagation():阻止事件進一步冒泡或捕獲。
var obj = document.getElementById('nav_menu'); var handler = function (event) { alert('handler'); } var handler1 = function (event) { alert('handler1'); } obj.addEventListener('click', handler, false); document.body.addEventListener('click', handler1, false);
在這個例子中給菜單和body分別添加了單擊事件,當點擊菜單時會出現兩次彈窗。如果想阻止事件冒泡,可以做如下修改:
var handler=function(event){ alert('handler'); event.stopPropagation(); }
2. IE中的事件對象
①event對象
使用DOM0添加的事件處理程序,event作為window對象的屬性存在。
var obj=document.getElementById("nav_menu"); obj.onclick=function(event){ alert(window.event.type); }
使用attachEvent添加的事件處理程序,可以將event作為函數的參數傳入,也可以直接使用window.event。
var obj=document.getElementById("nav_menu"); obj.attachEvent("onclick",function(event){ alert(event.type); alert(window.event.type); });
②event.srcElement:事件的目標元素,等同於DOM中的target屬性。
③event.returnValue:將該屬性設置為false可以阻止默認事件。相當於DOM中的preventDefault()。
④event.cancelBubble:將該屬性設為true可以阻止事件冒泡。相當於DOM中的 stopPropagation()。
3.跨瀏覽器的事件對象
綜合考慮DOM和IE中的事件對象,寫出跨瀏覽器的事件對象,放在之前的EventUtil中。
EventUtil = { addHandler: function (obj, event, handler) { ...... }, removeHandler: function (obj, event, handler) { ...... }, getEvent: function (event) { return event ? event : window.event; }, getTarget: function (event) { return event.target || event.srcElement; }, preventDefault: function (event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } }, stopPropagation: function (event) { if (event.stopPropation) { event.stopPropation(); } else { event.cancelBubble = true; } } }; //以園子首頁菜單欄為例調用 var obj = document.getElementById('nav_menu'); EventUtil.addHandler(obj, 'click', function (event) { var event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); alert(target); EventUtil.preventDefault(event); EventUtil.stopPropagation(event); }); EventUtil.addHandler(document.body, 'click', function () { alert('body'); })
四、事件類型
1.load事件
load可以用來判斷圖片加載完畢。注意:新創建的圖像元素不是在加載到頁面中才開始下載,而是設置src之后就開始下載。
var obj = document.getElementById('site_nav_top'); EventUtil.addHandler(obj, 'click', function () { var img = new Image(); EventUtil.addHandler(img, 'load', function () { alert('load'); }); img.src = 'http://static.cnblogs.com/images/logo_small.gif'; obj.appendChild(img); })
load可以用來判斷js加載完成。<script>元素可以觸發load事件,來判斷動態加載的js文件是否加載完成。它和img不同,必須設置了src屬性並且添加到文檔之后才會開始下載。
EventUtil.addHandler(document.body,"click",function(){ var script=document.createElement("script"); EventUtil.addHandler(script,"load",function(){ alert('load'); }); script.src="http://common.cnblogs.com/script/jquery.js"; document.body.appendChild(script); })
2.resize事件:當瀏覽器的寬度或高度變化時觸發該事件。有些瀏覽器窗口變化1像素時就觸發該事件,有的瀏覽器在停止調整窗口大小時才出發該事件。因此不要在該方法中寫大量的計算代碼,以免減緩瀏覽器速度。
3.獲取鼠標位置
- clientX和clientY:客戶區坐標位置。表示鼠標在視口中的水平位置和垂直位置。
- pageX和pageY:頁面坐標位置。表示鼠標在頁面中的位置,從頁面本身而不是視口的左邊和頂部開始計算的。
- screenX和screenY:屏幕坐標位置。鼠標指針相對於整個屏幕的坐標位置。
var obj = document.getElementById('headline_block'); EventUtil.addHandler(obj, 'click', function (event) { var result = event.clientX + ',' + event.clientY + '\r\n'; result += event.pageX + ',' + event.pageY+'\r\n'; result+=event.screenX+','+event.screenY; alert(result); })
4.beforeunload事件
事件在瀏覽器卸載頁面之前觸發,可以通過它來取消卸載並繼續使用原來的頁面。
要在卸載頁面時顯示彈窗提示,需要將event.returnValue設置為提示語,並且將該提示語作為函數的返回值。
EventUtil.addHandler(window,'beforeunload',function(event){ event=EventUtil.getEvent(event); var msg='確認關閉嗎'; event.returnValue=msg; return msg; });
五、內存和性能
1.事件委托
事件委托可以解決頁面中事件處理程序過多的問題。事件委托利用了事件冒泡,只指定一個事件處理程序,就可以處理某一類型的所有事件。
EventUtil.addHandler(document.body, 'click', function (event) { event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch (target.id) { case 'site_nav_top': alert('口號'); break; case 'nav_menu': alert('點擊菜單'); EventUtil.preventDefault(event); break; case 'editor_pick_lnk': alert('推薦區'); EventUtil.preventDefault(event); break; } });
2.移除事件處理程序
如果內存中保留大量無用的事件處理程序,會影響性能。所以一定要在不需要的時候及時移除事件處理程序。尤其注意以下情況:
使用innerHTML刪除帶有事件處理程序的元素時,要先將事件處理程序設置為null。使用委托也可以解決這個問題,不直接將事件加載會被innerHTML替換的元素,而是將事件賦給其父元素,這樣就可以避免了。
卸載頁面時,最好手工清除所有的事件處理程序。
六、DOM中的事件模擬
1. DOM中的事件模擬分三步:
- 使用document.createEvent()創建event對象。通過賦值不同的參數,可以模擬不同的事件類型
- 初始化event對象;
- 觸發事件,調用dispatchEvent()方法,所有支持事件的DOM節點都可以支持這個方法。
2. 模擬鼠標事件
首先createEvent()方法傳入參數是“MouseEvent”來模擬鼠標事件。
返回的event對象有一個initMouseEvent()方法,用來初始化事件信息。該方法有15個參數:
- type(字符串):要觸發的事件類型,比如“click”。
- bubbles(bool):事件是否冒泡。一般設為true。
- cancelable(bool):事件是否可以取消。一般設為true。
- view:與事件關聯的視圖,一般設置為document.defaultView。
- detail(整數):與事件有關的詳細信息,一般設置為0.
- screenX:事件相對於屏幕的X坐標。
- screenY:事件相對於屏幕的Y坐標。
- clientX:事件相對於視口的X坐標。
- clientY:事件相對於視口的Y坐標。
- ctrlKey(bool):是否俺下了Ctrl鍵,默認為false。
- altKey:是否按下了alt鍵,默認false。
- shiftKey:是否按下了shift鍵,默認false。
- metaKey:是否按下了meta鍵,默認false.
- button(整數):按下了哪個鼠標鍵,默認0。
- relatedTarget:與事件相關的對象,一般為null.只在模擬mouseover和mouseout的時候會用到。
最后給DOM元素調用dispatchEvent()方法來觸發事件。
//執行位置博客園首頁 var objmenu = document.getElementById('nav_menu'); objmenu.onclick = function () { console.log('menu'); } document.body.onclick = function () { console.log('body'); } var event = document.createEvent('MouseEvent'); event.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); for (var i = 0; i < 5; i++) { objmenu.dispatchEvent(event); }
3. 自定義DOM事件
DOM3中支持自定義DOM事件。要創建新的自定義事件,可以調用document.createEvent()方法,返回的對象有一個initCustomEvent()方法,包含四個參數:
- type:觸發事件的類型;
- bubbles:事件是否冒泡;
- cancelable:事件是否可以取消;
- detail:任意值,保存在event.detail屬性中。
最后在DOM元素調用dispatchEvent()方法觸發事件。
var objmenu = document.getElementById('nav_menu'); EventUtil.addHandler(objmenu, 'myevent', function (event) { console.log('menu' + event.detail); }); EventUtil.addHandler(document.body, 'myevent', function (event) { console.log('body' + event.detail); }) if (document.implementation.hasFeature('CustomEvents', '3.0')) { var event = document.createEvent('CustomEvent'); event.initCustomEvent('myevent', true, true, '測試事件detail'); for (var i = 0; i < 5; i++) { objmenu.dispatchEvent(event); } }