我的認爲:不管是瀏覽器自帶的事件,仍是自定義事件,都是觀察者模式的實現。更確切地說:事件流是會流動的,流到哪一個節點,事件在哪裏發生,事件發生時,節點便會調用在這個節點綁定的事件處理程序。節點是被觀察者,事件處理程序是觀察者,當事件流流到被觀察者時,被觀察者會對外宣稱「我這裏發生了某個事件」,即通知觀察者,也就是節點調用事件處理程序。事件流是不知道被觀察者有多少個的,因此即便是0個,事件流也會繼續流,流到節點時,節點會遍歷本身註冊的事件處理程序,存在就調用。具體瀏覽器的實現和優化確定更加複雜和精妙,但原理應該是這樣(以上爲我的理解)。javascript
事件流分爲事件冒泡和事件捕獲:css
- 若是你把手指放在圓心上,那麼你的手指指向的不是一個圓,而是紙上的全部圓。在瀏覽器上單擊按鈕的同時,你也單擊了按鈕的容器元素,甚至也單擊了整個頁面。事件流描述的是從頁面中接收事件的順序。
- IE開發團隊提出了事件冒泡流、Netscape開發團隊提出了事件捕獲流。
- 事件開始時由最具體的元素(文檔中嵌套層次最深的那個節點)接收,而後逐級向上傳播到較爲不具體的節點,全部現代瀏覽器都支持事件冒泡,除IE5.5外,均一直冒泡到window。
- 事件冒泡示意圖:
- 不太具體的節點應該更早接收到事件,而最具體的節點應該最後接收到事件。事件捕獲的用意在於在事件到達預約目標以前捕獲它。IE9+、Safari、Chrome、Opera和Firefox支持,且從window開始捕獲(儘管DOM2 級事件規範要求從document)。
- 事件捕獲示意圖:
- 因爲老版本的瀏覽器不支持,所以不多有人使用事件捕獲。咱們也建議讀者放心地使用事件冒泡,在有特殊須要時再使用事件捕獲。
爲了完全理解事件冒泡和捕獲,這裏寫了個例子:html
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <title>test1</title> <link rel="stylesheet" href="test1.css"> </head> <body> <div id="a"> <div id="b"> <div id="c"></div> </div> </div> <script src="test1.js"></script> </body> </html>
#a{ width: 300px; height: 300px; background: pink; } #b{ width: 200px; height: 200px; background: blue; } #c{ width: 100px; height: 100px; background: yellow; }
var a = document.getElementById("a"), b = document.getElementById("b"), c = document.getElementById("c"); c.addEventListener("click", function(event){ console.log("c1") // 注意第三個參數沒有傳進 false , 由於默認傳進來的是 false,表明冒泡階段調用,我的認爲處於目標階段也會調用的 }); c.addEventListener("click", function(event){ console.log("c2"); }, true); b.addEventListener("click", function(event){ console.log("b"); }, true); a.addEventListener("click", function(event){ console.log("a1"); }, true); a.addEventListener("click", function(event){ console.log("a2") }); a.addEventListener("click", function(event){ console.log("a3"); event.stopImmediatePropagation(); }, true); a.addEventListener("click", function(event){ console.log("a4"); }, true);
效果如圖
html5
註釋掉 event.stopImmediatePropagation,點擊 c,輸出 a一、a三、a四、b、c一、c二、a2java
另外,若是同一個事件處理程序(指針相同,好比用 handler 保存的事件處理程序),用 addEventListener 或 attachEvent 綁定屢次,若是第三個參數是相同的話,也只會被調用一次。但若是第三個參數一個設置爲true,另外一個設置爲false,那麼會被調用兩次。git
- 「DOM2級事件」規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。首先發生的是事件捕獲,爲截獲事件提供了機會。而後是實際的目標接收到事件。最後一個階段是冒泡階段。(事件處理中「處於目標階段」被當作冒泡階段的一部分)。
- IE九、Safari、Chrome、Firefox和Opera9.5及更高版本都會在捕獲階段觸發事件對象上的事件,就是有兩個機會在目標對象上面操做事件。(儘管DOM2級事件規範明確要求捕獲階段不涉及事件目標)。
簡單來說,HTML 事件處理程序是直接在HTML中綁定事件,以下github
<input type="button" value="Click Me" onclick="alert("Clicked")" />
&
、「」
、<
、>
,由於這是在HTML中綁定的,會形成瀏覽器解析DOM結構錯誤。擴展函數做用域,來看下面的代碼:web
<!-- 輸出 "Click Me、lzh" --> <form method="post"> <input type="text" name="username" value="lzh"> <input type="button" value="Click Me" onclick="alert(value);alert(username.value);"> </form>
若是當前元素是一個表單輸入元素,瀏覽器內部大概是這樣實現的:chrome
function () { with (document) { with (this.form) { with (this) { //元素屬性值 } } } }
form
元素,調用username
會報錯,因此不管是服務端渲染仍是Ajax請求回來數據再渲染,最好仍是把form結構寫完整。try{}catch(ex){}
,分離的寫法能夠在DOMContentLoaded以後再綁定。
- 每一個元素(包括
window
和document
)都有本身的事件處理程序屬性,這些屬性一般所有小寫。使用 DOM0 級指定的事件處理程序被認爲是元素的方法。this
引用當前元素。經過this
能夠訪問元素的任何屬性和方法。DOM0 級事件處理程序在冒泡階段被處理。
var btn = document.getElementById("myBtn"); btn.onclick = function () { alert(this.id); //"myBtn" };
addEventListener()
包含三個參數,要處理的事件名、事件處理函數、布爾值,布爾值爲true,表示在捕獲階段調用事件處理程序,反之在冒泡階段調用。- DOM2 級事件處理程序中的
this
也指向addEventListener
的那個元素。- 能夠添加多個事件處理程序,按添加順序依次調用。
removeEventListener
沒法移除匿名函數的事件處理程序。
var btn = document.getElementById("myBtn"); var handler = function () { alert(this.id); }; btn.addEventListener("click", handler, false); //這裏省略了其餘代碼 btn.removeEventListener("click", handler, false); // 有效!
- IE九、Firefox、Safari、Chrome 和Opera 支持DOM2 級事件處理程序。
attachEvent
detachEvent
接收兩個參數,事件處理程序名稱、事件處理程序函數。因爲IE8及更早版本只支持事件冒泡,因此該事件處理程序只支持事件冒泡。- 老版本的Opera支持這種方法,但如今Opera已經改用blink內核,IE11已經不支持這種方法,注意 IE9 就已經支持 DOM2 級事件處理程序了。
- 特別要注意:第一個參數包含on,好比onclick。
- 區別於DOM0 級事件處理程序,
this
指向 'window'。- 也能夠添加多個事件處理程序。
var EventUtil = { addHandler: function(element, type, handler){ if (element.addEventListener){ element.addEventListener(type, handler, false); } else if (element.attachEvent){ element.attachEvent("on" + type, handler); } else { element["on" + type] = handler; } }, removeHandler: function(element, type, handler){ if (element.removeEventListener){ element.removeEventListener(type, handler, false); } else if (element.detachEvent){ element.detachEvent("on" + type, handler); } else { element["on" + type] = null; } } };
- 存在問題:
- IE事件處理程序 中的
this
指向window
。- 只支持 DOM0 級的瀏覽器不能屢次添加事件處理程序,不過這種瀏覽器應該很少了,即便是IE8 也支持attachEvent。
- 會不會有一些事件,在瀏覽器支持 DOM2 級事件處理程序的狀況下,那些事件只能用
on + name
的形式呢? 以前一直懷疑 (1).xhr.onreadystatechange()
和 (2).DOMNodeInserted
事件,這裏我多慮了,通過驗證,(1).是支持 DOM2 級事件的,(2).天生就是 DOM2 級的。這裏只是爲了打消個人疑慮,記錄下來。
- 兼容 DOM 的瀏覽器會將一個
event
對象傳入事件處理程序, IE9 及更高版本能夠。不管指定事件處理程序時使用什麼方法(DOM0 級 DOM2 級),HTML 事件處理程序能夠經過訪問event
變量獲得event
對象。- event 中的屬性和方法都是隻讀的
- 經常使用屬性:
target
事件的目標currentTarget
綁定事件的元素,與 'this' 的指向相同stopPropagation()
取消事件的進一步捕獲或冒泡。若是bubbles爲true,則可使用這個方法stopImmediatePropagation()
取消事件的進一步捕獲或冒泡,同時阻止任何事件處理程序被調用(DOM3級事件中新增)preventDefault()
取消事件的默認行爲,好比點擊連接跳轉。若是cancelable
是true
,則可使用這個方法type
被觸發的事件類型eventPhase
調用事件處理程序的階段:1表示捕獲階段,2表示「處於目標」,3表示冒泡階段
this
target
currentTarget
舉例:
document.body.onclick = function(event){ alert(event.currentTarget === document.body); //true alert(this === document.body); //true alert(event.target === document.getElementById("myBtn")); //true };
- 經過
event.type
與switch case
組合,能夠經過一個函數處理多個事件。- 只有在事件處理程序執行期間,
event
對象纔會存在;一旦事件處理程序執行完成,event
對象就會被銷燬。
- DOM0 級的事件處理程序,
event
做爲window
的一個屬性存在。(從 IE9 開始,event 能夠從參數中得到)attachEvent
添加的事件處理程序,event
做爲參數傳入,也能夠經過window
來訪問event
對象。- HTML 事件處理程序依然能夠經過訪問
event
變量獲得event
對象。- 屬性和方法:
cancelBubble
設置true
orfalse
能夠取消事件冒泡returnValue
設置true
orfalse
能夠取消事件的默認行爲。srcElement
事件的目標(與DOM中的target
相同)
- 注意事項:
attachEvent
中的event.srcElement === this
嗎? 答案是否認的,由於前面說到過attachEvent
中this
指向window
, DOM0 級、DOM2 級 事件處理程序this
才指向event.target / window.event.srcElement
var EventUtil = { getEvent: function(event){ return event ? event : window.event; // window.event DOM0級時IE }, getTarget: function(event){ return event.target || event.srcElement; // event.srcElement for IE }, preventDefault: function(event){ if (event.preventDefault){ event.preventDefault(); } else { event.returnValue = false; // IE } }, stopPropagation: function(event){ if (event.stopPropagation){ event.stopPropagation(); } else { event.cancelBubble = true; // IE } } };
- DOM3 級事件規定了幾類事件;HTML5 也定義了一組事件;還有一些事件沒有規範,瀏覽器的實現不一致。
- DOM3 級事件模塊在 DOM2 級事件模塊基礎上從新定義了這些事件,也添加了一些新事件。包括 IE9 在內的全部主流瀏覽器都支持 DOM2 級事件。IE9 也支持 DOM3 級事件。
這裏只總結一些常見的事件類型數組
- load 事件,當頁面徹底加載後(包括全部圖像、JavaScript 文件、CSS 文件等外部資源),就會觸發
window
上面的 load 事件。
EventUtil.addHandler(window, "load", function(){ var image = document.createElement("img"); EventUtil.addHandler(image, "load", function(event){ event = EventUtil.getEvent(event); alert(EventUtil.getTarget(event).src); }); document.body.appendChild(image); image.src = "smile.gif"; //在此以前要先指定事件處理程序 });
- script 元素也會觸發 load 事件,據此能夠判斷動態加載的 JavaScript 文件是否加載完畢。與圖像不一樣,只有在設置了 script 元素的 src 屬性並將該元素添加到文檔後,纔會開始下載 JavaScript 文件
- IE8 及更早版本不支持 script 元素上的 load 事件。
- 在不屬於 DOM 文檔的圖像(包括未添加到文檔的 img 元素和 Image 對象)上觸發 load 事件時,IE8 及以前版本不會生成 event 對象。IE9 修復了這個問題。
- resize 事件
- 瀏覽器窗口大小發生變化時會觸發該事件,這個事件在
window
上觸發,IE、Safari、Chrome 和 Opera 會在瀏覽器窗口變化了 1 像素時就觸發 resize 事件,而後隨着變化不斷重複觸發。Firefox 則只會在用戶中止調整窗口大小時纔會觸發。- 注意不要在這個事件的處理程序中加入大計算量的代碼,或者採用函數節流的方式優化性能。
- 瀏覽器窗口最小化或最大化時也會觸發 resize 事件。
- scroll 事件
- 該事件在 window 上發生,此處和書上講的有點不同,webkit 內核或 blink 內核的瀏覽器(Chrome、Opera、Safari)能夠經過 document.body.scrollTop 獲取頁面被捲去的高度,而 Trident、Gecko (IE、火狐)能夠經過 document.documentElement.scrollTop來獲取該值。
- 另外標準模式、混雜模式這兩種方法還有出入,此處不討論。
- 因此最好經過
document.body.scrollTop + document.documentElement.scrollTop
的方式獲取 scrollTop 的值,由於二者之一會等於0,或者使用document.body.scrollTop || document.documentElement.scrollTop
,二者效果一致。
- 這裏忽略 DOMFocusIn、DOMFocusOut,由於只有 Opera 支持這個事件,且 DOM3 級事件廢棄了它們。
- blur:在元素失去焦點時觸發。這個事件不會冒泡;全部瀏覽器都支持它。
- focus:在元素得到焦點時觸發。這個事件不會冒泡;全部瀏覽器都支持它。
- focusin:與 focus 等價,但它冒泡。
- focusout:與 blur 等價,也冒泡。
- 支持 focusin、focusout 的瀏覽器有:IE5.5+、Safari 5.1+、Opera 11.5+和Chrome。但只支持 DOM2 級事件處理程序
- Firefox 不支持 focusin、focusout
- blur、focusout 的事件目標是失去焦點的元素;focus、focusin 的事件目標是得到焦點的元素
click
在用戶單擊住鼠標按鈕或按下回車鍵時觸發。 觸發順序 mousedown mouseup click,若是 mousedown、mouseup 其中之一被取消,就不會觸發 click 事件。dblclick
觸發順序 mousedown mouseup click mousedown mouseup click dblclick, 若是中間有事件被取消,dblclick 也不會被觸發mousedown
用戶按下了任意鼠標按鈕時觸發。mouseup
用戶釋放按鈕時觸發mouseenter
在鼠標光標從元素外部首次移動到元素範圍以內時觸發。不冒泡,並且在光標移動到後代元素上不會觸發。DOM2 級事件並無定義這個事,但 DOM3 級事件將它歸入了規範。IE、Firefox9+和Opera支持這個事件。mouseleave
在位於元素上方的鼠標光標移動到元素範圍以外時觸發。不冒泡,並且在光標移動到後代元素上不會觸發。DOM2 級事件並無定義這個事,但 DOM3 級事件將它歸入了規範。IE、Firefox9+ 和 Opera 支持這個事件。mouseover
在鼠標指針位於一個元素外部,而後用戶將其首次移入另外一個元素邊界以內時觸發。不能經過鍵盤觸發這個事件。mouseout
在鼠標指針位於一個元素上方,而後用戶將其移入另外一個元素時觸發。又移入的另外一個元素可能位於前一個元素的外部,也多是這個元素的子元素。不能經過鍵盤觸發這個事件。
<!DOCTYPE html> <html lang="zh-cn"> <head> <title>test1</title> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="test1.css"> </head> <body> <div class="mouseover"> <div class="sub-mouseover"> </div> </div> <div class="mouseenter"> <div class="sub-mouseenter"> </div> </div> <script src="test1.js"></script> </body> </html>
.wrap { width: 200px; height: 100px; } .mouseover { background: pink; } .mouseenter { margin-top: 30px; background: gray; } .sub-mouseover, .sub-mouseenter { width: 100px; height: 50px; background: #AE81FF; }
var div1 = document.querySelector(".mouseover"), div2 = document.querySelector(".mouseenter"); div1.addEventListener("mouseover", function(){ console.log("div1 mouseover"); }); div1.addEventListener("mouseout", function(){ console.log("div1 mouseout"); }) div2.addEventListener("mouseenter", function(){ console.log("div2 mouseenter"); }) div2.addEventListener("mouseleave", function(){ console.log("div2 mouseleave"); })
效果圖
鼠標由左側從上到下依次通過全部 div 的狀況,輸出 div1 mouseover
div1 mouseout
div1 mouseover
div1 mouseout
div2 mouseenter
div2 mouseleave
mousemove
當鼠標指針在元素內部移動時重複地觸發。不能經過鍵盤觸發這個事件。- 除了 mouseenter、mousedleave,全部鼠標事件都會冒泡,取消鼠標事件將會影響瀏覽器的默認行爲,也會影響其它事件,由於鼠標事件與其它事件是密不可分的。
- 關於
dblclick
IE8 及以前版本中的實現有一個小bug,所以在雙擊事件中,會跳過第二個mousedown 和click事件,其順序以下:mousedown
mouseup
click
mouseup
dblclick
,但仍是會觸發dblclick
事件- 客戶區座標位置:鼠標事件中的
event
都有clientX
clientY
屬性,表示在視口中客戶區的座標位置,這些值不包括頁面滾動的距離,所以這個位置並不表示鼠標在頁面上的位置:
- 頁面座標位置:pageX、pageY,這兩個屬性表示鼠標光標在頁面中的位置,在頁面沒有滾動的狀況下,pageX 和 pageY 的值與 clientX、clientY 的值相等。IE8 及更早版本不支持事件對象上的頁面座標,不過使用客戶區座標和滾動信息能夠計算出來。這時候須要用到document.body(混雜模式)或document.documentElement(標準模式)中的scrollLeft 和scrollTop 屬性。計算過程以下所示:
var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "click", function(event){ event = EventUtil.getEvent(event); var pageX = event.pageX, pageY = event.pageY; if (pageX === undefined){ pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft); } if (pageY === undefined){ pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop); } alert("Page coordinates: " + pageX + "," + pageY); });
- 屏幕座標位置:screenX、screenY
- 修改鍵 用戶按住Shift、Ctrl、Alt、Meta(Windows或Cmd,cmd(mac))時觸發鼠標事件,能夠在
event
中得到修改鍵。
var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "click", function(event){ event = EventUtil.getEvent(event); var keys = new Array(); if (event.shiftKey){ keys.push("shift"); } if (event.ctrlKey){ keys.push("ctrl"); } if (event.altKey){ keys.push("alt"); } if (event.metaKey){ keys.push("meta"); } alert("Keys: " + keys.join(",")); });
- IE九、Firefox、Safari、Chrome 和Opera 都支持這4 個鍵。IE8 及以前版本不支持metaKey 屬性。另外,舊版本的 IE 有本身的一套寫法。
- 相關元素
mouseover
mouseout
時的 event.relatedTarget,不作詳細記錄。- 鼠標按鈕
mousedown
mouseup
是在按下/釋聽任意鼠標按鈕時觸發的,因此經過 event.button: 0(左) 1(中) 2(右) 能夠判斷按的是哪一個鍵,可是IE8 及更低版本的瀏覽器不支持,有兼容寫法,此處不詳細敘述。EventUtil.getButton
有詳細實現。- mousewheel
event.whellDelta
爲正數時,向前滾動(回到頂部、頁面向下滑動),負數則反過來,這個值是120的倍數,Opera低版本中正負相反,火狐中有本身的一套方法,這裏不作詳細記錄。- 觸摸設備
- 不支持dblclick 事件。雙擊瀏覽器窗口會放大畫面,並且沒有辦法改變該行爲。
- 輕擊可單擊元素會觸發mousemove 事件。若是此操做會致使內容變化,將再也不有其餘事件發生;若是屏幕沒有所以變化,那麼會依次發生mousedown、mouseup 和click 事件。輕擊不可單擊的元素不會觸發任何事件。可單擊的元素是指那些單擊可產生默認操做的元素(如連接),或者那些已經被指定了onclick 事件處理程序的元素。
- mousemove 事件也會觸發mouseover 和mouseout 事件。
- 兩個手指放在屏幕上且頁面隨手指移動而滾動時會觸發mousewheel 和scroll 事件。
- 無障礙性問題
- 若是須要考慮這個問題,不建議使用
click
以外的鼠標事件。由於這個不能經過鍵盤觸發,不利於屏幕閱讀器訪問。此處不詳細記錄。
keydown
: 當用戶按下鍵盤上的任意鍵時觸發,並且若是按住不放的話,會重複觸發此事件。keypress
當用戶按下鍵盤上的字符鍵時觸發,並且若是按住不放的話,會重複觸發此事件。按下Esc 鍵也會觸發這個事件。Safari 3.1 以前的版本也會在用戶按下非字符鍵時觸發keypress事件。keyup
:當用戶釋放鍵盤上的鍵時觸發。- 觸發順序:
keydown
、keypress
、keyup
,keydown
、keypress
都是在文本框發生變化以前被觸發的;keyup
事件則是在文本框已經發生變化以後被觸發的。- 若是用戶按下了一個字符鍵不放,就會重複觸發 keydown 和keypress 事件,直到用戶鬆開該鍵爲止。
- 鍵盤事件也支持修改鍵(ctrl等)
- keydown、keyup 中的 event 有 keyCode, 與ASCII 碼中對應小寫字母或數字的編碼相同。
- keypress 中的 event 有 charCode,這個值是按下的那個鍵所表明字符的 ASCII 編碼,用
String.fromCharCode()
能夠轉換成實際的字符- DOM3 級中,有
key
和char
,其中key
能夠直接獲得 "k"、"K"、"Shift" 等,char
屬性在按下字符鍵時行爲與key
相同,在按下非字符鍵時爲null
,可是支持還不完整,chrome 老是輸出 undefined。keyIdentifier
Chrome 已經不推薦使用- 表示按下的按鍵在鍵盤的位置,好比按下左右側的shift鍵,這個值就不一樣,Chrome 和 Safari 的實現有 bug。
textInput
: 在文本插入文本框以前會觸發textInput 事件。目的是代替keypress,退格鍵不會觸發textInput,可是會觸發keypress(只要改變文本),只有真正能夠編輯的區域纔會觸發textInput,可是keypress得到焦點便可觸發。event.data中包含用戶的輸入,拼音輸入法中輸入過程的拼音不會觸發該事件。- inputMethod 表明用戶是怎樣輸入的,好比經過粘貼的方式,可是支持的瀏覽器不多。
DOM2 級的變更(mutation)事件能在 DOM 中的某一部分發生變化時給出提示,好比 DOM 節點的插入、移除、特性被修改等等
- contextmenu 事件
EventUtil.addHandler(window, "load", function(event){ var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "contextmenu", function(event){ event = EventUtil.getEvent(event); EventUtil.preventDefault(event); var menu = document.getElementById("myMenu"); menu.style.left = event.clientX + "px"; menu.style.top = event.clientY + "px"; menu.style.visibility = "visible"; }); EventUtil.addHandler(document, "click", function(event){ document.getElementById("myMenu").style.visibility = "hidden"; }); });
- beforeunload 事件,用戶關閉標籤頁時提示
EventUtil.addHandler(window, "beforeunload", function(event){ event = EventUtil.getEvent(event); var message = "I'm really going to miss you if you go."; event.returnValue = message; return message; });
- DOMContentLoaded 在造成完整DOM樹以後就會觸發,不理會圖像、JavaScript 文件、CSS 文件或其它資源是否已經下載完畢。其實更應該使用 DOMContentLoaded 而不是 window.onload:
EventUtil.addHandler(window, "DOMContentLoaded", function(event){ alert("Content loaded."); }); EventUtil.addHandler(window, "load", function(event){ alert("Window loaded."); });
- IE9+、Firefox、Chrome、Safari 3.1+ 和 Opera9+ 都支持 DOMContentLoaded 事件。
- readystatechange 事件,略。
- pageshow 和 pagehide 事件,此處要了解 Firefox 和 Opera 有一個特性叫 「往返緩存」(back-forward cache/bfcache),用戶點擊「前進」、「後退」按鈕時,會將頁面緩存在內存。不從新加載,JavaScript的狀態會保留。可是不管頁面是否來自 bfcache,都會觸發 pageshow 事件,pageshow 的事件處理程序的 event 對象中有
event.persisted
屬性,爲true
表明頁面來自bfcache,一樣 pagehide 事件觸發時,若是頁面被保存到 bfcache 中,則該屬性爲 true。支持pageshow、pagehide 事件的瀏覽器有 Firefox、Safari5+、Chrome 和 Opera。 IE9 及之前的版本不支持這兩個事件。指定了 onunload 事件處理程序的頁面會被自動排除在 bfcache 以外。- hashchange 事件。在 window 上觸發,event 包含 oldURL、newURL 兩個屬性。支持該事件的有 IE8+、Firefox3.6+、Safari5+、Chrome 和 Opera10.6+,但oldURL、newURL只有Firefox6+、Chrome和Opera支持。因此最好用 location 來指定當前的 hash:
EventUtil.addHandler(window, "hashchange", function(event){ console.log(location.hash); })
- orientationchange 事件,屏幕轉動。
- touchstart: 當手指觸摸屏幕時觸發;即便已經有一個手指放在了屏幕上也會觸發。
- touchmove: 當手指在屏幕上滑動時連續地觸發。在這個事件發生期間,調用preventDefault() 能夠阻止滾動。
- touchend:當手指從屏幕上移開時觸發。
- touchcancel:當系統中止跟蹤觸摸時觸發。關於此事件的確切觸發時間,文檔中沒有明確說明。
- event 對象中包含的常見 DOM 屬性有:bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、ctrlKey 和metaKey。
- event 對象中還包含如下用於跟蹤觸摸的屬性:
- touches:表示當前跟蹤的觸摸操做的Touch 對象的數組。
- targetTouchs:特定於事件目標的Touch 對象的數組。
- changeTouches:表示自上次觸摸以來發生了什麼改變的Touch 對象的數組。每一個Touch 對象包含下列屬性:clientX、clientY、pageX、pageY、screenX、screenY、target、identifier(標識觸摸的惟一ID)
function handleTouchEvent(event) { //only for one touch if (event.touches.length == 1) { var output = document.getElementById("output"); switch (event.type) { case "touchstart": output.innerHTML = "Touch started (" + event.touches[0].clientX + "," + event.touches[0].clientY + ")"; break; case "touchend": output.innerHTML += "<br>Touch ended (" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")"; break; case "touchmove": event.preventDefault(); //prevent scrolling output.innerHTML += "<br>Touch moved (" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")"; break; } } }
- 一次觸摸的事件觸發順序爲:touchstart、mouseover、mousemove(一次)、mousedown、mouseup、click、touchend
- 手勢事件:
- gesturestart:當一個手指已經按在屏幕上而另外一個手指又觸摸屏幕時觸發。
- gesturechange:當觸摸屏幕的任何一個手指的位置發生變化時觸發。
- gestureend:當任何一個手指從屏幕上面移開時觸發。
- 屬性有標準的鼠標事件屬性,還有兩個:rotation(正值表示順時針)和scale(從1開始)
- 每一個函數都是對象,都會佔用內存;內存中的對象越多,性能就越差。
- 必須事先指定全部事件處理程序而致使的 DOM 訪問次數,會延遲整個頁面的交互就緒時間。
<body> <ul id="myLinks"> <li id="goSomewhere">Go somewhere</li> <li id="doSomething">Do something</li> <li id="sayHi">Say hi</li> </ul> <script type="text/javascript"> (function(){ var list = document.getElementById("myLinks"); EventUtil.addHandler(list, "click", function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(target.id){ case "doSomething": document.title = "I changed the document's title"; break; case "goSomewhere": location.href = "http://www.wrox.com"; break; case "sayHi": alert("hi"); break; } }); })(); </script> </body>
- 上面的方法只取得了一個 DOM 元素,只添加了一個事件處理程序,佔用的內存更少。
- 若是將事件委託到 document 中,會更有優點:
- document 對象很快就能夠訪問,並且能夠在頁面生命週期的任什麼時候點上爲它添加事件處理程序(無需等待 DOMContentLoaded 或 load 事件)。
- 在頁面中設置事件處理程序所需的時間少。只添加一個事件處理程序所需的 DOM 引用更少,所花的時間也更少。
- 整個頁面佔用的內存空間更少,可以提高總體性能。
- 最適合採用事件委託技術的事件包塊
click
、mousedown
、mouseup
、keydown
、keyup
和keypress
- 若是你知道某個元素即將被移除,那麼最好手工移除事件處理程序,由於有的瀏覽器(尤爲是 IE)不會做出恰當地處理,它們頗有可能會將對元素和對事件處理程序的引用都保存在內存中。
- IE8 及更早的版本在頁面被卸載(刷新,切換頁面)以前沒有清理乾淨事件處理程序,它們會滯留在內存中,能夠經過 onunload 事件處理程序移除全部事件處理程序。
- 在測試 Web 應用程序,模擬觸發事件是一種極其有用的技術。DOM2 級規範爲此規定了模擬特定事件的方式,IE九、Opera、Firefox、Chrome 和 Safari 都支持這種方式。IE有它本身模擬事件的方式(IE8 及如下才要用到)
- 能夠在 document 對象上使用 createEvent 方法建立 event 對象。這個方法接收一個參數,即表示要建立的事件類型的字符串。在 DOM2 級中,全部這些字符串都使用英文複數形式,而在 DOM3 級中變成了單數。這個字符串能夠是下列幾個字符串之一:
- UIEvents,DOM3 級中是 UIEvent
- MouseEvents: 通常化的鼠標事件,DOM3 級中是 MouseEvent
- MutationEvents: 通常化的 DOM 變更事件。 ...
- HTMLEvents 通常化的 HTML 事件。沒有對應的 DOM3 級事件(HTML 事件被分割到其餘類別中)
- createEvent 方法返回的 event 對象中,有 initMouseEvent() 方法,須要傳 15 個參數。type(好比"click"),bubbles(Boolean) 是否冒泡,應該設置爲 true, cancelable(Boolean) 應該設置爲 true,view(幾乎老是document.defaultView), detail(一般設置爲0), screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button(表示按下了哪一個鼠標,默認0), relatedTarget(只有在模擬 mouseover 或 mouseout時使用)
- 將 event 對象傳給 DOM 節點的 dispatchEvent 方法便可觸發事件,以下:
<body> <input type="button" value="Click me" id="myBtn" /> <input type="button" value="Send click to the other button" id="myBtn2" /> <p>This example works in DOM-compliant browsers (not IE).</p> <script type="text/javascript"> (function(){ var btn = document.getElementById("myBtn"); var btn2 = document.getElementById("myBtn2"); EventUtil.addHandler(btn, "click", function(event){ alert("Clicked!"); alert(event.screenX); //100 }); EventUtil.addHandler(btn2, "click", function(event){ //create event object var event = document.createEvent("MouseEvents"); //initialize the event object event.initMouseEvent("click", true, true, document.defaultView, 0, 100, 0, 0, 0, false,