事件是一種非同步編程的實現方式,本質上是程式各個組成部分之間的通信,DOM支持大量的事件; 本文通過這幾點向大家詳細解析事件處理的基本原理:事件類型、事件目標、事件處理程式、事件對象、事件傳播 最後再向大家介紹Event對象; (原創文章,轉摘請註明:蘇服:http://www.cnblogs.com ...
事件是一種非同步編程的實現方式,本質上是程式各個組成部分之間的通信,DOM支持大量的事件; 本文通過這幾點向大家詳細解析事件處理的基本原理:事件類型、事件目標、事件處理程式、事件對象、事件傳播 最後再向大家介紹Event對象; (原創文章,轉摘請註明:蘇服:http://www.cnblogs.com/susufufu/p/5768431.html) 一、事件類型(event type):是一個用來說明發生了什麼類型事件的全小寫的字元串,如‘mouseover’ 傳統事件類型:表單事件,Window事件,滑鼠事件,鍵盤事件,DOM事件, HTML5事件,觸摸屏和移動設備事件等,這裡是所有事件的詳細介紹:DOM事件類型詳解 二、事件目標(event target):觸發事件的對象 三、事件處理程式(或事件監聽程式)(event listener):處理或響應事件的函數。當某對象觸發某事件時,瀏覽器將自動調用在該對象上註冊的函數; 註冊事件處理程式(監聽事件): 1.作為HTML屬性註冊(只會在冒泡階段觸發)如<table id="t" onclick="modifyText();">;而某些事件類型通常直接在瀏覽器上觸發,而非任何特定文檔元素上觸發,把這些事件處理程式放在<body>標簽上,但瀏覽器會在Window對象上註冊它們,如<body onload="alert('Hello world!')">,這些事件有: onafterprint onfocus ononline onresize onbeforeprint onhashchange onpagehide onstorage onbeforeunload onload onpageshow onundo onblur onmessage onpopstate onunload onerror onoffline onredo 作為HTML屬性的事件的值是JS代碼字元串,是處理函數的主體,不含{},註意:儘量不要在任何其他HTML標簽上註冊事件,它違反了HTML與JavaScript代碼相分離的原則,倘若事件函數可能還沒載入進來就點擊了事件對象元素,這會導致錯誤; 2.作為DOM元素的屬性來註冊(只會在冒泡階段觸發),此時的事件處理程式屬性的名字需加‘on’首碼,這種方式相容所有瀏覽器,唯一的缺點是只能註冊一個事件處理函數,如果定義兩次onclick屬性,後一次定義會覆蓋前一次;如:window.onload = function(){...}; 3.除了IE8及之前版本外的所有瀏覽器中,DOM的事件操作(監聽和觸發),都定義在EventTarget介面。Element節點、document節點和window對象,都部署了這個介面。此外,XMLHttpRequest、AudioNode、AudioContext等瀏覽器內置對象,也部署了這個介面。該介面有三個方法,addEventListener和removeEventListener用於綁定和移除監聽函數,dispatchEvent用於觸發事件; addEventListener(type,listener,boolean)方法來註冊listener,第三個參數設置事件的傳播方式,通常使用預設值false,表示監聽函數只在冒泡階段(pupple)被觸發,當設為true時,表示監聽函數在捕獲階段(capture)觸發;可以為同一對象上的同一類型事件註冊任意多個listener,所有listener會按照註冊順序觸發(註冊重覆的listener將會被瀏覽器忽略); 如果希望向監聽函數傳遞參數,可以用匿名函數包裝一下監聽函數,如elm.addEventListener('click',function(){listen('實參')},false); 當註冊的listener是一個對函數的引用變數,就可以用removeEventLestener(type,listener,boolean)在事件目標上刪除該listener,對同一監聽事件的冒泡事件和捕獲事件需要分別刪除,兩者互不幹擾; var div = document.getElementById('div'); var listener = function (event) { /* do something here */ }; div.addEventListener('click', listener, false); div.removeEventListener('click', listener, false); dispatchEvent(event)方法在當前節點上手動觸髮指定事件,從而觸發監聽函數的執行。該方法返回一個布爾值,只要有一個監聽函數調用了Event.preventDefault(),就返回false,否則為true,參數是一個Event對象的實例,該參數不能為空,且必須是一個有效的事件對象,否則報錯; btn.addEventListener('click', listener, false); var e = new Event('click'); btn.dispatchEvent(e); //在btn上立即觸發click事件,將立即調用listener 下麵例子根據dispatchEvent方法的返回值,判斷事件是否被取消了 var canceled = !btn.dispatchEvent(event); if (canceled) { console.log('事件取消'); } else { console.log('事件未取消'); }} 4.IE8及之前版本僅支持attachEvent (type,listener)和detachEvent(type,listener),它們的用法和addEventListener的區別:a.參數只有兩個;b.參數type必須加'on'首碼;c.它允許對同一監聽事件進行重覆註冊,且都會被調用;d.使用attachEvent方法有個缺點,是this的值會變成 window 對象而不是觸發事件的元素; 調用順序問題:1.通過設置對象屬性或HTML屬性註冊的處理程式一直優先調用; 2.使用addEventListener 註冊的處理程式按照它們的註冊順序調用; 3.舊版IE中使用attachEvent註冊的處理程式可能按照任何順序調用。 返回值問題:1.事件處理程式的返回值只對通過屬性註冊的處理程式才有意義,通過設置對象屬性或HTML屬性註冊 事件處理程式的返回值為false,就是告訴瀏覽器不要執行這個事件相關的預設操作。當瀏覽器要跳轉到新頁面時觸發Window對象的onbeforeunload事件,若它的的返回值為字元串,則它將出現在詢問確認對話框中; 2.addEventListener()或attachEvent()註冊事件處理程式若要取消瀏覽器的預設操作必須調用preventDefault()方法或設置事件對象的returnValue屬性。 this指向問題:1.addEventListener方法指定的監聽函數,內部的this對象總是指向觸發事件的那個節點; 2.IE8及以前的attachEvent方法註冊的事件處理函數的this指向全局對象; 以下寫法的this對象都指向Element節點。 element.onclick = print; element.addEventListener('click', print, false) element.onclick = function () {console.log(this.id);} <element onclick="console.log(this.id)"> 以下寫法的this對象,都指向全局對象。 element.onclick = function (){ doSomething() }; element.setAttribute('onclick', 'doSomething()'); <element onclick="doSomething()"> element.attachEvent('onclick',doSomething) //IE8 記憶體問題:對如下代碼,每個迴圈中都會創建一個新的匿名函數,占用的記憶體越來越多;由於沒有保持到匿名函數的引用,它不可能被調用 removeEventListener;所以應當把第二參數listener保持為對處理事件函數的引用; for(i=0 ; i<els.length ; i++){ els[i].addEventListener("click", function(e){/*do something*/}, false}); } 通用的相容舊版IE的工具函數: 確保事件處理程式的this指向事件的目標對象的工具函數addEvent function addEvent(target,type,func){ if(target.addEventListener){ target.addEventListener(type,func,false); }else{ target.attachEvent('on'+type,function(e){ //這裡attachEvent註冊的處理函數未綁定引用,所以無法用detachEvent刪除 return func.call(target,e); }); } } //通用的事件處理程式(因為IE8及以前版本,作為事件目標的on-屬性的處理程式需要window.event來獲得事件對象,且觸發事件的目標節點對象通過event.srcElement屬性獲得) function func(event){ var event = event||window.event; var target = event.target || event.srcElement; //......處理程式代碼 } 四、事件傳播(event propagation):是瀏覽器決定哪個對象觸發其事件處理程式的過程。 “DOM2級事件”規定的事件流包括三個階段:事件捕獲階段==>處於目標階段==>事件冒泡階段。首先發生的是事件捕獲階段(從外層向內層傳播),為事件傳播經過的所有節點截獲事件提供了機會。然後是實際的目標接收事件(按註冊順序執行)。最後一個階段是冒泡階段(從內層向外層冒泡)。 當容器元素及嵌套元素,即在捕獲階段又在冒泡階段調用事件處理程式時:事件按DOM事件流的順序執行事件處理程式,且當事件處於目標階段時,事件調用順序決定於綁定事件的書寫順序 如果希望事件到某個節點為止,不再傳播,有兩種方式:1.使用事件對象的event.stopPropagation()方法來阻止當前監聽函數的傳播;2.使用事件對象的event.stopImmediatePropagation()方法來阻止當前事件在其事件對象上的所有監聽函數的傳播; 事件的代理(delegation):由於事件會在冒泡階段向上傳播到父節點,因此可以把子節點的監聽函數定義在父節點上,由父節點的監聽函數統一處理多個子元素的事件; 五、事件對象(Event):事件發生以後,會生成一個事件對象,作為參數傳給監聽函數。瀏覽器原生提供一個Event對象,所有的事件都是這個對象的實例,或者說繼承了Event.prototype對象。Event對象本身就是一個構造函數,可以用來生成新的實例。 var ev = new Event("look", {"bubbles":true, "cancelable":false}); document.dispatchEvent(ev); Event構造函數接受兩個參數。第一個參數是字元串,表示事件的名稱;第二個參數是一個對象,表示事件對象的配置。該參數可以有以下兩個屬性。 bubbles:布爾值,可選,預設為false,表示事件對象是否冒泡。 cancelable:布爾值,可選,預設為false,表示事件是否可以被取消。 Event對象的屬性: 1.與事件的階段有關: bubbles: 只讀屬性,返回一個布爾值,表示當前事件是否會冒泡,可根據事件是否會冒泡來調用不同的函數。 eventPhase:返回一個整數值(0,1,2,3之一),表示事件目前所處的狀態 0,事件目前沒有發生。 1,事件目前處於捕獲階段,即處於從祖先節點向目標節點的傳播過程中。該過程是從Window對象到Document節點,再到HTMLHtmlElement節點,直到目標節點的父節點為止。 2,事件到達目標節點,即target屬性指向的那個節點。 3,事件處於冒泡階段,即處於從目標節點向祖先節點的反向傳播過程中。該過程是從父節點一直到Window對象。只有bubbles屬性為true時,這個階段才可能發生 2.與事件的預設行為有關: cancelable:返回一個布爾值,表示事件是否可以取消。如果要取消某個事件,需要在這個事件上面調用preventDefault方法 defaultPrevented:返回一個布爾值,表示該事件是否調用過preventDefault方法。 3.與事件的目標節點有關: currentTarget:返回事件執行的監聽函數所綁定的那個節點。 target:返回觸發事件的那個節點。在IE6—IE8之中,該屬性的名字不是target,而是srcElement 4.與事件對象的其他信息相關: type:返回一個字元串,表示事件類型 detail:返回一個數值,表示事件的某種信息。具體含義與事件類型有關,對於滑鼠事件,表示滑鼠按鍵在某個位置按下的次數,比如對於dblclick事件,detail屬性的值總是2 timeStamp:返回一個毫秒時間戳,表示事件發生的時間。從PerformanceTiming.navigationStart開始計算,即表示距離用戶導航至該網頁的時間。如果想將這個值轉為Unix紀元時間戳,就要計算event.timeStamp + performance.timing.navigationStart isTrusted:返回一個布爾值,表示該事件是否可以信任。用處不大,不同瀏覽器的支持不一樣。 Event對象的方法: preventDefault():取消瀏覽器對當前事件的預設行為,該方法生效的前提是,事件的cancelable屬性為true,如果為false,則調用該方法沒有任何效果。 stopPropagation():終止事件在傳播過程的捕獲、目標處理或起泡階段進一步傳播。調用該方法後,該節點上處理該事件的處理程式將被調用,事件不再被分派到其他節點。註意:該方法不能阻止同一個 Document 節點上的其他事件句柄被調用,但是它可以阻止把事件分派到其他節點 stopImmediatePropagation():阻止同一個事件的其他監聽函數被調用,只要其中有一個監聽函數調用了該方法,其他的監聽函數就不會再執行了。 參考鏈接: http://javascript.ruanyifeng.com/dom/event.html#toc31 https://developer.mozilla.org/zh-CN/docs/Web/API JavaScript權威指南第六版