讀javascript進階編程11-事件
事件流指從頁面中接收事件的順序。 1.事件冒泡(常用) IE中採用的事件流是事件冒泡,先從具體的接收元素,然後逐步向上傳播到不具體的元素。 Image 2.事件捕獲(少用) Netscapte採用事件捕獲,先由不具體的元素接收事件,最具體的節點最後才接收到事件。 Image(1) 3.DOM事件流 Image(2) 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");function show(){alert(this.innerHTML);}obj.addEventListener("click",show,false);obj.removeEventListener("click",show,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);//點擊運行結果 clickevent對象包含於建立它的事件對象相關的屬性和方法。幾種常用屬性和方法: ① 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); }}