鍍金池/ 教程/ HTML/ DOM 事件
書(shū)單
JavaScript 動(dòng)畫(huà)
HTML
CSS Reset
屬性操作
DOM 事件
閉包
Photoshop
Atom 文本編輯器
表單操作
布局解決方案
類型系統(tǒng)
開(kāi)發(fā)實(shí)踐
數(shù)據(jù)通信
變量作用域
BOM
JavaScript 程序設(shè)計(jì)
前端工程師概述
CSS
響應(yīng)式布局
表達(dá)式與運(yùn)算符
基本語(yǔ)法
JavaScript 介紹
版本控制
布局
調(diào)試器
背景
圖片保存
多媒體
文檔樹(shù)
列表操作
Sublime 編輯器
盒模型
常見(jiàn)布局樣例
類型識(shí)別
變形
數(shù)據(jù)存儲(chǔ)
選擇器
頁(yè)面架構(gòu)
開(kāi)發(fā)及調(diào)試工具
頁(yè)面模塊化
節(jié)點(diǎn)操作
測(cè)量及取色
瀏覽器兼容
HTML 簡(jiǎn)介
內(nèi)置對(duì)象
實(shí)體字符
產(chǎn)品前端架構(gòu)
協(xié)作流程
切圖
工具, 面板, 視圖
正則表達(dá)式
動(dòng)畫(huà)
語(yǔ)句
面向?qū)ο?/span>
HTML 語(yǔ)法
HTML 標(biāo)簽
技術(shù)選擇
樣式操作
圖片優(yōu)化與合并
語(yǔ)法
DOM 編程藝術(shù)
Canvas
接口設(shè)計(jì)
頁(yè)面優(yōu)化
文本

DOM 事件

DOM 事件

何為 DOM 事件,HTML DOM 使JavaScript 有能力對(duì) HTML 事件做出反應(yīng)。(例如,點(diǎn)擊 DOM 元素,鍵盤(pán)被按,輸入框輸入內(nèi)容以及頁(yè)面加載完畢等)

事件流

一個(gè) DOM 事件可以分為捕獲過(guò)程、觸發(fā)過(guò)程冒泡過(guò)程。 DOM 事件流為 DOM 事件的處理及執(zhí)行的過(guò)程。下面以一個(gè)<a>元素被點(diǎn)擊為例。

http://wiki.jikexueyuan.com/project/fend_note/images/E/event_flow.jpg" alt="" />

  1. [紅虛線]Capture Phase(事件捕獲過(guò)程)當(dāng) DOM 事件發(fā)生時(shí),它會(huì)從window節(jié)點(diǎn)一路跑下去直到觸發(fā)事件元素的父節(jié)點(diǎn)為止,去捕獲觸發(fā)事件的元素。
  2. [紅綠實(shí)線]Target Phase(事件觸發(fā)過(guò)程)當(dāng)事件被捕獲之后就開(kāi)始執(zhí)行事件綁定的代碼
  3. [綠虛線]Bubble Phase(冒泡過(guò)程)當(dāng)事件代碼執(zhí)行完畢后,瀏覽器會(huì)從觸發(fā)事件元素的父節(jié)點(diǎn)開(kāi)始一直冒泡到window元素(即元素的祖先元素也會(huì)觸發(fā)這個(gè)元素所觸發(fā)的事件

關(guān)于捕獲過(guò)程的補(bǔ)充

如果有一個(gè)支持三個(gè)階段的事件,它一定在觸發(fā)時(shí)遵循下面的順序:

Capture -> Target -> Bubbling

http://wiki.jikexueyuan.com/project/fend_note/images/E/event-phases.png" alt="" />

使用下面的代碼來(lái)舉例:

// 添加Capture階段事件
docuemnt.addEventListener('click',function(){
    alert('capture:'+1);
},true);
tableNode.addEventListener('click',function(){
    alert('capture:'+2);
},true);
tdNode.addEventListener('click',function(){
    alert('capture:'+3);
},true);

// 添加Bubbling階段事件
docuemnt.addEventListener('click',function(){
    alert('bubble:'+1);
});
tableNode.addEventListener('click',function(){
    alert('bubble:'+2);
});
tdNode.addEventListener('click',function(){
    alert('bubble:'+3);
});

輸出結(jié)果為:

capture:1
capture:2
capture:3
bubble:3
bubble:2
bubble:1

http://wiki.jikexueyuan.com/project/fend_note/images/E/event-apply.png" alt="" />

// 對(duì)document添加了三個(gè)bubbling階段的事件
document.addEventListener('click',function(){
    alert(1);
});
document.addEventListener('click',function(){
    alert(2);
});
document.addEventListener('click',function(){
    alert(3);
});

如上面的代碼所示,其為同一節(jié)點(diǎn)添加了同一階段的多個(gè)事件,那執(zhí)行順序如何呢? 早期并沒(méi)有規(guī)范定義,DOM 3 中規(guī)范已經(jīng)明確規(guī)定 同一節(jié)點(diǎn)同一階段的事件應(yīng)按照注冊(cè)函數(shù)的順序執(zhí)行。

在實(shí)際項(xiàng)目過(guò)程中,某些情況下比如若干的組件或者模塊都需要監(jiān)聽(tīng)某個(gè)節(jié)點(diǎn)的某個(gè)事件,但是組件或者模塊的生成(即添加事件的時(shí)機(jī))是不一定保證順序的,所以這個(gè)情況下如果某個(gè)組件對(duì)這個(gè)節(jié)點(diǎn)的這個(gè)事件的優(yōu)先級(jí)特別高(需要保證必須先觸發(fā)這個(gè)組件里的這個(gè)事件)而這個(gè)平臺(tái)又支持這個(gè)階段事件的話可以添加 capture 階段事件,用三階段的順序來(lái)保證,比如移動(dòng)平臺(tái)模擬手勢(shì)的實(shí)現(xiàn)會(huì)添加 document 上 touchXXX 的 capture 階段事件,以優(yōu)先識(shí)別手勢(shì)操作 當(dāng)然實(shí)踐過(guò)程中考慮到不同瀏覽器對(duì)三階段支持的情況的差異,大部分情況下都采用的是 bubbling 階段的事件

—— 蔡劍飛 網(wǎng)易前端工程師

NOTE:低版本 IE 中并未實(shí)現(xiàn)捕獲過(guò)程。也不是所有事件均存在這三個(gè)完整的過(guò)程(例如 load 沒(méi)有冒泡事件)

NOTE+:在這三個(gè)階段中無(wú)論將事件捕獲事件處理注冊(cè)到任意一個(gè)父或祖父節(jié)點(diǎn)上都會(huì)被觸發(fā)事件。

事件注冊(cè)

事件注冊(cè),取消以及觸發(fā)其作用對(duì)象均為一個(gè) DOM 元素。

注冊(cè)事件

eventTarget.addEventListener(type, listener[,useCapture])
  • evenTarget 表示要綁定事件的DOM元素
  • type 表示要綁定的事件,如:"click"
  • listener 表示要綁定的函數(shù)
  • useCapture 可選參數(shù),表示是否捕獲過(guò)程

NOTE:useCapture 為設(shè)定是否為捕獲過(guò)程,默認(rèn)事件均為冒泡過(guò)程,只有 useCapturetrue 時(shí)才會(huì)啟用捕獲過(guò)程。

// 獲取元素
var elem = document.getElemenyById('id');

// 事件處理函數(shù)
var clickHandler = function(event) {
  // statements
};

// 注冊(cè)事件
elem.addEventListener('click', clickHandler, false);

// 第二種方式,不建議使用
elem.onclick = clickHandler;
// 或者來(lái)彌補(bǔ)只可觸發(fā)一個(gè)處理函數(shù)的缺陷
elem.onclick = function(){
  clickHandler();
  func();
  // 其他處理函數(shù)
};

取消事件

eventTarget.removeEventListener(type, listener[,useCapture]);
  • evenTarget 表示要綁定事件的DOM元素
  • type 表示要綁定的事件,如:"click"
  • listener 表示要綁定的函數(shù)
  • useCapture 可選參數(shù),表示是否捕獲過(guò)程
// 獲取元素
var elem = document.getElemenyById('id');

// 取消事件
elem.removeEventListener('click', clickHandler, false);

// 第二種方式。不建議使用
elem.onclick = null;

觸發(fā)事件

點(diǎn)擊元素,按下按鍵均會(huì)觸發(fā) DOM 事件,當(dāng)然也可以以通過(guò)代碼來(lái)觸發(fā)事件。

eventTarget.dispatchEvent(type);

// 獲取元素
var elem = document.getElemenyById('id');

// 觸發(fā)事件
elem.dispatchEvent('click');

瀏覽器兼容型

以上均為 W3C定義的標(biāo)準(zhǔn)定義,但早期瀏覽器 IE8 及其以下版本,均沒(méi)有采用標(biāo)準(zhǔn)的實(shí)現(xiàn)方式。不過(guò)這些低版本瀏覽器也提供了對(duì)于 DOM 事件的注冊(cè)、取消以及觸發(fā)的實(shí)現(xiàn)。

事件注冊(cè)與取消attchEvent/detachEvent。事件觸發(fā),fireEvent(e),其也不存在捕獲階段(Capture Phase)。

兼容低版本代碼實(shí)現(xiàn)

注冊(cè)事件

var addEvent = document.addEventListener ?
  function(elem, type, listener, useCapture) {
    elem.addEventListener(type, listener, useCapture);
  } :
  function(elem, type, listener, useCapture) {
    elem.attachEvent('on' + type, listener);
  }

取消事件

var addEvent = document.removeElementListener ?
  function(elem, type, listener, useCapture) {
    elem.removeElementListener(type, listener, useCapture);
  } :
  function(elem, type, listener, useCapture) {
    elem.detachEvent('on' + type, listener);
  }

事件對(duì)象

調(diào)用事件處理函數(shù)時(shí)傳入的信息對(duì)象,這個(gè)對(duì)象中含有關(guān)于這個(gè)事件的詳細(xì)狀態(tài)和信息,它就是事件對(duì)象 event。其中可能包含鼠標(biāo)的位置,鍵盤(pán)信息等。

// 獲取元素
var elem = document.getElemenyById('id');

// 事件處理函數(shù)
var clickHandler = function(event) {
  // statements
};

// 注冊(cè)事件
elem.addEventListener('click', clickHandler, false);

NOTE:在低版本 IE 中事件對(duì)象是被注冊(cè)在 window 之上而非目標(biāo)對(duì)象上。使用下面的兼容代碼既可解決。

var elem = document.getElemenyById('id');

// 事件處理函數(shù)
var clickHandler = function(event) {
  event = event || window.event;
  // statements
};

屬性和方法

通用屬性和方法

屬性

  • type 事件類型
  • target(srcElement IE 低版本) 事件觸發(fā)節(jié)點(diǎn)
  • currentTarget 處理事件的節(jié)點(diǎn)

方法

  • stopPropagation 阻止事件冒泡傳播
  • preventDefault 阻止默認(rèn)行為
  • stopImmediatePropagation 阻止冒泡傳播
阻止事件傳播

event.stopPropagation()(W3C規(guī)范方法),如果在當(dāng)前節(jié)點(diǎn)已經(jīng)處理了事件,則可以阻止事件被冒泡傳播至 DOM 樹(shù)最頂端即 window 對(duì)象。

event.stopImmediatePropagation() 此方法同上面的方法類似,除了阻止將事件冒泡傳播值最高的 DOM 元素外,還會(huì)阻止在此事件后的事件的觸發(fā)。

event.cancelBubble=true 為 IE 低版本中中對(duì)于阻止冒泡傳播的實(shí)現(xiàn)。

阻止默認(rèn)行為

默認(rèn)行為是指瀏覽器定義的默認(rèn)行為(點(diǎn)擊一個(gè)鏈接的時(shí)候,鏈接默認(rèn)就會(huì)打開(kāi)。當(dāng)我們雙擊文字的時(shí)候,文字就會(huì)被選中),比如單擊鏈接可以打開(kāi)新窗口。

Event.preventDefault() 為 W3C 規(guī)范方法,在 IE 中的實(shí)現(xiàn)方法為 Event.returnValue=false。

事件分類

Event

http://wiki.jikexueyuan.com/project/fend_note/images/E/event_types.jpg" alt="" />

事件類型 是否冒泡 元素 默認(rèn)事件 元素例子
load NO Window, Document, Element None window, image, iframe
unload NO Window, Document, Element None window
error NO Window, Element None window, image
select NO Element None input, textarea
abort NO Window, Element None window, image
window
  • load 頁(yè)面全部加載完畢
  • unload 離開(kāi)本頁(yè)之前的卸載
  • error 頁(yè)面異常
  • abort 取消加載
image
  • load 圖片加載完畢
  • error 圖標(biāo)加載錯(cuò)誤
  • abort 取消圖標(biāo)加載

在目標(biāo)圖標(biāo)不能正常載入時(shí),載入備份替代圖來(lái)提供用戶體驗(yàn)。

<img src="http://sample.com/img.png" onerror="this.src='http://sample.com/default.png'">

UIEvent

事件類型 是否冒泡 元素 默認(rèn)事件 元素例子
resize NO Window, Element None window, iframe
scroll NO/YES Document, Element None document, div

NOTE:resize 為改變?yōu)g覽器或iframe的窗體大小時(shí)會(huì)觸發(fā)事件,scroll 則會(huì)在滑動(dòng)內(nèi)容時(shí)觸發(fā),作用于 Document 則不會(huì)冒泡,作用于內(nèi)部元素則會(huì)冒泡。

MouseEvent

DOM 事件中最常見(jiàn)的事件之一。

事件類型 是否冒泡 元素 默認(rèn)事件 元素例子
click YES Element focus/activation div
dbclick YES Element focus/activation/select div
mousedown YES Element drag/scroll/text selection div
mosuemove YES Element None div
mouseout YES Element None div
mouseover YES Element None div
mouseup YES Element context menu div
mouseenter NO Element None div
mouseleave NO Element None div

NOTE:mouseentermouseover 的區(qū)別為前者在鼠標(biāo)在子元素直接移動(dòng)不會(huì)觸發(fā)事件,而后者會(huì)觸發(fā)。 mouseleavemouseout 同上相似。

屬性
  • clientX, clientX
  • screenX, screenY
  • ctrlKey, shiftKey, altKey, metaKey 如果被按下則為真(true)
  • button(0, 1, 2) 鼠標(biāo)的間位

http://wiki.jikexueyuan.com/project/fend_note/images/M/mouse_move_event.jpg" alt="" />

MouseEvent 順序

鼠標(biāo)的移動(dòng)過(guò)程中會(huì)產(chǎn)生很多事件。事件的監(jiān)察頻率又瀏覽器決定。

例子:從元素 A 上方移動(dòng)過(guò)

mousemove -> mouseover(A) -> mouseenter(A) -> mousemove(A) -> mouseout(A) -> mouseleave(A)

例子:點(diǎn)擊元素

mousedown -> [mousemove] -> mouseup -> click

實(shí)例:拖動(dòng)元素
<div id="div0"></div>
<style media="screen">
  #div0 {
    position: absolute;
    top: 0;
    left: 0;
    width: 100px;
    height: 100px;
    border: 1px solid black;
  }
</style>
var elem = document.getElemenyById('div0');
var clientX, clientY, isMoving;
var mouseDownHandler = function(event) {
  event = event || window.event;
  clientX = event.clientX;
  clientY = event.clientY;
  isMoving = true;
}

var mouseMoveHandler = function(event) {
  if (!isMoving) return;
  event = event || window.event;
  var newClientX = event.clientX,
      newClientY = event.clientY;
  var left = parseInt(elem.style.left) || 0,
      top = parseInt(elem.style.top) || 0;
  elem.style.left = left + (newClientX - clientX) + 'px';
  elem.style.top = top + (newClientY - clientY) + 'px';
  clientX = newClientX;
  clientY = newClientY;
}

var mouseUpHandler = function() {
  isMoving = false;
}

addEvent(elem, 'mousedown', mouseDownHandler);
addEvent(elem, 'mouseup', mouseUpHandler);
addEvent(elem, 'mousemove', mouseMoveHandler);

滾輪事件(Wheel)

事件類型 是否冒泡 元素 默認(rèn)事件 元素例子
wheel YES Element scroll or zoom document div

屬性

  • deltaMode 鼠標(biāo)滾輪偏移量的單位
  • deltaX
  • deltaY
  • deltaZ

FocusEvent

其用于處理元素獲得或失去焦點(diǎn)的事件。(例如輸入框的可輸入狀態(tài)則為獲得焦點(diǎn),點(diǎn)擊外部則失去焦點(diǎn))

事件類型 是否冒泡 元素 默認(rèn)事件 元素例子
blur NO Window, Element None window, input
focus NO Window, Element None window, input
focusin NO window, Element None window, input
focusout NO window, Element None window, input

NOTE:blur 失去焦點(diǎn)時(shí),focus 獲得焦點(diǎn)時(shí),focusin 即將獲得焦點(diǎn),focusout即將失去焦點(diǎn)。

屬性

一個(gè)元素失去,既另一個(gè)元素獲得焦點(diǎn)。這里的 relatedTarget 則為相對(duì)的那個(gè)元素。

  • relatedTarget

InputEvent

輸入框輸入內(nèi)容則會(huì)觸發(fā)輸入事件。

事件類型 是否冒泡 元素 默認(rèn)事件 元素例子
beforeInput YES Element update DOM Element input
input YES Element None input

NOTE:beforeInput 為在按鍵按下后即將將輸入字符顯示之前生成的事件。

NOTE+:IE 并沒(méi)有 InputEvent 則需使用 onpropertychange(IE) 來(lái)代替。

KeyboardEvent

其用于處理鍵盤(pán)事件。

事件類型 是否冒泡 元素 默認(rèn)事件 元素例子
keydown YES Element beforeInput/input/focus/blur/activation div, input
keyup YES Element None div, input

屬性

  • key 按下的鍵字符串
  • code
  • ctrlKey, shiftKey, altKey, metaKey
  • repeat 代表按鍵不松開(kāi)為 true
  • keyCode
  • charCode
  • which

事件代理

事件代理是指在父節(jié)點(diǎn)上(可為元素最近的父節(jié)點(diǎn)也可為上層的其他節(jié)點(diǎn))處理子元素上觸發(fā)的事件,其原理是通過(guò)事件流機(jī)制而完成的。可以通過(guò)事件對(duì)象中獲取到觸發(fā)事件的對(duì)象(如下所示)。

var elem = document.getElemenyById('id');
elem.addEventListener('click', function(event) {
  var e = event || window.event;
  var target = e.target || e.srcElement;
  // statements
});

優(yōu)點(diǎn)

  • 需要管理的事件處理函數(shù)更少
  • 內(nèi)存分配更少,更高效
  • 增加與刪除子節(jié)點(diǎn)可以不額外處理事件

缺點(diǎn)

  • 事件管理的邏輯變的復(fù)雜(因?yàn)槊芭輽C(jī)制)
上一篇:頁(yè)面優(yōu)化下一篇:HTML