事件

本章內容:理解事件流葬凳、使用事件處理程序兢榨、不同的事件類型

JavaScript與HTML之間的交互是通過事件實現的嗅榕。事件,就是文檔或者瀏覽器窗口發(fā)生的一些特定的交互瞬間吵聪×枘牵可以使用偵聽器(或處理程序)來預定事件,以便事件發(fā)生時執(zhí)行相應的代碼暖璧。這種在傳統(tǒng)軟件工程中被稱為觀察員模式案怯,支持頁面的行為與頁面的外觀之間的松散耦合。

一澎办、事件流

當瀏覽器發(fā)展到第四代的時候(IE4及 Netscape Communicator 4)嘲碱,瀏覽器開發(fā)團隊遇到了一個很有意思的問題:頁面的哪一部分會擁有某個特定的事件金砍?要明白這個問題問的是什么,可以想象畫在一張紙上的一組同心圓麦锯。如果你把手指放在圓心上恕稠,那么你的手指指向的不是一個圓,而是紙上的所有圓扶欣。同樣:當你單機某個按鈕鹅巍,他們都認為單機事件不僅僅發(fā)生在按鈕上,在單機按鈕的同時料祠,你也單機了按鈕的容器元素骆捧,甚至單機了整個頁面。
事件流描述的是從頁面接收事件的順序髓绽。但有意思的是敛苇,IE 和 Netscape 開發(fā)團隊提出了差不多是完全相反的事件流概念。IE的事件流是事件冒泡流顺呕,而Netscape Communicator 的事件流是事件捕獲流枫攀。

1.1、事件冒泡

IE的事件流叫做事件冒泡(event bubbling)株茶,即事件開始時由最具體的元素接收来涨,然后逐級向上傳播到較為不具體的節(jié)點(文檔)。

<!DOCTYPE html>
<html>
  <head>
    <title>Event Bubbling</title>
  </head>
  <body>
    <div id="myDiv">click me</div>
  </body>
</html>

如果單機了頁面的<div>元素启盛,那么這個click事件會按照如下順序傳播:
(1):<div>
(2):<body>
(3):<html>
(4):<document>

下圖展示了事件冒泡的過程


事件冒泡

所有現代瀏覽器都支持事件冒泡蹦掐,在具體實現上還是有一些差別。IE5.5及更早版本中的事件冒泡會跳過<html>元素(<body>直接到 Document)驰徊。IE9笤闯、Firefox堕阔、Chrome棍厂、Safari則將事件一直冒泡到window對象

1.2、事件捕獲

Netscape Communicator團隊提出的另一種事件流叫做事件捕獲(event capturing)超陆。事件捕獲的思想是不太具體的節(jié)點應該更早接收到事件牺弹,而最具體的節(jié)點應該最后接收到事件。單機<div>元素就會以下列順序觸發(fā)click事件时呀。
(1):<document>
(2):<html>
(3):<body>
(4):<div>

下圖展示事件捕獲的過程

事件捕獲

IE9张漂、Safari、Chrome谨娜、Opera航攒、Firefox目前都支持這種事件流模型
由于有點老版本的瀏覽器不支持,因此較少使用事件捕獲趴梢。建議放心使用事件冒泡漠畜,在有特殊需要時再使用事件捕獲

1.3币他、DOM事件流

"DOM2級事件" 規(guī)定的事件流包括三個階段:事件捕獲階段、目標階段憔狞、事件冒泡階段蝴悉。首先發(fā)生的是事件捕獲階段,為截取事件提供了機會瘾敢;然后是實際的目標接收到事件拍冠;最后是冒泡階段,可以在這個階段對事件做出響應簇抵。
以前面的HTML代碼為例庆杜,當點擊<div>元素后的觸發(fā)事件順序

觸發(fā)順序

"目標階段",事件在<div>上發(fā)生碟摆,會在事件處理程序中被看成冒泡階段的一部分

"DOM2級事件" 規(guī)范明確要求捕獲階段不會涉及事件目標欣福,但IE9、Safari焦履、Chrome拓劝、Firefox、Opera9.5級更高版本都會在捕獲階段觸發(fā)事件對象上的事件嘉裤。結構郑临,就是有兩個機會在目標對象上面操作事件

二、事件處理程序

事件就是用戶或瀏覽器自身執(zhí)行的某種動作屑宠。而響應某個事件的函數就叫做事件處理程序(或事件偵聽器)

2.1怀大、HTML事件處理程序

某個元素支持的每種事件,都可以使用一個與相應事件處理程序相同的HTML特性指定蜈膨。
例如:在按鈕被單擊時執(zhí)行一些JavaScript

<input type="button" value="Click me" onclick="alert('clicked')" />

在 HTML 中定義的事件鳞芙,也可以調用在頁面其他地方定義的腳本

<script type="text/javascript">
  function showMessage() {
    alert('Hello World!')
  }
</script>
<input type="button" value="click me" onclick="showMessage()">

事件處理程序中的代碼在執(zhí)行時,有權訪問全局作用域中的任何代碼

這樣指定事件處理程序具有一些獨到之處卫玖。首先公你,這樣會創(chuàng)建一個封裝著元素屬性值的函數。這個函數中有一個全局變量 event假瞬,也就是事件對象陕靠。

<!-- 輸出事件類型,當前為 click -->
<input type="button" value="click me" onclick="alert(event.type)" />

通過 event 變量脱茉,可以直接訪問事件對象剪芥,你不同自己定義它,也不用從函數的參數列表中讀取琴许。在這個函數內部税肪,this 值等于事件的目標元素,例如:

<!-- 輸出當前 input 的value,這里為 "click me" -->
<input type="button" value="click me" onclick="alert(this.value)" />

關于這個動態(tài)創(chuàng)建的函數益兄,另一個有意思的地方是它擴展作用域的方式签财。在這個函數內部,可以向訪問局部變量一樣訪問 document及改元素本身的成員偏塞。這個函數使用 with 像下面這樣擴展作用域:

function() {
  with(document) {
    with(this) {
      // 元素屬性值
    }
  }
}

如下一來唱蒸,事件處理程序要訪問自己的屬性就簡單多了

<!-- 輸出 "click me" -->
<input type="button" value="click me" onclick="alert(value)" />

實際上,這樣的擴展作用域的方式灸叼,無法就是想讓事件處理程序無需引用表單元素就能訪問其它表單字段神汹,例如:

<form method="post">
  <input type="text" name="username" value="" />
  <!-- 輸出第一個文本框中的value值 -->
  <input type="button" value="Echo Username" onclick="alert(username.value)" />
</form>

不過,在HTML 中指定事件處理程序有幾個缺點

  1. 存在一個時差問題古今。用戶可能在HTML元素一出現在頁面上就觸發(fā)相應的事件屁魏,但當時的事件處理程序有可能尚不具備執(zhí)行條件。
  2. 這樣擴展事件處理程序的作用域鏈在不同瀏覽器中會導致不同的結果捉腥。不同 JavaScript引擎準許的標識符解析規(guī)則略有差異氓拼,很可能會訪問非限定對象成員時出錯。
  3. HTML 與 JavaScript代碼緊密耦合

2.2抵碟、DOM0 級事件處理程序

通過JavaScript指定事件處理程序的傳統(tǒng)方式桃漾,就是將一個函數賦值給一個事件處理程序屬性。一直沿用至今拟逮,具有 簡單撬统、跨瀏覽器的優(yōu)勢。

每個元素都有自己的事件處理程序屬性敦迄,這些屬性通常全部小寫恋追,例如onclick。將這種屬性的值設置為一個函數罚屋,就可以指定事件處理程序

var btn = document.getElementById('myBtn')
btn.onclick = function() {
  alert('clicked')
}

使用DOM0 級方法指定的事件處理程序被認為是元素的方法苦囱,因此,這時候的事件處理程序是在 元素的作用域中運行的脾猛;換句話說撕彤,程序中的this引用當前元素

btn.onclick = function() {
  alert(this.id) // myBtn
}

以這種方式添加的事件處理程序會在事件流的冒泡階段被處理
也可以刪除通過DOM0 級方法指定的事件處理程序,只要像下面這樣將事件處理程序屬性的值設為null即可尖滚。

btn.onclick = null // 刪除事件處理程序

2.3喉刘、DOM2 級事件處理程序

"DOM2級事件" 定義了兩個方法,用于處理指定和刪除事件處理程序的操作:addEventListener()漆弄、removeEventListener()。它們接受三個參數:

  1. 要處理的事件名
  2. 作為事件處理程序的函數
  3. 一個布爾
    • true造锅,表示在捕獲階段調用事件處理程序撼唾;
    • false,表示在冒泡階段調用事件處理程序哥蔚;

例如為按鈕添加 click 事件

var btn = document.getElmentById('myBtn')
btn.addEventListener('click', function() {
  alert(this.id)
}, false)

使用 DOM2 級方法添加事件處理程序的主要好處是可以為一個事件添加多個事件處理程序倒谷。

var btn = document.getElementById('myBtn')
btn.addEventListener('click', function() {
  alert(this.id)
}, false)

btn.addEventListener('click', function() {
  alert('Nice to see you')
}, false)

這兩個事件會依次觸發(fā)蛛蒙,myBtn Nice to see you


通過 addEventListener() 注冊的事件處理程序 只能使用 removeEventListener() 來移除;移除時傳入的參數與添加處理程序時傳入的參數相同渤愁。這意味著通過 addEventListener() 添加的匿名函數將無法移除

btn.addEventListener('click', function() {
  alert('Hello')
}, false)

btn.removeEventListener('click', function() { // 無效
  alert('Hello')
}, false)

雖然函數看上去是一致的牵祟,但實際上兩個函數不是同一個。
移除事件必須傳入相同的 事件處理程序函數抖格。如下:

var handle = function() {
  alert('Hello')
}

btn.addEventListener('click', handle, false)
btn.removeEventListener('click', handle, false)

大多數情況下诺苹,都是將事件處理程序添加到事件流的冒泡階段,這樣可以最大限度的兼容各種瀏覽器雹拄。如果不是特別需要收奔,不建議在事件捕獲階段注冊事件處理程序

2.4、IE事件處理程序

IE(IE11廢棄了下面的兩個方法支持DOM標準滓玖,IE11以下都支持下面的兩個方法坪哄,IE8及以下不支持DOM標準中提供的兩個方法)實現了與DOM中類似的兩個方法:attachEvent()、deleteEvent()势篡。這兩個方法接受相同的兩個參數:

  1. 事件處理名稱
  2. 事件處理函數翩肌。
    由于IE8及更早版本只支持事件冒泡,所以通過 attachEvent() 添加的事件處理程序都會被添加到冒泡階段
    使用 attachEvent() 為按鈕添加一個事件處理程序:
btn.attachEvent('onclick', function() {
  alert('See you')
})

在 IE 中使用 attachEvent() 與使用 DOM0 級方法的主要區(qū)別在于事件處理程序的作用域禁悠。
在使用DOM0 級方法的情況下摧阅,事件處理程序會在其所屬元素的作用域內運行;
attachEvent() 方法的情況下绷蹲,事件處理程序會在全局作用域中運行棒卷,因此 this 等于 window。

btn.attachEvent('onclick', function() {
  alert(this === window) // true
})

類是的祝钢,attachEvent() 也可以為同一個事件 添加多個事件處理程序比规。

btn.attachEvent('onclick', function() {
  alert('clicked')
})

btn.attachEvent('onclick', function() {
  alert('****')
})

不過與DOM方法不同的是,這些事件處理程序不是以添加它們的順序執(zhí)行拦英,而是以相反的順序被觸發(fā)蜒什。以上會先看到 **** 然后看到 clicked

使用 attachEvent() 添加的事件可以通過 detachEvent() 來移除,條件是必須提供相同的參數(與DOM一樣疤估,不能使用匿名函數)

var handle = function() {
  alert('Hello')
}
btn.attachEvent('onclick', handle)
btn.detachEvent('onclick', handle)

2.5灾常、跨瀏覽器的事件處理程序

根據 DOM 和 IE 封裝兼容函數,如下

var EventUtil = {
  addHandler: function(element, type, handler) {
    if (element.addEventListener) { // 支持 addEventListener
      element.addEventListener(type, handler, false)
    }else if (element.attachEvent) { // IE
      element.attachEvent('on' + type, handler)
    } else { // 都不支持的情況下铃拇,默認使用 DOM0 級
      element['on' + type] = handler
    }
  },
  removeHandler: function(element, type, handler) {
    if (element.removeEventListener) { // 支持 DOM
      element.removeEventListener(type, handler, false)
    } else if (element.detachEvent) { // IE
      element.detachEvent('on' + type, handler)
    } else {
      element['on' + type] = null
    }
  }
}

可以像下面這樣使用 EventUtil 對象:

var btn = document.getElementById('myBtn')
var handler = function() {
  alert('Hello')
}
// 注冊事件
EventUtil.addHandler(btn, 'click', handler)
// 移除事件
EventUtil.removeHandler(btn, 'click', handler)

addHandler() 和 removeHandler() 并沒有考慮到所有的瀏覽器問題钞瀑,例如在IE中的作用域問題。此外還需要注意慷荔,DOM0 級對每個事件只支持一個事件處理程序雕什。不過只支持DOM0 級的瀏覽器已經幾乎滅絕了,所以不是上面問題

三、事件對象

觸發(fā)DOM上的某個事件時贷岸,會產生一個事件對象 event壹士,這個對象中包含著所有與事件相關的信息。

3.1偿警、DOM中的事件對象

兼容DOM的瀏覽器會將一個 event 對象傳入到事件處理程序中躏救。

var btn = document.getElementById('myBtn')
btn.onclick = function(event) {
  alert(event.type) // click
}
btn.addEventListener('click', function(event) {
  alert(event.type) // click
}, false)

在通過 HTML 特性指定事件處理程序時,變量 event 中保存著 event 對象螟蒸。

<input type="button" value="Click me" onclick="alert(event.type)" />

event 對象包含于創(chuàng)建它的特定事件有關的屬性和方法盒使,所有的事件都會有下表列出的成員。

屬性/方法 類型 讀寫 說明
bubbles Boolean 只讀 表明事件是否冒泡
cancelable Boolean 只讀 表明是否可以取消事件的默認行為
currentTarget Element 只讀 其他事件處理程序當前正在處理事件的那個元素
defaultPrevented Boolean 只讀 為true表示已經調用了 preventDefault()
detail Integer 只讀 與事件相關的細節(jié)信息
eventPhase Integer 只讀 調用事件處理程序的階段:1表示捕獲階段尿庐,2表示處于目標階段忠怖,3表示處于冒泡階段
preventDefault() Function 只讀 取消事件的默認行為。如果cancelable是true抄瑟,則可以使用這個方法
stopImmediatePropagation() Function 只讀 取下事件的進一步捕獲或冒泡凡泣,同時阻止任何事件處理程序被調用
stopPropagation() Function 只讀 取消事件的進一步捕獲或冒泡。如果bubbles 為 true皮假,則可以使用這個方法
target Element 只讀 事件的目標
trusted Boolean 只讀 為true表示事件是瀏覽器生成的鞋拟。為false表示事件是由開發(fā)人員通過JavaScript創(chuàng)建的
type String 只讀 被觸發(fā)的事件類型
view Abstractview 只讀 與事件關聯的抽象視圖。等同于發(fā)生事件的window對象

在事件處理程序內部惹资,對象 this 始終等于 currentTarget 的值贺纲,而 target 則只包含事件的實際目標。如果直接將事件處理程序指定給了目標元素褪测,則 this猴誊、currentTarget、target包含相同的值侮措。

var btn = document.getElementById('btn')
btn.onclick = function (event) {
  console.log(event.currentTarget === this) // true
  console.log(event.target === this) // true
}

在需要通過一個函數處理多個事件時懈叹,可以利用 type 屬性實現

var btn = document.getElementById('myBtn')
var handler = function(event) {
  switch (event.type) {
    case 'click':
      alert('clicked')
      break;
    case 'mouseover':
      event.target.style.backgroundColor = 'red'
      break;
    case 'mouseout':
      event.target.style.backgroundColor = ''
      break;
  }
}

btn.onclick = handler
btn.onmouseover = handler
btn.onmouseout = handler

要阻止特定事件的默認行為,可以使用 preventDefault() 方法分扎。例如澄成,取消鏈接的默認行為

var link = document.getElementsByTagName('a')[0]
link.onclick = function(event) {
  event.preventDefault() // 取消默認行為

  // todo
}

只有 cancelable 屬性設置為 true的事件,才可以使用preventDefault() 來取消其默認行為畏吓。


stopPropagation() 方法用于立即停止事件在 DOM 層次中的傳播墨状,即取消進一步的事件捕獲或冒泡。

var btn = document.getElementById('myBtn')
btn.onclick = function(event) {
  alert('clicked')
  event.stopPropagation() // 阻止冒泡
}
document.body.onclick = function(event) {
  alert('Body clicked')
}

當點擊 btn 的時候菲饼,只會觸發(fā) btn的點擊事件肾砂,而不會通過冒泡同時去觸發(fā) body 的click事件


事件對象的 eventPhase屬性,可以用來確定事件當前正位于事件流的哪個階段巴粪。

  • 捕獲階段調用的事件處理程序通今,eventPhase 等于 1
  • 事件處理程序處于目標身上粥谬,則 event-Phase 等于 2
  • 冒泡階段調用的事件處理程序肛根,eventPhase 等于 3

需要注意的是辫塌,盡管”處于目標階段“發(fā)生在冒泡階段,但 eventPhase 仍然會等于2

var btn = document.getElementById('myBtn')
btn.onclick = function (event) {
  alert (event.eventPhase) // 2
}
document.body.addEventListener('click', function(event) {
  alert(event.eventPhase) // 1
}, true) // 設置為 事件捕獲
document.body.onclick = function (event) {
  alert(event.eventPhase)
}

3.2派哲、IE中的事件對象(IE8-)

與訪問DOM中的event 對象不同臼氨,要訪問IE中的 event 對象有幾種不同的方式,取決于指定事件處理程序的方法芭届。

使用DOM0級方法添加事件處理程序時储矩,event 對象作為window 對象的一個屬性存在。

var btn = document.getElementById(myBtn')
btn.onclick = function() {
  var event = window.event
  alert(event.type)
}

如果事件處理程序是使用 attachEvent() 添加的褂乍,那么就會有一個 event 對象作為參數被傳入事件處理程序函數中持隧。

var btn = document.getElementById('myBtn')
btn.attachEvent('onclick', function(event) {
  alert(event.type) // click
})

如果是通過 HTML 特性指定的事件處理程序,那么還可以通過一個名叫 event的變量來訪問 event 對象

<input type="button" value="Click me" onclick="alert(event.type)" />

IE 的 event 對象同樣也包含以創(chuàng)建它的事件相關的屬性和方法逃片。下列是所有事件對象都會包含的屬性和方法

屬性/方法 類型 讀/寫 說明
cancelBubble Boolean 讀/寫 默認值為false屡拨,但將其設置為 true 就可以取消事件冒泡(與DOM中的 stopPropagation() 方法的作用相同 )
returnValue Boolean 讀/寫 默認值為 true,但將其設置為 false 就可以取消事件的默認行為(與 DOM 中的 preventDefault() 方法的作用相同 )
srcElement Element 只讀 事件的目標(與DOM中的target屬性相同)
type String 只讀 被觸發(fā)的事件的類型

因為事件處理程序的作用域是根據指定它的方式來確定的褥实,所以不能認為 this 會始終等于事件目標呀狼。故而,最好還是使用 event.srcElement 比較保險损离。

var btn = document.getElement('myBtn')
btn.onclick = function() {
  alert(window.event.srcElement === this) // true
}

btn.attachEvent('onclick', function(event) {
  alert(event.srcElement === this) // false
})

returnValue 屬性相當于 DOM 中的 preventDefault() 方法哥艇,他們的作用域都是取消給定事件的默認行為。只要將 returnValue 設置為 false僻澎,就可以阻止默認行為貌踏。

var link = document.getElementsByTagName('a')[0]
link.onclick = function() {
  window.event.returnValue = false
}

cancelBubble 屬性 與 DOM 中的 stopPropagation() 方法作用相同,都是用來停止事件冒泡的窟勃。由于IE不支持事件捕獲祖乳,因而只能取消事件冒泡;但stopPropagation() 可以同時取消 事件冒泡和捕獲

var btn = document.getElementById('myBtn')
btn.onclick = function() {
  alert('clicked')
  window.event.cancelBubble = true
}
document.body.onclick = function() {
  alert('Body clicked')
}

3.3拳恋、跨瀏覽器的事件對象

根據上面介紹的屬性凡资,對前面的 EventUtil 對象加以增強

var EventUtil = {
   // 注冊事件
  addHandler: function(element, type, handler) {
    if (element.addEventListener) { // 支持 addEventListener
      element.addEventListener(type, handler, false)
    }else if (element.attachEvent) { // IE
      element.attachEvent('on' + type, handler)
    } else { // 都不支持的情況下,默認使用 DOM0 級
      element['on' + type] = handler
    }
  },
  // 移除事件
  removeHandler: function(element, type, handler) {
    if (element.removeEventListener) { // 支持 DOM
      element.removeEventListener(type, handler, false)
    } else if (element.detachEvent) { // IE
      element.detachEvent('on' + type, handler)
    } else {
      element['on' + type] = null
    }
  },

  // 獲取 event 對象
  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.stopPropagation) {
      event.stopPropagation()
    } else {
      event.cancelBubble = true
    }
  }
}

使用

<body>

  <a href="www.baidu.com" id="link">點我</a>

<script src="./EventUtil.js"></script>
<script>

var link = document.getElementById('link')

var handler = function() {
  var event = EventUtil.getEvent() // 獲取兼容的 event 對象

  EventUtil.preventDefault(event)// 阻止默認行為

  var getTarget = EventUtil.getTarget(event) // 觸發(fā)事件的元素
  EventUtil.stopPropagation(event)// 阻止冒泡

  console.log('clicked') // clicked
  console.log(getTarget.tagName) // A
}

EventUtil.addHandler(link, 'click', handler)

document.body.onclick = function() {
  alert('clicked')
}
</script>

當點擊 a 標簽的時候谬运,不會觸發(fā) body 的點擊事件隙赁。只會輸出 'clicked' 及 'A'(標簽名)

四、事件類型

“DOM3級事件” 規(guī)定了以下幾類事件

  • UI(User Interface梆暖,用戶界面)事件伞访,當用戶與頁面上的元素交互時觸發(fā)
  • 焦點事件,當元素獲得或失去焦點時觸發(fā)
  • 鼠標事件轰驳,當用戶通過鼠標在頁面上執(zhí)行操作時觸發(fā)
  • 滾輪事件厚掷,當使用鼠標滾輪(或類似設備)時觸發(fā)
  • 文本事件弟灼,當在文檔中輸入文本時觸發(fā)
  • 鍵盤事件,當用戶通過鍵盤在頁面上執(zhí)行操作時觸發(fā)
  • 合成事件冒黑,當為IME(Input Method Editor田绑,輸入法編輯器)輸入字符時觸發(fā)
  • 變動(mutation)事件,當底層DOM結構發(fā)送變化時觸發(fā)

除了這幾類事件之外抡爹,HTML5也定義了一組事件掩驱,有些瀏覽器還會在DOM和BOM中實現其他專有事件,這些專有的事件一般都是根據開發(fā)人員需求定制的冬竟,沒有什么規(guī)范欧穴,因此不同瀏覽器的實現有可能不一致。

DOM3 級事件模塊在 DOM2級事件模塊基礎上重新定義了這些事件泵殴,也添加了一些新事件涮帘。包括IE9在內的所有主流瀏覽器都支持DOM2 級事件。IE9也支持DOM3級事件

4.1笑诅、UI 事件

UI事件指的是那么不一定于用戶操作有關的事件

  • load:當頁面完全加載后 window 上面觸發(fā)调缨,當所有框架都加載完畢時在框架集上面觸發(fā),當圖像加載完畢時在<img>元素上觸發(fā)苟鸯,或者當嵌入的內容加載完畢時在<object>元素上觸發(fā)
  • unload:當頁面完全卸載后再 window 上面觸發(fā)同蜻,當所有框架都卸載后再框架集上面觸發(fā),或者當嵌入的內容卸載完畢后在<object>上面觸發(fā)早处。
  • abort:當用戶停止下載過程時湾蔓,如果嵌入的頁面沒有加載完,則在<object>元素上面觸發(fā)
  • error:當發(fā)生 JavaScript 錯誤時在 window 上面觸發(fā)砌梆,當無法加載圖片時在 <img> 元素上面觸發(fā)默责,當無法加載嵌入內容時在 <object> 元素上面觸發(fā),或者當有一或多個框架無法加載時在框架集上面觸發(fā)咸包。
  • select:當用戶選擇文本框(<input> 或 <textarea>)中的一或多個字符時觸發(fā)桃序。
  • resize:當窗口或框架的大小變化時在window或框架上面觸發(fā)
  • scroll:當用戶滾動帶滾動條的元素中的內容時,在該元素上面觸發(fā)烂瘫。<body>元素中包含所有加載頁面的滾動條媒熊。

檢測 瀏覽器是否支持 DOM2級 或 DOM3級事件

var isSupportedDOM2Event = document.implementation.hasFeature('HTMLEvents', '2.0')
var isSupportedDOM3Event = document.implementation.hasFeature('UIEvent', '3.0') 
4.1.1、load事件

當頁面加載完成后(包括所有圖像坟比、JavaScript文本芦鳍、CSS文件等外部資源),就會觸發(fā)window 上面的 load 事件葛账。

第一種定義方式是使用如下所示的JavaScript代碼

EventUtil.addHandler(window, 'load', function() {
  alert('loaded!')
})

第二種指定 方式是通過為<body>元素添加一個 onload 特性

<body onload="alert('ok')">
</body>

實際上洪鸭,后面這種方法只是為了保證向后兼容的一種權宜之計娩脾,但所有瀏覽器都能很好地支持這種方式剃根,當然建議使用 JavaScript 的方式。


圖像上面也可以觸發(fā) load 事件贷祈,無論是在 DOM種的圖像元素還是在HTML中的圖像元素。
在HTML中為任何圖像指定onload事件處理程序

<img src="smile.gif" onload="alert('Image loaded!')" />

使用 JavaScript 實現

var img = document.getElementsByTagName('img')[0]
EventUtil.addHandler(img, 'load', function(event) {
  event = EventUtil.getEvent(event)
  alert(EventUtil.getTarget(event).src)
})

在創(chuàng)建新的<img>元素時喝峦,可以為其指定一個事件處理程序势誊,以便圖像加載完畢后給出提示。重要的是要在指定src屬性之前先指定事件


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'
})

新圖像元素不一樣要從添加到文檔后才開始下載愈犹,只要設置了 src 屬性 就會開始下載键科。

也可以通過使用 DOM0 級的 Image 對象實現闻丑′鲈酰可以像使用<img> 元素一樣使用 Image 對象,只不過無法將其添加到DOM樹中

EventUtil.addHandler(window, 'load', function() {
  var image = new Image()
  EventUtil.addHandler(image, 'load', function(event) {
    alert('Image loaded!')
  })
  image.src = 'smile.gif'
})

還有一些元素也以非標準的方式支持 load 事件嗦嗡。在IE9勋锤、Firefox、Opera侥祭、Chrome叁执、Safari3+及更高的版本中, <script> 元素和 <link> 元素也會觸發(fā)load事件矮冬,將它們添加到文檔并且設置了 src/href 屬性后谈宛,就會開始下載文件。

EventUtil.addHandler(window, 'load', function() {
  // script 元素
  var script = document.createElement('script')
  EventUtil.addHandler(script, 'load', function(event) {
    console.log('js loaded!')
  })
  script.src = 'example.js'
  document.body.appendChild(script)

  // link 元素
  var link = document.createElement('link')
  link.type = 'text/css'
  link.rel = 'stylesheet'
  EventUtil.addHandler(link, 'load', function(event) {
    console.log('css loaded!')
  })
  link.href = 'example.css'
  document.getElementsByTagName('head')[0].appendChild(link)
})
4.1.2胎署、unload 事件

與 load 事件對于的是 unload 事件吆录,這個事件在文檔被完全卸載后觸發(fā)。只要用戶從一個頁面切換到另一個頁面琼牧,就會發(fā)生 unload 事件恢筝。而利用這個事件最多的情況就是清除引用,以避免內存泄漏巨坊。

EventUtil.addHandler(window, 'unload', function(event) {
  alert('unload')
})

與 load事件 一樣撬槽,也可以 通過為 <body> 元素 添加 onunload 特性

需要注意的是:onload 事件是在一切都被卸載之后才觸發(fā),那么在頁面加載后存在的那些對象趾撵,此時就不一定存在了侄柔。此時,操作DOM節(jié)點或者元素的樣式就會導致錯誤

4.1.3 resize 事件

當瀏覽器窗口被調到一個新的高度或寬度時占调,就會觸發(fā) resize 事件暂题。這個事件在 window 上面觸發(fā),因此可以通過 JavaScript 或者 <body> 元素中 的 onresize 特性來制定事件處理程序妈候。

EventUtil.addHandler(window, 'resize', function(event) {
  console.log('onresize')
})

resize事件 可能會被頻繁執(zhí)行注意性能

4.1.4敢靡、scroll 事件

scroll 事件是在 window 對象上發(fā)生的,但它實際表示的則是頁面中相應元素的變化苦银。

EventUtil.addHandler(window, 'scroll', function(event) {
    console.log( document.body ? document.body.scrollTop : document.documentElement.scrollTop)
})

與resize 事件象實啸胧,scroll 事件也會在文檔被滾動期間重復被觸發(fā)赶站,所以有必要盡量保持事件處理程序的代碼簡單

4.2 焦點事件

焦點事件會在頁面元素獲得或失去焦點時觸發(fā)。利用這些事件并于 document.hasFocus() 方法及 document.activeElement 屬性配合使用纺念,可以知曉用戶在頁面上的行蹤

  • blur:在元素市區(qū)焦點時觸發(fā)贝椿。這個事件不會冒泡;所以瀏覽器都支持
  • focus:再元素獲得焦點時觸發(fā)陷谱。這個事件不會冒泡烙博;所以瀏覽器都支持它
  • focusin:再元素獲得焦點時觸發(fā)。這個事件與HTML事件focus扽火箭烟逊,但它毛冒泡渣窜。支持這個事件的瀏覽器有 IE5.5+、Safari5.1+宪躯、Opera11.5乔宿、Chrome、Firefox52+
  • focusout:在元素市區(qū)焦點時觸發(fā)访雪。這個事件時 HTML事件 blur的通用版详瑞。支持冒泡。支持這個事件的瀏覽器有 IE5.5+臣缀、Safari5.1+坝橡、Opera11.5、Chrome精置、Firefox52+

當焦點聰慧頁面的一個元素移動到另一個元素计寇,會依次觸發(fā)下列事件

  1. focusout 在失去焦點的元素上觸發(fā)
  2. focusin 在獲得焦點的元素上觸發(fā)
  3. blur 在失去焦點的元素上觸發(fā)
  4. focus 在獲得焦點的元素上觸發(fā)

檢測瀏覽器是否支持這些事件

var isSupported = document.implementation.hasFeature('FocusEvent', '3.0')

4.3、鼠標與滾輪事件

鼠標事件是 Web 開發(fā)中最常用的一類事件氯窍,畢竟鼠標還是最主要的定位設備饲常。DOM3 級事件中定義了 9個鼠標事件。如下:

  • click:在用戶單擊主鼠標按鈕(一般是左邊的按鈕)或者按下回車鍵時觸發(fā)狼讨。這一點對確保易訪問性很重要贝淤,意味著 onclick 事件處理程序既可以通過鍵盤也可以通過鼠標執(zhí)行。
  • dbclick:在用戶雙擊主鼠標按鈕(一般是左邊的按鈕)時觸發(fā)政供。從技術上說播聪,這個事件并不是DOM2級事件規(guī)范中規(guī)定的,但鑒于它得到了廣泛支持布隔,所以DOM3級事件將其納入了標準离陶。
  • mousedown:在用戶按下了任意鼠標按鈕時觸發(fā)。不能通過鍵盤觸發(fā)這個事件衅檀。
  • mouseenter:在鼠標光標從元素外部首次移動到元素范圍之內時觸發(fā)招刨。這個事件不冒泡,而且在光標移動到后代元素上不會觸發(fā)哀军。DOM2級事件并沒有定義 這個事件沉眶,但DOM3級事件將它納入了規(guī)范打却。IE、Firefox 9+谎倔、Opera支持這個事件柳击。
  • mouseleave:在位于元素上方的鼠標光標移動到元素范圍之外時觸發(fā)。這個事件不冒泡片习,而且在光標移動到后代元素上不會觸發(fā)捌肴。DOM2級事件并沒有定義 這個事件,但DOM3級事件將它納入了規(guī)范藕咏。IE状知、Firefox 9+、Opera支持這個事件侈离。
  • mousemove:當鼠標指針在元素內部移動時重復的觸發(fā)试幽。不能通過鍵盤觸發(fā)這個事件
  • mouseout:在鼠標指針位于一個元素上方,然后用戶將其移入另一個元素時觸發(fā)卦碾。又移入的另一個元素可能位于前一個元素的外部,也可能是這個元素的子元素起宽。不能通過鍵盤觸發(fā)這個事件洲胖。
  • mouseover:在鼠標指針位于一個元素外部,然后用戶將其首次移入另一個元素邊界之內時觸發(fā)坯沪。不能通過鍵盤觸發(fā)這個事件绿映。
  • mouseup:在用戶釋放鼠標按鈕時觸發(fā)。不能通過鍵盤觸發(fā)這個事件腐晾。

頁面上的所有元素都支持鼠標事件叉弦。處理mouseenter 和 mouseleave,所有鼠標事件都會冒泡藻糖,也可以被取消淹冰,而取消鼠標事件將會影響瀏覽器的默認行為。取消鼠標事件的默認行為還會影響其他事件巨柒,因為鼠標事件與其他事件是密不可分的關系樱拴。

在同一個元素上,上訴事件的觸發(fā)過程

  1. mousedown
  2. mouseup
  3. click
  4. mousedown
  5. mouseup
  6. click
  7. dbclick

click 和 dbclick 事件都會依賴于其他先行事件的觸發(fā)洋满;而 mousedown 和 mouseup 則不受其他事件的影響晶乔。IE8及之前有一個 bug,在雙擊事件中牺勾,會跳過第二個mousedown 和 click 事件正罢,IE9修復了這個bug

檢測瀏覽器是否支持上面所有的事件

var isSupported = document.implementation.hasFeature('MouseEvent', '3.0')

鼠標事件中還有一類滾輪事件,即滾輪事件驻民,其實就是一個 mousewheel 事件翻具。這個事件跟蹤鼠標滾輪袱饭,類似于Mac的觸控板。

4.3.1呛占、客戶區(qū)坐標位置

鼠標事件都是在瀏覽器視口中的特定位置上發(fā)生的虑乖。這個位置信息保存在事件對象的clientXclientY 屬性中。它們的值表示事件發(fā)生時鼠標指針在視口中水平和垂直坐標

客戶區(qū)坐標

可以使用類似下列代碼獲取鼠標事件的客戶端坐標信息

var div = document.getElementById('myDiv')
EventUtil.addHandler(div, 'click', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.clientX, event.clientY)
})
4.3.2晾虑、頁面坐標位置

通過事件對象的 pageXpageY 屬性疹味,表示鼠標光標在頁面中的位置,因此坐標是從頁面本身而非視口計算帜篇。

var div = document.getElementById('div')
EventUtil.addHandler(div, 'click', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.pageX, event.pageY)
})

在IE8及更早版本不支持事件對象上面的頁面坐標糙捺,,不過使用客戶區(qū)坐標和滾動信息可以計算出來笙隙。這時候需要用到document.body(混雜模式) 或 doucment.documentElement(標準模式)中的scrollLef 和 scrollTop屬性

var div = document.getElementById('div')
EventUtil.addHandler(div, 'click', function(event) {
  event = EventUtil.getEvent(event)
  var pageX = event.pageX
  var pageY = event.pageY

  if (pageX === undefined) {
    pageX = event.clienX + (document.body.scrollLeft || document.documentElement.scrollLeft)
  }
  if (pageY === undefined) {
    pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop)
  }

  console.log(pageX, pageY)
})

4.3.3洪灯、屏幕坐標位置相對于電腦屏幕的位置。通過 screenXscreenY

屬性就可以確定鼠標事件發(fā)生時鼠標指針相對于整個屏幕的坐標信息竟痰。


屏幕坐標位置
var div = document.getElementById('div')
EventUtil.addHandler(div, 'click', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.screenX, event.screenY)
})
4.3.4 修改鍵

雖然鼠標事件主要是使用鼠標來觸發(fā)的签钩,但在按下鼠標時鍵盤上的牟謝建的狀態(tài)也可以影響到所要采取的操作,這些鍵是Shift坏快、Ctrl铅檩、Alt、Meta(windows鍵/Cmd鍵)莽鸿。DOM為此規(guī)定了四個屬性:shiftKey昧旨、ctrlKey、altKey祥得、metaKey兔沃。這些屬性中包含的都是布爾值,如果相應的鍵被按下了级及,則值為true乒疏,否則值為false。

var div = document.getElementById('div')
EventUtil.addHandler(div, 'click', function(event) {
  event = EventUtil.getEvent(event)
  var keys = new Array()
  
  if (event.shiftKey) { // shift
    keys.push('shift')
  }
  if(event.ctrlKey) { // ctrl
    keys.push('ctrl')
  }
  if(event.altKey) { // alt
    keys.push('alt')
  }
  if (event.metaKey) { // meta
    keys.push('meta')
  }

  console.log(keys.join(', '))
})

IE8及之前不支持 metaKey 屬性

4.3.5创千、相關元素

在發(fā)生 mouseover 和 mouseout 事件時缰雇,還會涉及更多元素。這兩個是事件都會涉及把鼠標指針從一個元素的邊界之內移動到另一個元素的邊界之內追驴。

<body>
  <div id="myDiv" style="background-color: cyan; width:200px; height: 150px;"></div>
</body>

如上械哟,如果鼠標指針一開始位于這個<div>元素上,然后移除出了這個元素殿雪,那么就會在 <div> 元素上觸發(fā) mouseout 事件暇咆,相關元素就是 <body> 元素。與此同時,<body>元素上面會觸發(fā) mouseover 事件爸业,而相關元素變成了 <div>

DOM 通過 event 對象的 relatedTarget 屬性提供了相關元素的信息其骄。這個屬性只對于 mouseover、mouseout事件才包含值:對于其他事件扯旷,這個屬性的值 null拯爽。IE8之前版本不支持這個屬性(IE9支持),在mouseover事件觸發(fā)的時候钧忽,IE的 formElement 屬性中保存了相關元素毯炮;在mouseout 事件觸發(fā)時,IE的 toElement屬性中保存著相關元素耸黑。
將相關元素的方法添加到 EventUtil 對象中

var EventUtil = {
  getRelatedTarget: function(event) {
    if (event.relatedTarget) {
      return event.relatedTarget
    } else if (event.toElement) {
      return event.toElement
    } else if (event.fromElement) {
      return event.fromElement
    } else {
      return null
    }
  }
}

可以像下面這樣去使用 EventUtil.getRelatedTarget() 這個方法

var div = document.getElementById('div')
EventUtil.addHandler(div, 'mouseout', function(event) {
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event)
  var relatedTarget = EventUtil.getRelatedTarget(event)

  console.log('Moused out of ' + target.tagName + ' to ' + relatedTarget.tagName)
})

4.3.6桃煎、鼠標事件

只有在注釋表按鈕被單擊(或鍵盤回車鍵按下)時才會觸發(fā) click 事件,對于 mousedown 和 mouseup 事件來說大刊,則在其 event 對象存在一個 button 屬性为迈,表示按下或釋放的按鈕。DOM 的 button 屬性可能有 如下 3個值:

  • 0:表示主鼠標鍵(左鍵)
  • 1:表示中間的鼠標按鈕(鼠標滾輪)
  • 2:表示次鼠標按鈕(右鍵)

IE8及之前也提供了 與DOM button 有差異的 button 屬性缺菌。

  • 0:表示沒有按下按鈕
  • 1:表示按下了主鼠標按鈕葫辐。
  • 2:表示按下了次鼠標按鈕。
  • 3:表示同時按下了主男翰、次鼠標按鈕
  • 4:表示按下了中間的鼠標按鈕
  • 5:表示同時按下了主鼠標按鈕和中間的鼠標按鈕
  • 6:表示同時按下了次鼠標按鈕和中間的鼠標按鈕
  • 7:表示同時按下了三個鼠標按鈕

DOM模型下的 button 屬性比IE模型下的 button 屬性更簡單也更為實用另患,常見的做法就是將IE模型規(guī)范華為DOM方法,
由于單獨使用能力監(jiān)測無法確定差異(兩種模型有同名的button屬性)蛾绎,因此必須另辟蹊徑,我們知道鸦列,支持DOM版鼠標事件的瀏覽器可以通過hasFeature()方法監(jiān)測租冠,所以可以再為 EventUtil 對象 添加如下的 getButton() 方法。

var EventUtil = {
  // 省略了其他代碼
  getButton: function(event) {
    if (document.implementation.hasFeature('MouseEvents', '2.0')) { // 支持 Dom 版
      return event.button
    } else { // IE 低版本
      switch(event.button) {
        case 0:
        case 1:
        case 3:
        case 5:
        case 7:
          return 0
        case 2:
        case 6:
          return 2
        case 4:
          return 1
      }
    }
  }
}

使用該方法的實例:

var div = document.getElementById('div')
EventUtil.addHandler(div, 'mousedown', function(event) {
  event = EventUtil.getEvent(event)
  var button = EventUtil.getButton(event)
  console.log(button)
})

在<div> 元素中按下鼠標按鈕時薯嗤,會輸出對應的數值

4.2.7顽爹、更多的事件信息

“DOM2級事件” 規(guī)范在event 對象中還提供了 detail 屬性,用于給出有關事件更多的信息骆姐。對于鼠標事件來說镜粤,detail 中包含了一個數值,表示在給定位置上發(fā)生了多少次單擊玻褪。在同一元素上相繼地發(fā)生一次 mousedown 和 mouseup 事件后算作一次單擊肉渴。detail 屬性從1 開始計數,每次單擊發(fā)生后都會遞增带射。如果鼠標在 mousedown 和 mouseup 之間移動了位置同规,則detail 會被重置為0
IE 也通過下列屬性為鼠標事件提供更多信息。

  • altLeft:布爾值,表示是否按下了Alt鍵券勺。如果altleft 的值為true绪钥,則altKey 的值也為 true。
  • ctrlLeft:布爾值关炼,表示是否按下了 Ctrl 鍵程腹。如果 ctrlLeft 的值為 true,則 ctrlKey 的值也為 true
  • offsetX:光標相對于目標元素邊界的X坐標
  • offsetY:光標相對于目標元素邊界的Y坐標
  • shiftLeft:布爾值儒拂,表示是否按下了Shift鍵寸潦。如果shiftLeft的值為 true,則 shiftKey 的值也為 true侣灶。
    這些屬性的用處并不大甸祭,原因一方面只是有IE支持他們,另一方面它們提供的信息要么 沒有什么價值褥影,要么可以通過其他方式計算得來池户。
4.3.8、鼠標滾輪事件

IE 6.0 首先實現了 mousewheel 事件凡怎。與 mousewheel事件對應的 event 對象除包含鼠標事件的所有標準信息外校焦,還包含一個特性的wheelDelta屬性。當向前滾動鼠標滾輪時统倒,wheelDelta 是120的倍數寨典;向后滾動鼠標滾輪時,wheelDelta 是 -120的倍數房匆。

EventUtil.addHandler(document, 'mousewheel', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.wheelDelta)
})

多數情況下耸成,只要知道鼠標滾輪滾動的方向就夠了,而這通過檢測 wheelDelta 的正負號就可以確定浴鸿。


有一點需要注意:在 Opera 9.5 之前的版本中井氢,wheelDelta 值的正負號是顛倒的。如果打算支持早期的 Opera 版本岳链,就需要使用瀏覽器檢測技術來確定實際的值花竞,

// ...
var delta = (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta)

上面用到的 client, 在前面的客戶端檢測中有提到掸哑,檢測瀏覽器是不是早前的 Opera


Firefox 支持一個名為 DOMMouseScroll 的類似事件约急,也是在鼠標滾輪滾動時觸發(fā)。與mouseWhell事件一樣苗分,包含與鼠標事件有關的所有特性厌蔽。有關鼠標滾輪的信息則保存在 detail 屬性中,當向前滾動鼠標滾輪時俭嘁,這個屬性的值是 -3 的倍數躺枕,當向后滾動鼠標滾輪時,這個屬性的值是 3 的倍數

EventUtil.addHandler(document, 'DOMMouseScroll', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.detail)
})

測試后返回的 7(向下) 和 -7(向上)


要給出跨瀏覽器環(huán)境下的解決方案,第一步是創(chuàng)建一個能夠取得鼠標滾輪增量值(delta)的方法拐云。

  // 獲取 鼠標滾輪 增量值 (delta)
  getWheelDelta: function(event) {
    var detail = 0
    if (event.wheelDelta) {
      detail = client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta
    } else {
      detail = -event.detail
    }

    return detail > 0 ? -1 : 1
  }

由于在實際測試時罢猪,兩個屬性給出的值飄忽不定,所以這里只返回 -1 或 1 編碼滾動的方法即可叉瘩,-1代表向 頁面向下滾動膳帕, 1 代表頁面向上滾動
使用

var mouseWheelHandler =  function(event) {
  event = EventUtil.getEvent(event)
  var delta = EventUtil.getWheelDelta(event)

  console.log(delta)
}
EventUtil.addHandler(document, 'DOMMouseScroll',mouseWheelHandler)
EventUtil.addHandler(document, 'mousewheel'mouseWheelHandler)

如上:在多個瀏覽器下,都會有一致的返回結果

4.3.9薇缅、觸摸設備

iOS 和 Android 設備的實現非常特別危彩,因為這些設備沒有鼠標。在面向iPhone 和 iPad 中的 Safari 開發(fā)時泳桦,要記住以下幾點

  • 不知此 dbclick 事件汤徽。雙擊瀏覽器窗口會放大畫面,而且沒有辦法改變該行為
  • 輕擊可單機元素會觸發(fā) mousemove 事件灸撰。如果此操作會導致內容變化谒府,將不再有其他事件發(fā)生;如果屏幕沒有因此變化浮毯,那么會依次發(fā)生 mousedown完疫、mouseup、click事件债蓝。輕擊不可單機元素不會觸發(fā)任何事件厘贼÷嗉校可單機元素是指那些單機可產生默認操作的元素(如鏈接),或者那些已經被指定了 onclick 事件處理程序的元素
  • mousemove 事件也會觸發(fā) mouseover 和 mouseout 事件
  • 兩個手指放在屏幕上且頁面隨手指移動而滾動時會觸發(fā) mousewheel 和 scroll 事件
4.3.10浓利、無障礙性問題

Web應用程序或網站要確保殘疾人特別是那些使用屏幕閱讀器的人能夠訪問执解,那么在使用鼠標事件時就要格外小心嗜桌。通過鍵盤上的回車鍵可以觸發(fā) click 事件甸私,但其它鼠標事件無法通過鍵盤來觸發(fā)荣挨。為此,我們不建議使用 click 之外的其他鼠標事件來展示功能或引發(fā)代碼執(zhí)行莉掂。因為這樣會給盲人或障礙用戶造成極大不方便。以下是在使用鼠標事件時應該注意的幾個易訪問性問題千扔。

  • 使用 click事件執(zhí)行代碼憎妙。有人指出通過 onmousedown 執(zhí)行代碼會讓人絕對速度更快,對視力正常的人來說這是沒錯的曲楚。但是厘唾,在屏幕閱讀器中,由于無法觸發(fā)mousedown 事件龙誊,結構就會造成代碼無法執(zhí)行
  • 不要使用 onmouseover 向用戶顯示新的選項抚垃。原因同上,屏幕閱讀器無法觸發(fā)這個事件。如果確實非要通過這種方式來顯示新選項鹤树,可以考慮添加顯示相同信息的鍵盤快捷方式铣焊。
  • 不要使用 dbclick 執(zhí)行重要操作。鍵盤無法觸發(fā)這個事件罕伯。

遵照以上提示可以極大地提升殘疾人在訪問你的 Web 應用程序或網站時的 易訪問性

4.4曲伊、鍵盤與文本事件

用戶在使用鍵盤時會觸發(fā)鍵盤事件∽匪“DOM3級事件”為鍵盤事件制定了規(guī)范坟募,IE9率先完全實現了該規(guī)范。

有3個鍵盤事件邑狸,簡述如下

  • keydown:當用戶按下鍵盤上的任意鍵時觸發(fā)懈糯,而且如果按住不放的話,會重復觸發(fā)此事件
  • keypress:當用戶按下鍵盤上的字符鍵時觸發(fā)单雾,而且如果按住不放的話赚哗,會重復觸發(fā)此事件。按下Esc 鍵也會觸發(fā)這個事件铁坎。Safari 3.1之前的版本也會在用戶按下非字符鍵時觸發(fā) keypress 事件蜂奸。
  • keyup:當用戶釋放鍵盤上的鍵時觸發(fā)

雖然所有元素都支持以上3個元素,但只有在用戶通過文本框輸入時才最常用到

此外還有一個文本事件:textInput硬萍。這個事件是對 keypress 的補充扩所,用意是在將文本顯示給用戶之前更容易攔截文本。在文本插入文本框之前會觸發(fā) textInput 事件

如果用戶按下一個字符鍵不放朴乖,就會重復觸發(fā) keydown 和 keypress 事件祖屏,直到用戶松開改建為止。
如果按住這個非字符鍵不放买羞,那么就會一直重復觸發(fā) keydow 事件

4.4.1袁勺、鍵碼

在發(fā)生keydown、keyup事件時畜普,event 對象的 keyCode 屬性中會包含一個代碼期丰,與鍵盤上一個特定的鍵對應。DOM 和 IE 的 event對象都支持這個屬性

var textBox = document.getElementById('myText')
EventUtil.addHandler(textbox, 'keydown', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.keyCode)
})

無論是 keydown 或 keyup 事件都會存在一些特殊情況吃挑。在Firefox 和 Opera 中钝荡,按分號鍵時 keyCode 值為 59,也就是 ASCII 中分號的編碼舶衬;但IE 和 Safari 返回 186埠通,即鍵盤中按鍵的鍵碼。

4.4.2逛犹、字符編碼

在所有瀏覽器中端辱,按下能夠插入或刪除字符的鍵都會觸發(fā) keypress 事件梁剔;按下其他鍵能否發(fā)次事件因瀏覽器而異。
IE9舞蔽、Firefox荣病、Chrome、Safari 的 event 對象都支持一個 charCode 屬性喷鸽,這個屬性只有在發(fā)生 keypress 事件時才包含值众雷,這個值是按下的那個鍵所代表字符的 ASCII 編碼。此時的 keyCode 通常等于 0 或者 也可能等于所按鍵的鍵碼做祝。IE8及之前版本和 Opera 則是在 KeyCode 中保存字符的 ASCII 編碼砾省。
以跨瀏覽器的方式取得字符編碼

var EventUtil = {
  // ...
  getCharCode: function(event) {
    if (typeof event.charCode == 'number') {
      return event.charCode
    } else {
      return event.keyCode
    }
  }
}

使用這個方法

var textbox = document.getElementById('myText')
EventUtil.addHandler(textbox, 'keypress', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.keyCode)
  console.log(event.charCode)
})

在取得了字符編碼之后,就可以使用 String.fromCharCode() 將其轉換成實際的字符混槐。

4.4.3编兄、DOM3 級變化

盡管所有瀏覽器都實現了某種形式的鍵盤事件,DOM3級 事件做出了一些改變声登。比如狠鸳,DOM3級事件中的鍵盤事件,不在包含 charCode 屬性悯嗓,而是包含兩個新屬性:key 和 code(已廢棄)

key 屬性是為了取代 keyCode 而新增的件舵,它的值是一個字符串。在線下非字符鍵時脯厨,key的值是相應鍵的名

由于跨瀏覽器問題铅祸,目前不是推薦使用 key(兼容問題)、keyIdentifier(已棄用)合武、char(已棄用)临梗,在MDN上同時提到 keyCode 也不推薦使用,并給出了如下的推薦代碼

window.addEventListener("keydown", function (event) {
  if (event.defaultPrevented) { // 檢測是否設置了 preventDefault()
    return; // Should do nothing if the default action has been cancelled
  }

  var handled = false;
  if (event.key !== undefined) { // 支持 key
    // Handle the event with KeyboardEvent.key and set handled true.
  } else if (event.keyCode !== undefined) { // 支持 keyCode
    // Handle the event with KeyboardEvent.keyCode and set handled true.
  }

  if (handled) { // 都不支持
    // Suppress "double action" if event handled
    event.preventDefault();
  }
}, true);

DOM3級事件還添加了 location 屬性稼跳,這是一個數值盟庞,表示按下了什么位置上的鍵

  • 0:默認鍵盤
  • 1:表示左側位置(例如左位的Alt鍵)
  • 2:表示右側位置(例如右側的Shift鍵)
  • 3:表示數字小鍵盤
  • 4:表示移動設備鍵盤(也就是虛擬鍵盤)
  • 5:表示手柄

IE9支持這個屬性。Safari 和 Chrome 支持名為 keyLocation 的等價屬性汤善,但有bug——值始終是 0什猖,除非按下了數字鍵盤(3);否則红淡,不會是1卸伞、2、4锉屈、5

var textbox = document.getElementById('myText')
EventUtil.addHandler(textbox, 'keypress', function(event) {
  event = EventUtil.getEvent(event)
  var loc = (typeof event.location === 'number') ? event.location : event.keyLocation
  if (typeof loc === 'number') {
    console.log(loc)
  }
})

與 key 屬性一樣,支持 location的瀏覽器也不多垮耳,所以在跨瀏覽器開發(fā)中不推薦使用颈渊。


getModifierState()方法遂黍。這個方法接收一個參數,即等于Shift俊嗽、Control雾家、AltGraph、Meta的字符串绍豁,表示要檢測的修改鍵芯咧。如果指定的修改鍵是活動的(也就是處于被按下的狀態(tài)),這個方法就會返回true竹揍,否則返回false

var textbox = document.getElementById('myText')
EventUtil.addHandler(textbox, 'keypress', function(event) {
  event = EventUtil.getEvent(event)
  if (event.getModifierState) {
    console.log(event.getModifierState('Shift'))
  }
})

實際上敬飒,通過 event 對象的 shiftKey、altKey芬位、ctrlKey无拗、metaKey屬性以及可以取得類似的屬性了。

4.4.4昧碉、textInput 事件

“DOM3級事件” 規(guī)范中引入了一個新事件英染,名叫 textInput。當用戶在可編輯區(qū)域中輸入字符時被饿,就會觸發(fā)這個事件四康。這個事件與 keypress 之間有如下區(qū)別。

  1. 任何可以獲得焦點的元素都可以觸發(fā) keypress 事件狭握,但只有可編輯區(qū)域才能觸發(fā) textInput 事件闪金。
  2. textInput 事件只會在用戶按下能夠輸入實際支付的鍵時才會被觸發(fā),而 keypress 事件則在按下那么能夠影響文本顯示的鍵也會觸發(fā)

由于 textInput 事件主要考慮的是字符哥牍,因此它的 event 對象中還包含一個 data 屬性毕泌,這個屬性的值就是用戶輸入的字符。

var textbox = document.getElementById('myText')
EventUtil.addHandler(textbox, 'textInput', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.data)
})

event 對象上還有一個屬性嗅辣,叫 inputMethod(查不到撼泛,應該是廢棄了),表示把文本輸入到文本框中的方式

4.4.5澡谭、設備中的鍵盤事件

任天堂Wii 會在用戶按下 Wii 遙控器上的按鍵時觸發(fā)鍵盤事件愿题。盡管沒有辦法訪問 Wii遙控器中所有案件,但還是有一些鍵可以觸發(fā)事件蛙奖。

當用戶按下十字鍵盤(鍵碼為175~178)潘酗、減號(170)、加號(174)雁仲、1(172)仔夺、2(173)鍵時就會觸發(fā)鍵盤事件。但沒有辦法得知用戶是否按下了電源開發(fā)攒砖、A缸兔、B或主頁鍵

4.5日裙、復合事件

復合事件(composition event)是DOM3級事件中添加的一類事件,用于處理IME的輸入序列IME(Input Method Editor惰蜜,輸入法編輯器)可以讓用戶輸入在物理鍵盤上找不到的字符昂拂。

有以下三種復合事件

  • compositionstart:在IME的文本復合系統(tǒng)開發(fā)時觸發(fā),表示要開始輸入了
  • compositionupdate:在向輸入字段中插入新字符時觸發(fā)
  • compositionend:在IME的文本復合系統(tǒng)關閉時觸發(fā)抛猖,表示返回正常鍵盤輸入狀態(tài)

復合事件與文本事件在很多方法都很相似格侯。在觸發(fā)復合事件時,目標是接收文本的輸入字段财著。但它比文本事件的事件對象多一個屬性data联四,其中包含以下幾個值中的一個:

  • 如果在 compositionstart 事件發(fā)生時訪問,包含正在編輯的文本(例如瓢宦,已經選中的需要馬上替換的文本)
  • 如果在 compositionupdate 事件發(fā)生時訪問碎连,包含正插入的新字符
  • 如果在 compositionend 事件發(fā)生時訪問,包含此次輸入會話插入的所有字符
var textbox = document.getElementById('myText')
EventUtil.addHandler(textbox, 'compositionstart', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.data)
})

EventUtil.addHandler(textbox, 'compositionupdate', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.data)
})

EventUtil.addHandler(textbox, 'compositionend', function(event) {
  event = EventUtil.getEvent(event)
  console.log(event.data)
})

這個方法有一定的兼容問題驮履,低版本瀏覽器大多不支持鱼辙,可以使用以下代碼檢測瀏覽器是否支持復合事件

var isSupported = document.implementation.hasFeature('CompositionEvent', '3.0')

4.6、變動事件

DOM2級 的變動(mutation)事件及能在DOM中的某一部分發(fā)生變化時給出提示玫镐。變動事件是為XML或 HTML DOM 設計的倒戏,并不特定于某種語言。DOM2級定義了如下變動事件

  • DINSubtreeNodified:在DOM結構中發(fā)生任何變化時觸發(fā)恐似。這個事件在其他任何事件觸發(fā)后都會觸發(fā)
  • DOMNodeInserted:在一個節(jié)點作為子節(jié)點被插入到另一個節(jié)點中時觸發(fā)杜跷。
  • DOMNodeInsertedIntoDocument:在一個節(jié)點被直接插入文檔或通過子數間接插入文檔之后觸發(fā)。這個事件在DOMNodeInserted之后觸發(fā)矫夷。
  • DOMNodeRemovedFromDocument:在一個節(jié)點被直接從文檔中移除或通過子樹間接從文檔中移除之前觸發(fā)葛闷。這個事件在 DOMNodeRemoved 之后觸發(fā)。
  • DOMAttrModified:在特性被修改之后觸發(fā)双藕。
  • DOOMCharacterDataModified:在文本節(jié)點的值發(fā)生變化時觸發(fā)淑趾。

這些方法在不久的將來會廢棄,所以不推薦使用了忧陪。這里簡單的提一下

4.7扣泊、HTML5事件

DOM 規(guī)范沒有涵蓋所以瀏覽器支持的所有事件。HTML5 詳盡列出了瀏覽器應該支持的所有事件嘶摊。本節(jié)只討論其中得到瀏覽器玩上支持的事

4.7.1延蟹、contextmenu 事件

用以表示何時應該顯示上下文菜單(在瀏覽器中鼠標右鍵打開的菜單列表),以便開發(fā)人員取消默認的上下文菜單而提高自定義的菜單叶堆。
這個事件的目標是發(fā)生用戶操作的元素阱飘。在所有瀏覽器中都可以取消這個事件:在兼容DOM的瀏覽器中,使用 event.preventDefault();在IE中俯萌,將event.returnValue 的值設置為false

<body>
  <div id="myDiv"> Right click or Ctrl+click me to get a custom context menu. Click anywhere else to get the default conetext menu.</div>
<ul id="myMenu" style="position:absolute; visibility:hidden;background-color:silver">
  <li><a href="#">item 1</a></li>
  <li><a href="#">item 2</a></li>
  <li><a href="#">item 3</a></li>
</ul>
</body>

以上面的 HTML 結構為例

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'
})

這個例子很簡單果录,但它卻展示了 Web 上所有自定義上下文菜單的基本結構。只需為這個例子中的上下文菜單添加一些css樣式咐熙,就可以得到非常的效果

4.7.2、beforeunload 事件

這個事件會在瀏覽器卸載頁面之前觸發(fā)辨萍,可以通過它來取消卸載并繼續(xù)使用原有頁面棋恼。但是,不能徹底取消這個事件锈玉,因為那就相當于讓用戶無法離開當前頁面爪飘。為此,這個事件的意圖是將控制權交給用戶拉背,顯示的消息會告知用戶頁面將被卸載

為了顯示對話框师崎,提示用戶,必須將 event.returnValue 的值設置為要顯示給用戶的字符串(對IE及Firefox而言)椅棺,同時作為函數的值返回(對Safari和Chrome)

EventUtil.addHandler(window, 'beforeunload', function(event) {
  
  event = EventUtil.getEvent(event)
  var message = 'are you sure' 
  event.returnValue = message
  return message
})
4.7.3犁罩、DOMContentLoaded 事件

DOMContentLoaded事件在形成完整的DOM樹之后就會觸發(fā),不理會圖像两疚、JavaScript文件床估、CSS文件或其他資源是否下載完畢。與load事件不同诱渤,DOMContentLoaded 支持在頁面下載的早期添加事件處理程序丐巫,這也就意味著用戶能夠盡早地與頁面進行交互
可以為 document 或 window 添加相應的事件處理程序(盡管這個事件會冒泡到 window,但它的目標實際上是 document)

EventUtil.addHandler(window, 'DOMContentLoaded', function(event) {
  alert('Content loaded')
})

這個事件始終都會在load事件之前觸發(fā)勺美,支持這個屬性的有 IE9+递胧、Firefox、Chrome赡茸、Safari3.1+缎脾、Opera9+

4.7.4、readystatechange 事件

提供與文檔或元素的加載狀態(tài)有關的信息坛掠,支持 readystatechange 事件的每個對象都有一個 readyState 屬性赊锚,可能包含下列5個值中的一個。

  • uninitialized(未初始化):對象存在但尚未初始化
  • loading(正在加載):對象正在加載數據
  • loaded(加載完畢):對象加載數據完成
  • interactive(交互):可以操作對象了屉栓,但還沒有完全加載
  • complete(完成):對象已經加載完畢舷蒲。

但并非所有對象都會經歷 readyState 的這幾個階段。如果某個階段不適用某個對象友多,則該對象完全可能跳過該階段牲平;
對于 document 而言,值為"interactive"的 readyState 會在與 DOMContentLoaded 大致相同的時刻觸發(fā) readystatechange 事件域滥。此時纵柿,DOM數已經加載完畢蜈抓,可以安全操作它了,因此就會進入交互(interactive)階段昂儒。

EventUtil.addHandler(document, 'readystatechange', function(event) {
  if (document.readyState == 'interactive' ) alert('Content loaded')
})

為了盡可能搶到先機沟使,有必要同時檢測交互和完成階段

EventUtil.addHandler(document, 'readystatechange', function(event) {
  if (document.readyState == 'interactive' || document.readyState == 'complete') {
    EventUtil.removeHandler(document, 'readystatechange', arguments.callee)
    alert('Content loaded')
  }
})

當 readystatechange事件觸發(fā)時,會檢測 document.readyState 的值渊跋,看當前是否已經進入交互階段或完成階段腊嗡。如果是,則移除相應的事件處理程序以及在其他階段再執(zhí)行拾酝。


此外燕少,<script>(在IE11以下和Opera中)和<link>(僅IE11以下中)元素也會觸發(fā) readystatechange 事件,可以用來確定外部的 JavaScript 和 css 文件是否已經加載完成蒿囤。

EventUtil.addHandler(window, 'load', function() {

  // 加載 JavaScript 文件
  var script = document.createElement('script')

  EventUtil.addHandler(script, 'readystatechange', function(event) {
    console.log(1)
    event = EventUtil.getEvent(event)
    var target = EventUtil.getTarget(event)

    if (target.readyState == 'loaded' || target.readyState == 'complete') {
      EventUtil.removeHandler(target, 'readystatechange', arguments.callee)
      alert('Script Loaded')
    }
  });

  script.src = 'client.js'
  document.body.appendChild(script)


  // 加載 css文件
  var link = document.createElement('link')
  link.type = 'text/css'
  link.rel = 'stylesheet'

  EventUtil.addHandler(link, 'readystatechange', function(event) {
    event = EventUtil.getEvent(event)
    var target = EventUtil.getTarget(event)

    if (target.readyState == 'loaded' || target.readyState == 'complete') {
      EventUtil.removeHandler(target, 'readystatechange', arguments.callee)
      alert('CSS Loaded')
    }
  })
  link.href = 'example.css'
  document.getElementsByTagName('head')[0].appendChild(link)
})
4.7.5客们、 pageshow 和 pagehide 事件

Firefox 和 Opera 有一個特性,名叫“往返緩存”(back-forward cache材诽,或 bfcache)底挫,用戶使用瀏覽器的 “后退” 和 “前進” 按鈕時加快頁面的轉換速度。這個緩存中不僅包含著頁面數據岳守,實際上是將整個頁面都保存在了內存里凄敢。如果頁面位于 bfcache 中,那么再次打開該頁面就不會觸發(fā) load 事件湿痢。

pageshow涝缝,這個事件在頁面顯示時觸發(fā),無論該頁面是否來自 bfcache譬重,在重新加載的頁面中拒逮,pageshow 會在 load 事件觸發(fā)后觸發(fā);而對于 bfcache 中的頁面臀规,pageshow 會在頁面狀態(tài)完全恢復的的那一刻觸發(fā)滩援。雖然這個事件的目標是document,但必須將其事件處理程序加到 window塔嬉。

除了通常屬性外玩徊,pageshow 事件中的 event 對象還包含一個名為persisted的布爾值屬性,如果頁面被保存在了 bfcache 中谨究,這個屬性則為true恩袱,反之為 false

;(function() {
  var showCount = 0
  EventUtil.addHandler(window, 'load', function(event) {
    console.log('Load fired')
  })

  EventUtil.addHandler(window, 'pageshow', function(event) {
    showCount++
    console.log(event.persisted)
    console.log('show has been fired ' + showCount)
  })
})()

pagehide會在瀏覽器卸載頁面的時候觸發(fā),而且是在unload事件之前觸發(fā)胶哲。與pageshow 事件一樣畔塔,pagehide 在 document 上面觸發(fā),但其事件處理程序必須要添加到 window 對象。

EventUtil.addHandler(window, 'pagehide', function(event) {
  alert('Hiding. Persisted? ' + event.persisted )
})

對于 pagehide 事件澈吨,如果頁面在卸載之后被保存在 bfcache 中把敢,那么 persisted 的值也會被設置為 true。因此谅辣,當第一次觸發(fā) pageshow 時修赞,persisted 的值一定是 false,而在第一次觸發(fā) pagehide 時桑阶,persisted 就會變成 true榔组。

支持 pageshow 和 pagehide事件的瀏覽器有 Firefox、Safari5+联逻、Chrome、Opera检痰,IE9及之前版本不支持這兩個事件

4.7.6包归、hashchange 事件

*hashchange8 事件,在URL的參數列表(及URL中“#” 號謀面的所有字符串發(fā)送變化時通知開發(fā)人員)铅歼,必須把 hashchange 事件處理程序添加給 window 對象公壤,然后URL 參數列表只要變化就會調用它。此時的event 對象應該 額外包含兩個屬性:oldURL 和 newURL椎椰。

EventUtil.addHandler(window, 'hashchange', function(event) {
  console.log(event.oldURL)
  console.log(event.newURL)
})

支持 hashchange 事件的瀏覽器有 IE8+厦幅、Firefox3.6+、Safari5+慨飘、Chrome确憨、Opera10.6+。這些瀏覽器中瓤的,只有 Firefox6+休弃、Chrome和Opera支持 oldURL 和 newURL屬性,因此圈膏,最好還是使用 location對象來確定當前的參數列表塔猾。

可以用一下代碼,檢測瀏覽器是否支持 hashchange 事件

var isSupported = ('onhashchange' in window) && (document.documentMode === undefined || document.documentMode > 7)

4.8稽坤、設備事件

智能手機和平板電腦的普及,及用戶與瀏覽器交互引入了一種新的方式,而一類新事件也應運而生蓝翰。設備事件(device event)可以讓開發(fā)人員確定用戶在怎樣使用設備锣吼。

4.8.1、orientationchange 事件

orientationchange 事件茫多,能夠確定用戶何時將設備由橫向查看模式切換為縱向查看模式祈匙、移動Safari的 window,orientation 屬性可能包含三個值:

  • 0:表示肖像模式
  • 90: 表示向左旋轉的橫向模式(“主屏幕”按鈕在右側)
  • -90:表示向右旋轉的橫向模式(“主屏幕”按鈕在左側)

只要用戶改變了設備的查看模式,就會出發(fā) orientationchange 事件夺欲,唯一的相關信息可以通過 window.orientation 訪問到跪帝。

EventUtil.addHandler(window, 'load', function(event) {
  var div = document.getElementById('myDiv')
  div.innerHTML = 'Current orientation is ' + window.orientation

  EventUtil.addHandler(window, 'orientationchange', function(event) {
    div.innerHTML = 'Current orientation is ' + window.orientation
  })
})

所有 iOS 設備都支持 orientationchange 事件 和 window.orientation 屬性

4.8.2、deviceorientation 事件

deviceorientation事件時在加速加速計檢測到設備防腐變化時在 window 對象上觸發(fā)
設備在三維空間中時靠x些阅、y 和 z軸來定位的伞剑。當設備禁止放在水平表示上時,這三個值都是0市埋。x軸方向是從左往右黎泣,y軸方向是從上往下,z軸方向是從前往后

觸發(fā) deviceorientation 事件時缤谎,事件對象中包含著每個軸相對于設備靜止狀態(tài)下發(fā)生變化的信息抒倚。事件對象包含以下5個屬性。

  • alpha:在圍繞z軸旋轉時(即左右旋轉時)坷澡,y軸的度數差托呕;是一個介于0到360之間的浮點數
  • beta:在圍繞x軸旋轉時(即前后旋轉時),z軸的度數差频敛;是一個介于-180到180之間的浮點數
  • gamma:在圍繞y軸旋轉時(即扭轉設備時)项郊,z軸的度數差;是一個介于-90到90之間的浮點數斟赚。
  • absolute:布爾值着降,表示設備的指南針是否校驗過
  • compassCalibrated:布爾值,表示設備的指南針是否校準過拗军。

通過這些信息任洞,可以響應設備的方法,重新排列或修改屏幕上的元素食绿。要響應設備方向的改變而旋轉元素侈咕,可以參看如下代碼

EventUtil.addHandler(window, "deviceorientation", function(event) {
  var arrow = document.getElementById('arrow')
  arrow.style.webkitTransfrom = 'rotate('+ Math.round(event.alpha) + 'deg)'
})

元素“arrow” 會隨著 event.alpha值的變化而選擇,給人一種指南針的感覺器紧。為了保證循旋轉平滑耀销,這里CSS3變換使用了 舍入之后的值

4.8.3、 devicemotion 事件

devicemotion 事件铲汪。這個事件時要告訴開發(fā)人員設備什么時候移動熊尉,而不僅僅是設備方向如何改變。通過 devicemotion 能夠檢測到設備是不是正在 往下掉掌腰,或者是不是被走著的人拿在手里
觸發(fā) devicemotion 事件時狰住,事件對象包含以下屬性。

  • acceleration:一個包含X齿梁、Y 和 Z 屬性的對象催植,在不考慮重力的情況下肮蛹,告訴你在每個方法上的加速度。
  • accelerationIncludingGravity:一個包含x创南、y伦忠、z屬性的對象,在考慮z軸自然重力加速度的情況下稿辙,告訴你在每個方向上的加速度昆码。
  • interval:以毫秒表示的時間值,必須在另一個 devicemotion 事件出發(fā)前傳入邻储。這個值在每個事件中應該是一個常量
  • rotationRate:一個包含表示方向的 alpha赋咽、beta、gamma 屬性的對象

如果讀取不到 acceleration吨娜、accelerationIncludingGravivty脓匿、rotationRate值,則他們的值為null宦赠。

EventUtil.addHandler(window, 'devicemotion', function(event) {
  var output = document.getElementById('output')
  if (event.rotationRate !== null) {
    output.innerHTML += "Alpha=" + event.rotationRate.alpha + ",  Beta=" +
                                   event.rotationRate.beta + ", Gamma=" +
                                   event.rotationRate.gamma
  }
})

4.9亦镶、觸摸與手勢事件

iOS版 Safari 為了向開發(fā)人員 傳達一些特殊信息,新增了一些專有事件袱瓮。W3C制定了 Touch Events 規(guī)范,以下介紹的事件只針對觸摸設備爱咬。

4.9.1尺借、觸摸事件

包含 iOS 2.0 軟件的 iPhone 3G 發(fā)布時,也包含了一個新版本的 Safari 瀏覽器精拟。這款新的移動Safari 提供了一些與 觸摸(touch) 操作相關的事件燎斩。后來,Android 上的瀏覽器也實現了相同的事件蜂绎。具體來說栅表,有以下幾個觸摸事件。

  • touchstart:當手指觸摸屏幕時觸發(fā)师枣;即使已經有一個手指放在了屏幕上也會觸發(fā)怪瓶。
  • touchmove:當手指在屏幕上滑動時連續(xù)地觸發(fā)。在這個事件發(fā)生期間践美,調用preventDefault() 可以阻止?jié)L動洗贰。
  • touchend:當手指從屏幕上移開時觸發(fā)
  • touchcancel:當系統(tǒng)停止跟蹤觸摸時觸發(fā)。

上面的幾個事件都會冒泡
除了常見的DOM屬性外陨倡,觸摸事件還包含下列三個用于跟蹤觸摸的屬性

  • touches:表示當前跟蹤的觸摸操作的 Touch 對象的數組
  • targetTouchs:特定于事件目標的 Touch 對象的數組
  • changeTouches:表示自上次觸摸以來發(fā)生了什么改變的 Touch 對象的數組

每個 Touch 對象包含下列屬性

  • clientX:觸摸目標在視口中的x坐標
  • clientY:觸摸目標在視口中的y坐標
  • identifier:標識觸摸的唯一ID
  • pageX:觸摸目標在頁面中的x坐標
  • pageY:觸摸目標在頁面中的y坐標
  • screenX:觸摸目標在屏幕中的x坐標
  • screenY:觸摸目標在屏幕中的y坐標
  • target:觸摸的DOM節(jié)點目標

使用這些屬性就可以跟蹤用戶對屏幕的接觸操作敛滋。

function handleTouchEvent(event) {

  // 值跟蹤一次觸摸
  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].clentY + ")"
        break;
      case 'touchend':
        output.innerHTML = "<br> Touch ended (" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")"
        break;
      case 'touchmove':
        event.preventDefault() // 阻止默認滾動
        output.innerHTML += "<br> Touch moved (" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")"
        break;
    }
  }
}

EventUtil.addHandler(document, 'touchstart', handleTouchEvent)
EventUtil.addHandler(document, 'touchend', handleTouchEvent)
EventUtil.addHandler(document, 'touchmove', handleTouchEvent)
4.9.2、手勢事件

iOS2.0中的 Safari 還引入了一組手勢事件兴革。當兩個手指觸摸屏幕時就會產生手勢绎晃,手勢通常會改變顯示項的大小蜜唾,或者旋轉顯示項。有三個手勢事件庶艾,分別介紹如下:

  • gesturestart:當一個手指已經按在屏幕上而另一個手指有觸摸屏幕時觸發(fā)
  • gesturechange:當觸摸屏幕的任何一個手指的位置放生變化時觸發(fā)袁余。
  • getstureend:當任何一個手指從屏幕上面移開時觸發(fā)

只有兩個手指都觸摸到事件的接受容器是才會觸發(fā)這些事件。由于這些事件冒泡落竹,所有將事件處理程序放在文檔上也可以處理所有手勢事件泌霍。事件的目標就是兩個手指都位于其范圍內的那個元素

與觸摸事件一樣,每個手勢事件的 event 對象都包含著對標準的鼠標事件屬性(bubbles述召、cancelable朱转、clientX....)。此外积暖,還包含兩個額外的屬性:rotation藤为、scale

  • rotation 屬性表示手指變化引起的旋轉角度
    • 負值表示逆時針旋轉
    • 正值表示順時針旋轉(該值從 0 開始)
  • scale 屬性表示兩個手指間距離的變化情況(例如向內收縮會縮短距離);這個值從1開始夺刑,并隨距離拉大而增長缅疟,隨距離縮短而減小。

下面是使用手勢事件的一個示例

function handleGestureEvent(event) {
  var output = document.getElementById('output')
  switch(event.type) {
    case 'gesturestart':
      output.innerHTML = "Gesture started (rotation =" + event.rotation + ",scale=" + event.scale + ")"
      break;
    case 'gestureend':
      output.innerHTML += "<br>Gesture ended (rotation=" + event.rotation + ",scale=" + event.scale + ")"
      break;
    case 'gesturechange': 
      output.innerHTML += "<br>Gesture change (rotation=" + event.rotation + ",scale=" + event.scale + ")"
      break; 
  }
}

document.addEventListener('gesturestart', handleGestureEvent, false)
document.addEventListener('gestureend', handleGestureEvent, false)
document.addEventListener('gesturechange', handleGestureEvent, false)

五遍愿、內存和性能

在JavaScript中存淫,添加到頁面上的事件處理程序數量將直接關系到頁面的整體運行性能。導致這一問題的原因是多方面的沼填。首先桅咆,每個函數都是對象,都會占用內存坞笙;內存中的對象越多岩饼,性能就越差。其次薛夜,必須先指定所有事件處理程序而導致的DOM訪問次數籍茧,會延遲整個頁面的交互就緒時間。事實上梯澜,從如何利用好事件處理程序的角度出發(fā)寞冯,還是有一些方法能夠提升性能的。

5.1晚伙、事件委托

對“事件處理程序過多”問題的解決方案就是 事件委托简十。事件委托利用冒泡原理。我們可以為整個頁面指定一個 onclick 事件處理程序撬腾,而不必給每個可單機的元素分別添加事件處理程序螟蝙。

<ul id="myLinks">
  <li id="goSomewhere">Go somewhere</li>
  <li id="doSomething">Do something</li>
  <li id="sayHi">Say hi</li>
</ul>  

使用事件委托,只需要在DOM 樹中盡量最高的層此上添加一個事件處理程序民傻。

var list = document.getElementById('myLinks')

EventUtil.addHandler(list, 'click', function(event) {
  event = EventUtil.getEvent(event)
  var target = EventUtil.getTarget(event)

  switch(target.id) {
    case 'goSomewhere':
      location.
      break;
    case 'doSomething':
      document.body.style.backgroundColor = 'pink'
      break;
    case 'sayHi':
      alert('hi')
      break;    
  }
})

如果可信的話胰默,也可以考慮為 document 對象添加一個事件處理程序场斑,用以處理頁面上發(fā)生的某種特定類型的事件。這樣做與采取傳統(tǒng)的做法相比具有如下優(yōu)點牵署。

  • document對象很快就可以訪問漏隐,而且可以在頁面生命周期的任何時點上為它添加事件處理程序(無需等待 DOMContentLoaded 或 load 事件)。換句話說奴迅,只要可單機的元素呈現在頁面上青责,就可以立即具備適當的功能
  • 在頁面設置事件處理程序所需的事件更少。只添加一個事件處理程序所需的 DOM 引用更少取具,所花的時間也更少
  • 整個頁面占用的內存空間更少脖隶,能夠提升整體性能。

5.2暇检、移除事件處理程序

每當事件處理程序指定給元素時产阱,允許中的瀏覽器代碼與支持頁面交互的JavaScript代碼之間就會建立一個連接。這種連接越多块仆,頁面執(zhí)行起來就越慢构蹬。在不需要的時候移除事件處理程序,也是解決這個問題的一種方案悔据。內存中留有那些過時不用的“空事件處理程序”(dangling event handler)庄敛,也就是造成Web應用程序內存與性能問題的 主要原因

在兩種情況下,可能會造成上訴問題科汗。

  • 從文檔中移除帶有事件處理程序的元素時铐姚。這可能是通過純粹的DOM操作,例如使用 removeChild() 和 replaceChild() 方法肛捍,

  • 使用innerHTML替換頁面中某一部分的時候,如果帶有事件處理程序的元素被 innerHTML 刪除了之众,那么原來添加到元素中的事件處理程序極有可能無法被當做垃圾回收拙毫。

如上所述,如果你知道某個元素即將被移除棺禾,那么最好手工移除事件處理程序缀蹄,

<body>
  <div id="myDib">
    <button id="btn">click</button>
</div>
<script>
var btn = document.getElementById('btn')
btn.onclick = function() {
  // ...

  btn.onclick = null  // 移除事件處理程序
  document.getElementById('myDiv').innerHTML = 'Processing'
}
</script>
</body>

這樣就確保了內存可以被 ,再次利用膘婶,而從 DOM 中移除按鈕也做到了干凈利索缺前。

導致 “空事件處理程序” 的另一種情況,就是卸載頁面的時候悬襟。毫不奇怪衅码,IE8及更早版本在這種情況下依然是問題最多的瀏覽器,經過其他瀏覽器或多或少也有類似的問題脊岳。在頁面被卸載之前沒有清理干凈事件處理程序逝段,那它們就會滯留在內存中垛玻。
最好的做法是在頁面卸載之前,先通過 onunload 事件處理程序移除所有事件處理程序奶躯。在此帚桩,事件委托技術再次表現出它的優(yōu)勢——需要跟蹤的事件處理程序越少,移除他們就越容易嘹黔。對這種類似撤銷的操作账嚎,我們可以把它想象成:只要是通過 onload 事件處理程序添加的東西,最后都要通過 onunload 事件處理程序將他們移除儡蔓。

六郭蕉、模擬事件

可以使用 JavaScript 在任意時刻來觸發(fā)特定的事件,而此時的事件就如同瀏覽器創(chuàng)建的事件一樣浙值。

6.1恳不、DOM中的事件模擬

在意在 document 對象上使用 createEvent() 方法創(chuàng)建 event 對象。MDN上面推薦 使用 event constructors來 代替這個方法开呐。

createEvent() 這個方法接受一個參數烟勋,即表示要創(chuàng)建的事件類型的字符串。
這個字符串可以是下列字符串之一

  • UIEvents:以便化的UI事件筐付。鼠標事件和鍵盤事件都繼承自UI事件卵惦。DOM3級中時UIEvent
  • MouseEvents:一般化的鼠標事件。DOM3級中時 MouseEvent
  • MutationEvents:一般化的DOM 變動事件瓦戚。DOM3級中時 MutationEvent沮尿。
  • HTMLEvents:以便化的HTML 事件。沒有對應的DOM3級事件(HTML事件被分散到其他類別中)
6.1.1较解、模擬鼠標事件

為 createEvent() 傳入字符串“MouseEvents”畜疾。返回的對象有一個名為 initMouseEvent() 方法,用于指定與該鼠標事件有關的信息印衔。這個方法接受 15 個參數啡捶,分別與鼠標事件中每個典型的屬性一一對應。
這些參數的含義如下:

  • type(字符串):表示要出發(fā)的事件類型奸焙,例如‘click’
  • bubbles(布爾值):表示事件是否應該冒泡瞎暑。為精確地模擬鼠標事件,應該把這個參數設置為 true与帆。
  • cancelable(布爾值):表示事件是否可以取消了赌。為精確地模擬鼠標事件,應該把這個參數設置為true玄糟。
  • view(AbstractView):與事件關聯的視圖勿她。這個參數幾乎總要設置為 document.defaultVieiw。
  • detail(整數):與事件有關的詳細信息阵翎。這個值一般只有事件處理程序使用嫂拴,但通常都設置為0播揪。
  • screenX(整數):事件相對于屏幕的X坐標
  • screenY(整數):事件相對于屏幕的Y坐標
  • clientX(整數):事件相對于視口的X坐標。
  • clientY(整數):事件相對于視口的Y坐標筒狠。
  • ctrlKey(布爾值):表示是否按下了 Ctrl 鍵猪狈。默認值為false
  • altKey(布爾值):表示是否按下了Alt 鍵,默認值為false
  • shiftKey(布爾值):表示是否按下了Shift 鍵辩恼,默認值為false
  • metaKey(布爾值):表示是否按下了 meta 鍵雇庙,默認值為false
  • button(整數):表示按下了哪一對鼠標鍵。默認值為0.
  • relatedTarget(對象):表示與事件相關的對象灶伊。這個從參數只在模擬mouseover 或 mouseout 時使用疆前。

當把dispatchEvent() 方法時,這個對象 target屬性會自動設置聘萨。

var btn = document.getElementById('myBtn')

// 創(chuàng)建事件對象
var event = document.createEvent('MouseEvents')

// 初始化事件對象
event.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, 0,
0, false, false, false, false, 0, null)

// 觸發(fā)事件
btn.dispatchEvent(event)
6.1.2竹椒、模擬鍵盤事件

DOM3 級規(guī)定,調用 createEvent() 并傳入 “KeyboardEvent” 就可以創(chuàng)建一個鍵盤事件米辐。返回的事件對象包含一個 initKeyEvent() 方法胸完,這個方法接受下列參數

  • type(字符串):表示要出發(fā)的事件類型,如‘keydown’
  • bubbles(布爾值):表示事件是否應該冒泡翘贮。為精確模擬鼠標事件赊窥,應該設置為 true
  • cancelable(布爾值):表示事件是否可以取消。為精確模擬鼠標事件狸页,應該設置為 true
  • view(AbstractView):與事件關聯的視圖锨能。這個參數幾乎總是要設置為 document,defaultView
  • key(布爾值):表示按下的鍵的鍵碼
  • location(整數):表示按下了哪里的鍵芍耘。0 表示默認的主鍵盤址遇,1 表示左, 2 表示右斋竞, 3 表示數字鍵盤倔约, 4 表示移動設備
  • modifiers(字符串):空格分割的修改鍵列表,如“shift”
  • repeat(整數):在一行中按了這個鍵多少次窃页。

由于 DOM3級不提倡使用 keypress 事件,因此只能利用這種技術來模擬 keydown 和 keyup 事件复濒。

var textbox = document.getElementById('myTextbox'), event

// 以DOM3 級方式創(chuàng)建事件對象
if (document.implementation.hasFeature('KeyboardEvents', '3.0')) {

  event = document.createEvent('KeyboardEvent')

  // 初始化事件對象
  event.initKeyboardEvent('keydown', true, true, document.defaultView, 'a', 0, 'shift', 0)

}
console.log(event)
// 觸發(fā)事件
textbox.dispatchEvent(event)

這個例子模擬的是按住 Shift 的同時又 按下 A鍵


在 Firefox 中脖卖,調用 createEvent() 并傳入“KeyEvents”就可以創(chuàng)建一個鍵盤事件。返回的事件對象會包含一個 initKeyEvent() 方法巧颈,這個方法接受下列 10個參數畦木。

  • type(字符串):表示要出發(fā)的事件類型,如‘keydown’
  • bubbles(布爾值):表示事件是否應該冒泡砸泛。為精確模擬鼠標事件十籍,應該設置為 true
  • cancelable(布爾值):表示事件是否可以取消蛆封。為精確模擬鼠標事件,應該設置為 true
  • view(AbstractView):與事件關聯的視圖勾栗。這個參數幾乎總要設置為 document.defaultView惨篱。
  • ctrlKey(布爾值):表示是否按下了 Ctrl 鍵。默認值為false
  • altKey(布爾值):表示是否按下了 Alt 鍵围俘。默認值為false
  • shiftKey(布爾值):表示是否按下了Shift 鍵砸讳,默認值為false
  • metaKey(布爾值):表示是否按下了 meta 鍵,默認值為false
  • keyCode(整數):被按下或釋放的鍵的鍵碼界牡。這個參數對 keydown 和 keyup 事件有用簿寂,默認值為 0
  • charCode(整數):通過按鍵生成的字符的ASCII編碼。這個參數對 keypress 事件有用宿亡,默認值為0

將創(chuàng)建event 對象傳入到 dispatchEvent() 方法就可以出發(fā)鍵盤事件常遂,如下:

// 只適用于Firefox
var textbox = document.getElementById('myTextbox')

// 創(chuàng)建事件對象
var event = document.createEvent('KeyEvents')

// 初始化事件對象
event.initKeyEvent('keypress', true, true, document.defaultView, false, false, false, false, 65, 65)

// 觸發(fā)事件
textbox.dispatchEvent(event)

在 Firefox 中運行上面的代碼,會在指定的文本框中輸入字母A挽荠。統(tǒng)一克胳,也可以依此模擬keyup 和 keydown 事件
在其它瀏覽器中,則需要創(chuàng)建一個通用的事件坤按,然后再向事件對象中添加鍵盤事件特有的信息毯欣。例如:

var textbox = document.getElementById('myTextbox')

// 創(chuàng)建事件對象
var event = document.createEvent('Events')

// 初始化事件對象
event.initEvent(type, bubbles, cancelable)
event.view = document.defaultView
event.altKey = false
event.ctrlKey = false
event.shiftKey = false
event.metaKey = false
event.keyCode = 65
event.charCode = 65

// 觸發(fā)事件
textbox.dispatchEvent(event)
6.1.3、模擬其他事件

雖然鼠標事件和鍵盤事件是在瀏覽器中最經常模擬的事件臭脓,但有時候同樣需要模擬變動事件和HTML 事件酗钞。

模擬變動事件,可以使用 createEvent('MutationEvents') 創(chuàng)建一個包含 initMutationEvent() 方法的變動事件對象来累。這個方法接受的參數包括:type砚作、bubbles、cancelable嘹锁、relateNode葫录、preValue、newValue领猾、attrName米同、attrChange
模擬變動事件

var event = document.createEvent('HTMLevents')
event.initMutationEvent('DOMNodeeInserted', true, false, someNode, '', '', '', 0)
target.dispatchEvent(event)

要模擬HTML 事件,同樣需要先創(chuàng)建一個 event對象——通過 createEvent("HTMLEvents")摔竿,然后再使用這個對象的initEvent() 方法來初始化它即可面粮。

var event = document.createEvent('HTMLEvents')
even.initEvent('focus', true, false)
target.dispatchEvent(event)

這個例子展示了如何在給定目標上模擬 focus 事件。模擬其他HTML事件的方法也是這樣

6.1.4继低、自定義DOM事件

DOM3級 還定義了“自定義事件”熬苍。創(chuàng)建自定義事件,可以調用 createEvent('CustomEvent')返回的對象有一個名為 initCustomEvent()的方法,接受如下4個參數

  • type(字符串):觸發(fā)的事件類型
  • bubbles(布爾值):表示事件是否應該冒泡
  • cancelable(布爾值):表示事件是否可以取消
  • detail(對象):任意值柴底,保存在event 對象的detail 屬性中婿脸。

可以像分派其他事件一樣在 DOM中分派創(chuàng)建的自定義事件對象。例如:

var div = document.getElementById('myDiv'), event

EventUtil.addHandler(div, "myevent", function(event) {
  alert("DIV:" + event.detail)
})
EventUtil.addHandler(document, "myevent", function(event) {
  alert("DOCUMENT:" + event.detail)
})
if (document.implementation.hasFeature("CustomEvents", "3.0")) {
  event = document.createEvent("CustomEvent")
  event.initCustomEvent("myevent", true, false, "Hello world!")
  div.dispatchEvent(event)
}

6.2柄驻、IE中時事件模擬

IE8 及之前版本中模擬事件與在DOM中模擬事件的思路相似:先創(chuàng)建 event 對象狐树,然后為期指定相應的信息,然后再使用該對象來觸發(fā)事件凿歼。

document.createEventObject() 方法可以在IE中創(chuàng)建event 對象褪迟。這個方法不接受參數,結果會返回一個通用的 event 對象答憔。然后味赃,你必須為證對象添加所有必要的信息。最后一步就是在目標上調用 fireEvent() 方法虐拓,這個方法接受兩個參數:事件處理程序的名稱 和 event 對象心俗。
在調用 fireEvent() 方法時,會自動為event 對象添加 srcElement 和 type 屬性蓉驹;

模擬在一個按鈕上觸發(fā)click 事件過程

var btn = document.getElementById('myBtn')

// 創(chuàng)建事件對象
var event = document.createEventObject()

// 初始化事件對象
event.screenX = 100
event.screenY = 0
event.clientX = 0
event.clientY = 0
event.ctrlKey = false
event.altKey = false
event.shiftKey = false
event.button = 0

// 觸發(fā)事件
btn.fireEvent("onclick", event)

采用同樣的模式 也可以模擬 出發(fā) keypress 事件

var textbox = document.getElementById('myTextbox')

// 創(chuàng)建事件對象
var event = document.createEventObject()

// 初始化事件對象
event.ctrlKey = false
event.altKey = false
event.shiftKey = false
event.keyCode = 65

// 觸發(fā)事件 
textbox.fireEvent('onkeypress', event)

其實城榛,目前來說新出來了很多API用于模擬事件,比如 click() 方法态兴,所以這些都很少見了

七狠持、小結

在使用事件時,需要考慮如下一些內存與性能方面的問題

  • 在必要限制一個頁面中事件處理程序的數量瞻润,數量太多會導致占用大量內存喘垂,而且也會讓用戶感覺頁面反應不夠靈敏
  • 建立在事件冒泡機制之上的事件委托技術,可以有效地減少事件處理程序的數量

事件時 JavaScript 中最重要的主題之一绍撞,深入理解事件的工作機制以及它們對性能的影響至關重要

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末正勒,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子傻铣,更是在濱河造成了極大的恐慌章贞,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件非洲,死亡現場離奇詭異鸭限,居然都是意外死亡,警方通過查閱死者的電腦和手機两踏,發(fā)現死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門败京,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹤竭,“玉大人,你說我怎么就攤上這事慌随「榱希” “怎么了看蚜?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵夯缺,是天一觀的道長强岸。 經常有香客問我峦树,道長渡冻,這世上最難降的妖魔是什么戚扳? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮族吻,結果婚禮上帽借,老公的妹妹穿的比我還像新娘。我一直安慰自己超歌,他們只是感情好砍艾,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巍举,像睡著了一般脆荷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上懊悯,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天蜓谋,我揣著相機與錄音,去河邊找鬼炭分。 笑死桃焕,一個胖子當著我的面吹牛,可吹牛的內容都是我干的捧毛。 我是一名探鬼主播观堂,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岖妄!你這毒婦竟也來了型将?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤荐虐,失蹤者是張志新(化名)和其女友劉穎七兜,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體福扬,經...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡腕铸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了铛碑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狠裹。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖汽烦,靈堂內的尸體忽然破棺而出涛菠,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布俗冻,位于F島的核電站礁叔,受9級特大地震影響,放射性物質發(fā)生泄漏迄薄。R本人自食惡果不足惜琅关,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望讥蔽。 院中可真熱鬧涣易,春花似錦、人聲如沸冶伞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽响禽。三九已至账劲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間金抡,已是汗流浹背瀑焦。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留梗肝,地道東北人榛瓮。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像巫击,于是被迫代替她去往敵國和親禀晓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內容

  • ??JavaScript 與 HTML 之間的交互是通過事件實現的。 ??事件顷级,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,503評論 1 11
  • JavaScript 與 HTML 之間的交互是通過事件實現的凫乖。事件,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬...
    LemonnYan閱讀 687評論 0 4
  • 事件流 JavaScript與HTML之間的交互是通過事件實現的弓颈。事件帽芽,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互...
    DHFE閱讀 834評論 0 3
  • JavaScript 程序采用了異步事件驅動編程模型。在這種程序設計風格下翔冀,當文檔导街、瀏覽器、元素或與之相關的對象發(fā)...
    劼哥stone閱讀 1,259評論 3 11
  • JavaScript 與 HTML 之間的交互是通過事件實現的纤子。事件搬瑰,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬...
    小小的白菜閱讀 305評論 0 0