事件
- 了解事件流
- 使用事件處理程式
- 不同的事件類型
JS 與 HTML 之間互動便是通過事件實作的。事件就是文檔或者浏覽器視窗中發生的一些特定的互動瞬間。可以使用監聽器來預定事件,以便事件發生的時候執行相應的代碼。
1. 事件流
事件流描述的是從頁面中接收時間的順序,IE的為冒泡流,而Netscape為事件捕獲流。
1.1 事件冒泡
那麼事件流的順序按照冒泡為
div -> body -> html -> Document
需要注意的是 隻有 IE9, FF, Chrome 和 Safari 會一直冒泡到 window
1.2 事件捕獲
事件捕獲則是一個相反的流程
Document ->html -> body -> div
依次從上到下傳遞事件流。、
1.3 DOM 事件流
DOM2級事件 中規定事件流包括3各階段:事件捕獲,處理目标和事件冒泡。
對于IE,直到IE9才支援該DOM2級事件。
2. 事件處理程式
事件就是使用者或者浏覽器自身執行某種動作,響應某個事件的函數就稱之為事件處理程式。
2.1 HTML事件處理程式
該操作通過指定 onclick 特性并将一些 JS 代碼作為它的值來進行定義。
需要注意的地方就是 HTML事件中 this 的指向問題
如上的 input 代碼 當 onclick="alert(this.value)",沒有問題會彈出 "Click Me" 的結果。
但是一旦 onclick="showValue()"再定義
function showValue(){
alert(this.value);
}
結果為
undefined。 因為這裡的this 已經指向到
window,如果需要将函數定義在外部全局中,并且需要使用到所點選的節點DOM,那麼就在HTML的JS代碼中傳入 this
即可,onclick="showValue(this)",在處理函數。
2.2 DOM 0級事件處理程式
通過JS指定事件處理程式傳統方式就是将函數賦給一個事件處理程式屬性。簡單又有跨浏覽器優勢。
函數内部的 this 會指向目前元素
删除事件隻需要将事件屬性置為 null 即可。
2.3 DOM 2級事件處理程式
DOM 2級事件 定義了2個方法, addEventListener 和 removeEventListener 。參數有三個, 處理的事件名,事件函數以及是否是捕獲階段的布爾值。
在此的事件函數是使用匿名函數的方式傳入,是以就不能在使用 removeEventListener 來解除綁定了,因為就算傳入了一樣的匿名函數,也是指向不同的函數,是以希望使用 remove 的,必須在add事件的時候使用變量引用傳入。
還有就是對于一個元素的一個屬性上可以綁定多個事件,并且是按照綁定的順序執行的。addEventListener添加的事件隻能使用 removeEventListener 來解綁,不能使用 0級事件處理程式的方式來解除事件綁定。
(IE9, FF,Safari, Chrome, Opera 支援 DOM2級)
2.4 IE事件處理程式
IE實作的與DOM中類似的兩個方法 attachEvent 和 detachEvent 接受兩個參數 事件處理程式名稱 和 事件處理程式函數。 其第一個參數為 on 開頭,如 onclick,onsubmit等。
在IE中使用attachEvent與使用DOM0級方法最大的差別是事件處理程式的作用域。DOM0級中事件處理程式會在所屬元素的作用域内運作,但是在 attachEvent 方法下,事件處理程式會在全局作用于下運作,this會指向于window。
在對于編寫跨浏覽器的代碼時,記住這點很重要。
還有一個與标準的DOM2級不同的是,在一個元素添加多個事件處理程式的時候,addEventListener是按照綁定的順序先綁定的先執行原則觸發
事件處理程式的,但是 attachEvent 是先執行最近綁定的事件的,即執行順序上與 addEventListener 相反。
在點選後,先彈出 “Helloworld!”,而後 “Clicked”。
2.5 跨浏覽器的事件處理程式
這邊隻是簡單的提供了一個相容方案,當然沒有很細緻的考慮其他的比如IE中事件處理程式的作用域問題,以及綁定多個事件的執行順序問題。
3. 事件對象
在觸發DOM上的某個事件的時候,會産生一個事件對象event,該對象中包含了所有與事件有關的資訊。例如滑鼠點選時刻滑鼠的位置,鍵盤按下有關于鍵的資訊等。
3.1 DOM中的事件對象
相容DOM的浏覽器不管使用 DOM0級還是DOM2級都會将 event 事件對象傳入。
event 對象包含與建立他的特定時間有關的屬性和方法,觸發的事件類型不一樣可用的屬性和方法也不一樣,不過所有的又會有以下屬性:
在事件處理程式内部,this 始終指向 currentTarget的值, 對于 target 隻包含事件的實際目标,當直接将事件處理程式指定給目标元素,那麼三個值就是相等的。
如下,将事件處理程式綁定在 body上
也可以看做提高效率的 事件委托 的雛形。
event 事件對象中 preventDefault() 來取消預設行為,比如 a 标簽的跳轉行為等,stopPropagation() 方法使得事件停止在DOM中的傳播,取消進一步的事件捕獲或者冒泡。
注意,隻有在事件處理程式執行期間event事件對象才會存在,一旦事件結束,event對象就會銷毀。
3.2 IE中的事件對象
之前所說的均是标準DOM下的方式, IE對于事件對象有一定的不同,但是總體上是一緻的。在DOM 0級下,event對象作為window的一個屬性存在
event并不是直接存在于事件處理程式之中的,而需要擷取到 window.event 再進行使用。在attenchEvent中,event對象就會被作為參數傳入事件處理程式之中。
IE的event同樣也會包含一些屬性和方法
因為事件處理程式的作用域是更具指定他的方式來确定的,所有不能認為this會始終等于時間目标,使用 event.srcElement 比較保險。
3.3 跨浏覽器的事件對象
雖然 DOM 與 IE 下event對象有所不同,但是基于相似性還是可以實作跨浏覽器的解決方案來的。
4. 事件類型
DOM3級規定了以下
UI事件,當使用者與頁面上的元素互動的時候觸發。
焦點事件,當元素獲得或者失去焦點的時候觸發。
滑鼠事件,當使用者通過滑鼠在頁面上執行操作的時候觸發。
滾輪事件,當使用者滑鼠滾輪時觸發。
文本事件,當文檔中輸入文本的時候觸發。
鍵盤事件。
合成事件,當為IME 輸入法編輯器 輸入字元的時候觸發。
變動事件,當低層的DOM結構發生變化的時候觸發。
DOM3級事件子產品在DOM2級事件子產品的基礎上重新定義了這些事件,也添加了新的事件,IE9在内主流浏覽器均已支援DOM2級事件,IE9也支援部分DOM3事件。
4.1 UI事件
DOMActivate:表示元素已經被使用者操作過(DOM3中已經廢棄)。
load:當頁面完全加載後在window上面觸發,當所有的架構都加載完畢的時候在架構集上觸發,當圖像加載完畢的時候在
上觸發,以及嵌入的。
unload:頁面完全解除安裝的時候在window上面觸發。。。
abort:使用者停止下載下傳過程,内容還沒有加載完畢的時候。
error:當JS在window上發生錯誤的時候,圖像無法加載的時候等。
select:當使用者選着文本框中的一個或者多姿字元時候觸發。
resize:視窗或者架構大小變化的時候觸發。
scroll:使用者滾動帶滾動條的内容的時候觸發。
需要注意的是,resize和scroll事件會在我們調整視窗大小或者滾動的時候重複被觸發,是以應該盡量保持事件處理程式的代碼簡單,或者做好邏輯處理。
4.2 焦點事件
blur:當元素失去焦點的時候觸發,事件不冒泡,各個浏覽器相容。
focus:當元素獲得焦點的時候觸發,事件不冒泡,各個浏覽器相容。
focusin:當元素獲得焦點的時候觸發,事件冒泡,各個浏覽器相容(IE5.5+ SF5.1+ Op 11.5+ chrome)。
focusout:當元素失去焦點的時候觸發,事件冒泡,各個浏覽器相容(IE5.5+ SF5.1+ Op 11.5+ chrome)。
那麼當焦點從一個元素移動到另外一個元素的時候會依次觸發
1. focusout 在失去焦點的元素上觸發
2. focusin 在獲得焦點的元素上觸發
3. blue 在失去焦點的元素上觸發
4. focusout 在獲得焦點的元素上觸發
( Opera 上 還有DOMFocusIn 和 DOMFocusOut 事件 )
4.3 滑鼠與滾輪事件
click: 使用者單擊滑鼠按鈕時,或者按下Enter鍵的時候。
dblclick: 使用者輕按兩下滑鼠時候觸發。知道DOM3才将其納入标準。
mousedown:使用者按下任意滑鼠按鈕,不可以通過鍵盤觸發。
mouseup:使用者釋放任意滑鼠按鈕,不可以通過鍵盤觸發。
mouseenter:在滑鼠光标從元素外部移到元素範圍内的時候觸發,事件不冒泡,而且在移動到後代元素上的時候不觸發,DOM未定義該事件,DOM3納入規範(IE,FF 9+,Opera等均支援)。
mouseleave:在滑鼠光标從元素内部移到元素範圍外的時候觸發,事件不冒泡,而且在移動到後代元素上的時候不觸發,DOM未定義該事件,DOM3納入規範(IE,FF 9+,Opera等均支援)。
mousemove:當滑鼠在元素内部移動的時候重複的觸發。不能通過鍵盤觸發。
mouseout:滑鼠在一個元素上方,然後首次移入到另外一個元素的時候觸發。
mouseover:滑鼠在一個元素外部,然後首次移入到該元素上方的時候觸發。
在輕按兩下滑鼠的時候
mousedown -> mouseup -> click -> mousedown -> mouseup -> click ->dblclick
在對于IE8之前的版本來說會有一個小BUG, 輕按兩下時候會跳過第二個 mousedown 和 click。
檢測是否支援DOM2 DOM3級
document.implementation.hasFeature("MouseEvents", "2.0");
document.implementation.hasFeature("MouseEvent", "3.0");
還有一個跟蹤滑鼠滾輪的事件 mousewheel 。
1. 客戶區坐标位置
滑鼠事件都是在浏覽器視口中特定位子發生的,位子資訊儲存在事件對象的 clientX 與 clientY 屬性中。
可以使用下面代碼擷取
該位置為滑鼠相對于用戶端視口的位子,不包括頁面滾動的距離,并不表示滑鼠在頁面上的位置。
2. 頁面坐标位置
頁面位置通過事件對象的 pageX 和 pageY 屬性來擷取,此坐标為頁面本身而非視口的左邊與頂邊計算的。
是以在頁面沒有滾動的情況下,clientX,clientY 與 pageX,pageY的值是相等的。
IE8以及早期的版本不支援事件對象的頁面坐标,不過可以通過客戶區坐标和滾動資訊計算出來,需要使用 scrollLeft和scrollTop。
3. 螢幕坐标位置
還有一個相對于螢幕的位子坐标資訊 screenX,screenY。
代碼如下:
4. 修改鍵
滑鼠事件主要是由滑鼠來觸發的,但是也有可能按下了其他鍵。 狀态為 shiftKey,ctrlKey,altKey和metaKey。
IE8及之前版本不支援。
5. 相關元素
相關元素指的是滑鼠在發生 mouseover 和 mouseout 的時候還會涉及到其他元素。
DOM通過event 的 relatedTarget 屬性提供相關元素的資訊,隻對 mouseover 和 mouseout 事件才包含,對于其他事件均為 null。
IE8之前版本沒有relatedTarget 屬性,可以使用fromElement 和 toElement 來擷取相等意義的元素。
6. 滑鼠按鈕
DOM的button 屬性
0:主滑鼠鍵; 1:滑鼠中間鍵或者滾輪鍵; 2:次滑鼠鍵。
IE8及以前浏覽器
7. 更多的事件資訊
DOM2級事件規範還在event對象中提供了 detail 屬性,用于給出更多的事件資訊。
對于滑鼠事件來說,detail中包含了一個數值表示在給定的位子上發生了多少次的點選。
8. 滑鼠滾輪事件
從IE6起,就支援 mousewheel 事件,當使用者通過滑鼠滾輪與頁面互動,在垂直方向上滾動頁面的時候都會觸發 mousewheel
事件。在滾動的時候還包含一個特殊的 wheelDelta 屬性,記錄的滾動的量值,為120的倍數向前為正,向後為負。Firefox 支援一個名為
DOMMouseScroll的類似事件,資訊儲存在detail屬性中,量值為3的倍數。向前為負,向後為正。
9. 觸摸裝置
由于iOS, 安卓等裝置較為特殊,沒有滑鼠是以
1. 不支援 dbclick,輕按兩下放大畫面,無法改變該行為。
2. 輕擊可單擊元素會觸發 mouseover 事件,如果此操作會導緻内容變化将不再有其他事件發生。如果沒有變化 一次會觸發 mousedown mouseup 和 click。
3. mousemove 也會觸發 mouseover 和 mouseout 事件。
4. 兩個手指在螢幕上且頁面随手指滾動的時候也會觸發 mousewheel 和 scroll 事件。
10. 無障礙性問題
4.4 鍵盤與文本事件
DOM3級事件 為鍵盤事件制訂了規範
隻有一個文本事件 textInput,是對keypress 的補充,使得将文本展現給使用者之前可以攔截文本,在文本插入文本框之前就會觸發 textInput。
1. 鍵碼
keydown 和 keyup 的事件 event 對象的 keyCode 屬性中包含一個代碼,來顯示所按下的。
2. 字元編碼
IE9 以及其他主流浏覽器的 event 對象都支援一個 charCode,代表按下鍵的 ASCII編碼。
3. DOM3級變化
DOM3的鍵盤事件不再包含 charCode 屬性, 而是分開為 key 和 char 的兩個新屬性。
4. textInput 事件
當使用者在可編輯區域中輸入字元的時候就會觸發該事件,任何可獲得焦點的元素都可以觸發 keypress,而是由可編輯區域才可以觸發 textInput。
4.5 複合事件
是DOM3中的新添加的一類事件用于處理 IME 輸入序列。可以讓使用者輸入鍵盤上沒有的符号。
4.6 變動事件
DOM2級變動事件能在DOM的某一部分發生變化的時候來給出提示。
對于浏覽器支援情況來說
1. 删除節點
在使用removeChild() 或者 replaceChild() 從DOM中删除節點的時候首先會觸發 DOMNodeRemoved事件。該事件的 event.target 就是被删除的節點。
而 event.relatedNode則是對目标節點父節點的引用。
在上面HTML中,我們删除 ul 元素,就會依次:
1. 在 ul 上觸發 DOMNodeRemoved 事件,relatedNode 屬性等于 document.body。
2. 在 ul 上觸發 DOMNodeRemovedFromDocument 事件。
3. 在 ul 下的 每一個 li 上 觸發DOMNodeRemovedFromDocument 事件。
4. 在 document.body 上觸發 DOMSubtreeModified 事件。
驗證代碼:
2. 插入節點
在使用appendChild,replaceChild 和 insertBefore 向DOM中插入節點的時候首先會觸發 DOMNodeInserted 事件。
首先觸發的是 DOMNodeInserted 事件,該事件的目标是被插入的節點,相同的 event.relatedNode 屬性中包含的是對父節點的引用。并且該事件是冒泡的。
其次為在新插入的節點上觸發 DOMNodeInsertedIntoDocument 事件,該事件不冒泡。事件的目标為被插入的節點,是以需要在該節點插入之給節點添加好事件處理程式。
4.7 HTML 5 事件
DOM規範沒有涵蓋所有的浏覽器支援的事件許多浏覽器推出了自己的特殊事件。HTML5詳盡的列出了浏覽器應該支援的所有事件。
1. contextmenu 事件
為了實作上下文菜單,windows下是滑鼠右鍵單擊的菜單,如何操作該事件,于是就有了contextmenu 事件,用以表示何時該顯示上下文菜單,以便開發人員自定義上下文菜單。
JS 實作自定義上下文菜單
基本浏覽器均已支援。
2. beforeunload 事件
window對象上的事件處理函數, 是為了讓開發人員有可能在頁面解除安裝之前來阻止該操作。
除了 opera11之前版本,其餘均以支援。
3. DOMContentLoaded 事件
window的load事件會在頁面中的一切都加載完畢之後觸發,而DOMContentLoaded 事件則在完整的DOM數之後就觸發,不會受到圖檔,JS檔案,css檔案等影響,意味着客戶可以更早的與頁面互動。
支援的浏覽器有 IE9+ FF Chrome SF Op9+ 。
對于不支援的浏覽器建議頁面加載期間設定一個事件為 0ms 的setTimeout 來進行模拟處理。即在目前JS處理完畢後立即運作該函數。
4. readystatechange 事件
IE 為 DOM 文檔中的某些部分提供了 readystatechange 事件,目的是為了提供與文檔或者元素的加載狀态相關的資訊,支援readystatechange 的每個對象還有一個 readyState 屬性:
1. uninitialized 未初始化 :對象存在但是尚未初始化
2. loading 正在加載 : 對象正在加載資料
3. loaded 加載完畢 :對象加載資料完成
4. interactive 互動 : 可以操作對象但是還沒有完全加載
5. complete 完成 : 對象已經加載完畢
狀态很直覺,但是并非所有的對象都會經曆 readyState 的這些階段。
對于 document 來說, 值為interactive 的readyState 會與DOMContentLoaded 大緻相同,但是不能保證先後順序。
支援readystatechange的有 IE 、Firefox 4+ 與 Opera。
還有一個需要注意的是
我們要檢測加載完畢的時候需要一并檢測 readyState的兩個狀态,并且在事件調用了一次之後便移除該事件。因為有些對象會值出現某一個狀态,而有些會出現兩個狀态。
5. pageshow 和 pagehide 事件
頁面顯示,解除安裝的時候觸發。有時當我們使用頁面的前進後退按鈕,會調用頁面的緩存而不會觸發頁面的load,而 pageshow 則不會影響。其對象為 window。而pagehide則與 beforeunload類似。
相容的浏覽器 Firefox,Safari 5+,chrome 和 opera。IE9及之前版本均為支援。
6. hashchange
HTML5
新增的 hashchange 事件,再URL的參數清單(url中
#後部分)發生變化的時候觸發通知開發人員。主要用于在ajax應用中開發人員經常需要利用
url參數清單來儲存狀态或者導航資訊。當然也是需要将該事件處理程式添加給 window 對象。
event對象中會有兩個屬性 oldURL 和 newURL,儲存了變化前後各自的URL。可以使用以下代碼來檢測
var
isSupported = ( "onhashchange" in window ) && (
document.documentMode === undefined || document.documentmode > 7 );
4.8 裝置事件
裝置事件 device event 可以讓開發人員确定使用者在怎樣的使用裝置,W3C從2011年開始就在制定關于裝置時間的新草案,以涵蓋不斷增長的裝置類型并且定義相關事件。
1. orientationchange 事件
蘋果公司為一定 safari 添加orientationchange 事件,友善開發人員對于橫向與豎向檢視模式的處理。
window.orientation 屬性中包含3個值,0 表示肖像模式,90 代表左旋轉橫向模式 -90代表右旋轉橫向模式,文檔中還有 180 代表倒置,但還未支援。
隻要裝置更改了檢視模式就會觸發 orientationchange 事件,event中不包含任何有價值的資訊,唯一資訊可以通過 window.orientation 通路到。
2. MozOrientation 事件
Firefox 3.6 為檢測裝置的方向引入了該事件。與iSO的 orientationchange 事件不同,該事件隻能提供一個平面方向的變化。
event
對象中包含3個值, 想x,y,z處于 1 至 -1 之間,豎直狀态下 x:0 ,y:0, z:1如果裝置向右傾斜,x
會減小,反之向左傾斜x增大。向遠離使用者方向傾斜 y減少,接近方向傾斜則會增大。z表示垂直上的加速度,1表示靜止不動,失重狀态下為0
(為實驗性的API)。
3. deviceorientation事件 與 devicemotion事件。
4.9 觸摸 與 手勢事件
touchstart : 當手指觸摸螢幕觸發,即是已經有手指放在螢幕上了也會觸發。
touchmove:在手指滑動時候連續觸發。
touchend:當手指從螢幕上移開的時候觸發。
touchcancel:當系統停止跟蹤觸摸時觸發。
以上的均可以冒泡以及取消。除了常見的DOM屬性之外,觸摸事件還包括3個用于跟蹤觸摸的屬性。
touches:表示目前跟蹤的觸摸操作Touch對象數組。
targetTouches:特定于時間目标的Touch對象數組。
changeTouches:上次觸摸以來發生改變了的Touch對象數組。
其中每個 Touch 對象包含一下屬性。
clientX:觸摸目标在視口中的x坐标。
clientY:觸摸目标在視口中的y坐标。
identifier:辨別觸摸的唯一id。
pageX:觸摸目标在頁面中的x坐标。
pageY:觸摸目标在頁面中的y坐标。
screenX:觸摸目标在螢幕中的x坐标。
screenY:觸摸目标在螢幕中的y坐标。
target:觸摸的DOM節點目标。
在觸摸螢幕上的元素時,事件觸發程式順序如下。
1. touchstart
2. mouseover
3. mousemove 一次
4. mousedown
5. mouseup
6. click
7. touchend
對于手勢事件
gesturestart:當一個手指已經在螢幕上,另外一個手指觸摸螢幕的時候觸發。
gesturechange:當觸摸螢幕的任何一個手指位子發生變化的時候觸發。
gestureend:當任何一個手指從螢幕上移開的時候觸發。‘
5. 記憶體與性能
JS中添加到引入面上的事件處理程式數量将直接關系到頁面的整體運作性能,首先每個函數都是對象會占用記憶體,記憶體占用越多性能就會越差,其次指定事件的處理程式導緻的對DOM通路次數會延遲整個頁面的互動就緒時間。
5.1 事件委托
對于 事件處理程式過多 的問題,采用的解決方案就是 事件委托。
事件委托利用冒泡,指定一個事件處理程式來管理某一類型的所有事件。
對于清單項點選觸發事件,可以逐一的對各項 li 進行事件處理程式的綁定,但是一旦 li 項較多的時候就會有很大的問題,事件委托就是将事件處理程式綁定在外層的 ul 上(也可以是更外層的元素上),進行 event 中 target 判斷來執行代碼。
如果可行可以考慮在document對象上添加一個處理程式,用以處理頁面上發生的某種特定類型事件。
1. document 對象很快就可以通路,可以在頁面生命周期的任何時間點添加處理程式甚至無需等待DOMContentloaded 或者 load 事件。
2. 頁面的設定事件處理程式所需時間更少。
3. 整個頁面占用的記憶體空間更少,提升整體性能。
最适合采用的委托事件有 click,mousedown,mouseup,keydown,keyup和 keypress。
5.2 移除事件處理程式
每當将事件處理程式指定給元素的時候,運作中的浏覽器代碼和支援頁面互動的JS之間就會建立一個連接配接,連接配接越多頁面就越慢在不需要的時候移除那些不需要的事件處理程式也能改善頁面整體性能。
第一種情況需要注意的是,從文檔中移除帶有事件處理程式的元素時,可能通過純粹的DOM操作例如 removeChild 和 replaceChild
方法,但更多的是使用 innerHTML 來替換頁面中的某一部分,如果是使用
innerHTML來删除了那麼原來的事件處理程式極有可能将無法被當做垃圾回收。
還有一種情況就是在解除安裝頁面的時候,對于IE8以及之前版本浏覽器依舊有很多問題。在頁面解除安裝之前沒有清理幹淨事件處理程式,那麼他們将依舊滞留在記憶體之中。在我們切換頁面以及重新整理等操作的時候就會不斷地消耗記憶體。
6. 模拟事件
很少人知道可以使用JS在任意時刻來觸發特定事件,而此時的事件如同浏覽器建立事件一樣,在測試web應用程式的時候模拟出發時間是一種極其有用的技術。DOM2規範了此模拟事件的方式。IE9,Opera,Firefox,chrome和safari。
http://blog.chinaunix.net/uid-26672038-id-3956106.html