本篇内容
  • UI事件
  • 焦点事件
  • 鼠标与滚轮事件
  • 键盘与文本事件
  • 复合事件
  • 变动事件
  • HTML5事件
  • 设备事件
  • 触摸与手势事件

mdn-事件列表

4 事件类型

Web浏览器中可能发生的事件有很多类型。如前所述,不同的事件类型具有不同的信息,而“DOM3级事件”规定了以下几类事件。

  • UI(User Interface,用户界面)事件,当用户与页面上的元素交互时触发;
  • 焦点事件,当元素获得或失去焦点时触发;
  • 鼠标事件,当用户通过鼠标在页面上执行操作时触发;
  • 滚轮事件,当使用鼠标滚轮(或类似设备)时触发;
  • 文本事件,当在文档中输入文本时触发;
  • 键盘事件,当用户通过键盘在页面上执行操作时触发;
  • 合成事件,当为IME(Input Method Editor,输入法编辑器)输入字符时触发;
  • 变动(mutation)事件,当底层DOM结构发生变化时触发。
  • 变动名称事件,当元素或属性名变动时触发。此类事件已经被废弃,没有任何浏览器实现它们,无卵用

DOM3级事件模块在DO阳级事件模块基础上重新定义了这些事件,也添加了一些新事件。包括IE9在内的所有主流浏览帮都支持DOM2级事件。IE9也支持DOM3级事件。

4.1 UI事件

mdn-UIEvent

UI事件事件指的是那些不一定与用户操作有关的事件。这些事件在DOM规范出现之前,都是以这种或那种形式存在的,而在DOM规范中保留是为了向后兼容。现有的UI事件如下。

  • DOMActivate:表示元素已经被用户操作(通过鼠标或键盘)激活。这个事件在DOM3级事件中被废弃,但Firefox2+和Chrome支持它。考虑到不同浏览器实现的差异,不建议使用这个事件。
  • load:当页面完全加载后在window上面触发,当所有框架都加载完毕时在框架集上面触发,当图像加载完毕时在<img>元素上面触发, 或者当嵌入的内容加载完毕时在<object>元素上面 触发。
  • unload:当页丽完全卸载后在window上面触发,当所有框架都制裁后在框架集上面触发,或者当嵌入的内容卸载完毕后在<object>元素上面触发。
  • abort:在用户停止下载过程时.如果嵌入的内容没有加载完,则在<object>元素上面触发。
  • error:当发生JavaScript错误时在window上面触发,当无法加载图像时在<img>元素上面触发,当无法加载嵌入内容时在<object>元素上面触发,或者当有一或多个框架无法加载时在框架集上面触发。
  • select:当用户选择文本框(<input><textarea>)中的一或多个字符时触发。
  • resize:当窗口或框架的大小变化时在window或框架上面触发。
  • scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。<body>元素中包含所加载页面的滚动条。

除了DOMActivate之外,其他事件在DOM2级事件中都归为HTML事件(DOMActivate在DOM2级中仍然属于UI事件)。要确定浏览器是否支持DOM2级事件规定的HTML事件,可以使用如下代码:

1
var isSupported = document.implementation.hasFeature('HTMLEvents', '2.0');

要确定浏览器是否支持DOM3级事件定义的事件,可以使用如下代码:

1
var isSupported = document.implementation.hasFeature('UIEvent', '3.0');

4.1.1 load事件

JavaScript中最常用的一个事件就是load。当页面完全加载后(包括所有图像、JavaScript文件、css文件等外部资源),就会触发window上面的load事件。有两种定义onload事件处理程序的方式。

第一种

1
2
3
EventUtil.addHandler(window, 'load', function (e) {
alert('loaded');
});

第二种

1
2
3
4
5
6
7
8
9
<!Doctype html>
<html>
<head>
<title>test</title>
</head>
<body onload="alert('loaded')">
<div id="testDiv">click</div>
</body>
</html>

一般来说,在window上面发生的任何事件都可以在<body>元素中通过相应的特性来指定,因为在HTML中无法访问window元素。实际上,这只是为了保证向后兼容的一种权宜之计,但所有浏览器都能很好地支持这种方式。

图像上面也可以触发load事件,无论是在DOM中的图像元素还是HTML中的图像元素。

在不属于DOM文档的图像(包括未添加到文挡的<img>元素和Image对象)上触发load事件时,IE8及之前版本不会生成event对象。IE9修复了这个问题。

还有一些元素也以非标准的为式支持load事件。在IE9+、Firefox、Opera、Chrome和Safari3+及更高版本中,<script>元素也会触发load事件,以便开发人员确定动态加载的JavaScript文件是否加载完毕。与图像不同,只有在设置了<script>元素的src属性并将该元素添加到文档后,才会开始下载JavaScript文件。换句话说,对于<script>元素而言,指定src属性和指定事件处理程序的先后顺序就不重要了。以下代码展示了怎样为<script>元素指定事件处理程序。

1
2
3
4
5
6
7
8
9
EventUtil.addHandler(window, 'load', function (e) {
var script = document.createElement('script');
EventUtil.addHandler(script, 'load', function (e) {
alert('loaded');
});

script.src = 'test.js';
docuemnt.body.appendChild(script);
});

IE8及更早版本不支持<script>元素上的load事件。

4.1.2 unload事件

这个事件在文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,就会发生unload事件。而利用这个事件最多的情况是消除引用,以避免内存泄漏。与load事件类似,也有两种制定onunload事件处理程序的方式。

既然unload事件是在一切都被卸载之后才触发,那么在页面加载后存在的那些对象,此时就不一定存在了。此时,操作DOM节点或者元素的样式就会导致错误。

4.1.3 resize事件

当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件。这个事件在window(窗口)上面触发,因此可以通过JavaScript或者<body>元素中的onresize特性来指定事件处理程序。
关于何时会触发resize事件,不同浏览器有不同的机制。IE、Safari、Chrome和Opera会在浏览器窗口变化了1像素时就触发resize事件,然后随着变化不断重复触发。Firefox则只会在用户停止调整窗口大小时才会触发resize事件。由于存在这个差别,应该注意不要在这个事件的处理程序中加入大计算量的代码,因为这些代码有可能被频繁执行,从而导致浏览器反应明显变慢。

注意:浏览器窗口最小化或最大化时也会触发resize事件。

4.1.4 scroll事件

虽然scroll事件是在window对象上发生的,但它实际表示的则是页面中相应元素的变化。在混杂模式下,可以通过<body>元素的scrollLeft和scrollTop来监控到这一变化;而在标准模式下,除Safari之外的所有浏览器都会通过<html>元素来反映这一变化(Safari仍然基于<body>跟踪滚动位置)。与resize事件类似,scroll事件也会在文档被滚动期间重复被触发,所以有必要尽量保持事件处理程序的代码简单。

4.2 焦点事件

mdn-FocusEvent

焦点事件会在页面获得或失去焦点时触发。利用这些事件并与document.hasFocus()方法及 document.activeElement属性配合,可以知晓用户在页面上的行踪。有以下6个焦点事件。

  • blur:在元素失去焦点时触发。这个事件不会冒泡:所有浏览器都支持它。
  • DOMFocusin:在元素获得焦点时触发。这个事件与HTML事件focus等价,但它冒泡。只有Opera支持这个事件。DOM3级事件废弃了DOMFocusin,选择了focusin。
  • DOMFocusOut:在元素失去焦点时触发。这个事件是HTML事件blur的通用版本。只有Opera支持这个事件。DOM3级事件废弃了DOMFocu sout,选择了focusout。
  • focus:在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
  • focusin:在元素获得焦点时触发。这个事件与日TML事件,focus等价,但它冒泡。支持这个事件的浏览器有IE5.5+、Safari5.1+、Opera11.5+和Chrome。
  • focusout:在元素失去焦点时触发。这个事件是HTML事件blur的通用版本。支持这个事件的浏览器有IE5.5+、Safari 5.1+、Opera 11.5+和Chrome。

这一类事件中最主要的两个是focus和blur,它们都是JavaScript早期就得到所有浏览器支持的事件。这些事件的最大问题是它们不冒泡。

当焦点从页商中的一个元素移动到另一个元素,会依次触发下列事件:

  • 1.focusout在失去焦点的元素上触发;
  • 2.focusin在获得焦点的元素上触发:
  • 3.blur在失去焦点的元素上触发;
  • 4.DOMFocusOUt在失去焦点的元素上触发;
  • 5.focus在获得焦点的元素上触发;
  • 6.DOMFocusin在获得焦点的元素上触发。

其中,blur、DOMFocusOut和focusout的事件目标是失去焦点的元素;而focus、DOMFocusin和focusin的事件目标是获得焦点的元素。

4.3 鼠标与滚轮事件

mdn-MouseEvents

DOM3级事件中定义了9个鼠标事件

  • click:在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对确保易访问性很重要,意味着onelick事件处理程序既可以通过键盘也可以通过鼠标执行。
  • dblclick:在用户双击主鼠标拨钮(一般是左边的按钮)时触发。从技术上说,这个事件并不是DOM2级事件规范中规定的,但鉴于它得到了广泛支持,所以DOM3级事件将其纳入了标准。
  • mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。
  • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。DOM2级事件并没有定义这个事件,但DOM3级事件将它
    纳入了规范。IE、Firefox如和Opera支持这个事件。
  • mouseleave:在位于元素上方的鼠标光标移动到元素在范围之外时触发。这个事件不冒泡.而且在光标移动到后代元素上不会触发。DOM2级事件并没有定义这个事件,但DOM3级事件将它 纳入了规范。IE、Firefox9+和Opera支持这个事件。
  • mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。
  • mouseout:在鼠标指针位于一个元素上方,然后用户将其移人另-个元素时触发。又移入的另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。
  • mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另-个元素边界之内时触发。不能通过键盘触发这个事件。
  • mouseup:在用户释放鼠标梭钮时触发。不能通过键盘触发这个事件。

页面上的所有元素都支持鼠标事件。除了mouseenter和mouseleave,所有鼠标事件都会冒泡,也可以被取消,而取消鼠标事件将会影响浏览器的默认行为。取消鼠标事件的默认行为还会影响其他事件,因为鼠标事件与其他事件是密不可分的关系。

只有在同一个元素上相继触发mousedown和mouseup事件,才会触发click事件;如果mousedown或mouseup中的一个被取消,就不会触发click事件。类似地,只有触发两次click事件,才会触发一次dblclick事件。如果有代码阻止了连续两次触发click事件(可能是直接取消click事件,也可能通过取消mousedown或mouseup间接实现),那么就不会触发dblclick事件了。这4个事件触发的顺序始终如下:

  • 1.mousedown
  • 2.mouseup
  • 3.click
  • 4.mousedown
  • 5.mouseup
  • 6.click
  • 7.dblclick

IE8及之前版本中的实现有一个小bug,因此在双击事件中,会跳过第二个mousedown和click事件。

鼠标事件中还有一类滚轮事件。其实就是一个mousewheel事件。

4.3.1 客户区坐标位置

鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的clientX和clientY属性。所有浏览器都支持这两个属性,它们的值表示事件发生时鼠标指针在视口中的水平和垂直坐标。

可以使用如下代码获取鼠标事件的客户端坐标信息:

1
2
3
4
5
var div = docuemnt.getElementById('test');
EventUtil.addHandler(div, 'click', function (e) {
e = EventUtil.getEvent(e);
alert(e.clientX + ',' + e.clientY);
});

4.3.2 页面坐标位置

通过客户区坐标能够知道鼠标是在视口中什么位置发生的,而页面坐标通过事件对象的pageX和pageY属性,能告诉你事件是在页而中的什么位置发生的。换句话说,这两个属性表示鼠标光标在页面中的位置,因此坐标是从页面本身而非视口的左边和顶边计算的。

可以使用如下代码获取鼠标事件在页面中的坐标

1
2
3
4
5
var div = docuemnt.getElementById('test');
EventUtil.addHandler(div, 'click', function (e) {
e = EventUtil.getEvent(e);
alert(e.pageX + ',' + e.pageY);
});

在页面没有滚动的情况下,pageX和pageY的值与clientX和clientY的值相等。

IE8及更早版本不支持事件,对象上的页面坐标,不过使用客户区坐标和滚动信息可以计算出来。这时候需要用到document.body(混杂模式)或document.documentElement(标准模式)中的scrollLeft和scrollTop属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var div = docuemnt.getElementById('test');
EventUtil.addHandler(div, 'click', function (e) {
e = EventUtil.getEvent(e);
var pageX = e.pagex.
pageY = e.pageY;

if (pageX === undefined) {
pageX = event.clientX + (document.body.scrollLeft || document.document Element.scrollLeft);
}
if (pageY === undefined) {
pageX = event.clientY + (document.body.scrollTop || document.document Element.scrollTop);
}

alert(pageX + ',' + pageY);
});

4.3.3 屏幕坐标位置

鼠标事件发生时,不仅会有相对于浏览器窗口的位置,还有-个相对于整个电脑屏幕的位置。而通过screenX和screenY属性就可以确定鼠标事件发生时鼠都指针相对于整个屏幕的坐标信息。

可以使用如下代码获取鼠标事件在屏幕中的坐标

1
2
3
4
5
var div = docuemnt.getElementById('test');
EventUtil.addHandler(div, 'click', function (e) {
e = EventUtil.getEvent(e);
alert(e.screenX + ',' + e.screenY);
});

4.3.4 修改键

虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。这些修改键就是Shift、Ctrl、Alt和Meta(在Windows键盘中是Windows键,在苹果机中是Cmd键),它们经常被用来修改鼠标事件的行为。DOM为此规定了4个属性,表示这些修改键的状态:shiftKey、ctrlKey、altKey和metaKey。这些属性中包含的都是布尔值,如果相应的键被按下了,则值为true,否则值为false。当某个鼠标事件发生时,通过检测这儿个属性就可以确定用户是否同时按下了其中的键。

可以使用如下代码获取鼠标事件在屏幕中的坐标

1
2
3
4
5
6
7
8
9
10
11
var div = docuemnt.getElementById('test');
EventUtil.addHandler(div, 'click', function (e) {
e = EventUtil.getEvent(e);
var keys = [];

if (e.shiftKey) keys.push('shift');
if (e.ctrlKey) keys.push('ctrl');
if (e.altKey) keys.push('alt');
if (e.metaKey) keys.push('meta');
alert(keys.join(','));
});

IE9、Safari、Chrome和Opera都支持这4个键。IE8及之前版本不支持metaKey属性。

4.3.5 相关元素

在发生rnouseover和mouserout事件时,还会涉及更多的元素。这两个事件都会涉及把鼠标指针从一个元素的边界之内移动到另一个元素的边界之内。对mouseover事件而言,事件的主目标是获得光标的元素,而相关元素就是那个失去光标的元素。类似地,对mouseout事件而言,事件的主目标是失去光标的元素,而相关元素则是获得光标的元素。

DOM通过event对象的relatedTarget属性提供了相关元素的信息。这个属性只对于mouseover和mouseout事件才包含值;对于其他事件,这个属性的值是null。IE不支持realtedTarget属性,但提供了保存着同样信息的不同属性。在mouseover事件触发时,IE的fromElement属性中保存了相关元素;在mouseout事件出发时,IE的toElement属性中保存着相关元素。(IE9支持所有这些属性)可以把下面这个跨浏览器取得相关元素的方法添加到EventUtil对象中。

1
2
3
4
5
6
7
8
9
var EventUtil = {
//...
getRelatedTarget: function (e) {
if (e.relatedTarget) return e.relatedTarget;
if (e.toElement) return e.toElement;
if (e.fromElement) return e.fromElement;
return null;
}
};

4.3.6 鼠标按钮

只有在主鼠标按钮被单击(或键盘回车键被按下)时才会触发click事件.因此检测按钮的信息并不是必要的。但对于mousedown和mouseup事件来说,则在其event对象存在一个button属性,表示按下或释放的按钮。DOM的button属性可能有如下3个值:0表示主鼠标按钮,1表示中间的鼠标按钮(鼠标滚轮按钮),2表示次鼠标按钮。在常规的设置中,主鼠标按钮就是鼠标左键, 而次鼠标按钮就是鼠标右键。

IE8及之前版本也提供了button属性,但这个属性的值与DOM的button属性有很大差异。

  • 0 :表示没有按下按钮。
  • 1:表示按下了主鼠标按钮。
  • 2:表示按下了次鼠标按钮。
  • 3:表示同时按下了主、次鼠标按钮。
  • 4:表示按下了中间的鼠标按钮。
  • 5 :表示同时按下了主鼠标按钮和中间的鼠标按钮。
  • 6:表示同时按下了次鼠标按钮和中间的鼠标按钮。
  • 7:表示同时按下了三个鼠标按钮。

不难想见,DOM模型下的button属性比IE模型下的button属性更简单也更为实用,因为同时按下多个鼠标按钮的情形十分罕见。最常见的做法就是将IE模型规范化为DOM方式,毕竟除IE8及更 早版本之外的其他浏览稽都原生支踌DOM模型。而对主、中、次按钮的映射并不困难,只要将IE的其他选项分别转换成如同按下这三个按键中的一个即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var EventUtil = {
// ...
getButton: function (e) {
if (document.implementation.hasFeature('MouseEvents', '2.0')) {
return e.button;
} else {
switch (e.button) {
case 0:
case 1:
case 3:
case 5:
case 7:
return 0;
case 2:
case 6:
return 2;
case 4:
return 1;
}
}
}
};

4.3.7 更多的事件信息

“DOM2级事件”规范在event对象中还提供了detail属性,用于给出有关事件的更多信息。对于鼠标事件来说,detail中包含了一个数值。表示在给定位置上发生了多少次单击。在同一个像蒙上相继地发生一次mousedown和一次mouseup事件算作一次单击。detail属性从1开始计数,每次单击发生都会递增。如果鼠标在mousedown和mouseup之间移动了位置,则detail会被重量为0。

4.3.8 鼠标滚轮事件

IE6.0首先实现了mousewheel事件。此后,Opera、Chrome和Safari也都实现了这个事件。
这个事件可以在任何元素上面触发,最终会冒泡到document(IE8)或window对象。与mousewheel事件对应的event对象除包含鼠标事件的所有标准信息外,还包含一个特殊的wheelDelta属性。当用户向前滚动鼠标滚轮时,wheelDelta是120的倍数;当用 户向后滚动鼠标滚轮时,wheelDelta是-120的倍数。

将mousewheel事件处理程序指定给页闺中的任何元素或document对象,即可处理鼠标滚轮的交互操作。

1
2
3
4
EventUtil.addHandler(document, 'mousewheel', function (e) {
e = EventUtil.getEvent(e);
alert(e.wheelDelta);
});

Firefox支持一个名为DOMMouseScroll的类似事件,也是在鼠标滚轮滚动时触发。与mousewheel事件-样,DOMMouseScroll也被视为鼠标事件,因而包含与鼠标事件有关的所有属性。而有关鼠标滚轮的信息则保存在detail属性中,当向前滚动鼠标滚轮时,这个属性的值是-3的倍数,当向后滚动鼠标滚轮时,这个属性的值是3的倍数。 可以将DOMMouseScroll事件添加到页面中的任何元素,而且该事件会冒泡到window对象。

1
2
3
4
5
6
7
8
9
10
11
var EventUtil = {
// ...
getWheelDelta: function (e) {
if (e.wheelDelta) {
return (client.engine.opera && client.engine.opera < 9.5 ?
-e.wheelDelta : e.wheelDelta);
} else {
return -e.detail * 40;
}
}
}

4.3.9 触摸设备

  • 不支持dblclick事件。双击浏览器窗口会放大画面,而且没有办法改变该行为。
  • 轻击可单击元素会触发mousemove事件。如果此操作会导致内容变化,将不再有其他事件发生;如果屏幕没有因此变化,那么会依次发生mousedown、mouseup和click事件。轻击不可单击的元素不会触发任何事件。可单击的元素是指那些单击可产生默认操作的元素(如链接),或者那些已经被指定了onclick事件处理程序的元素。
  • mousemove事件也会触发mouseover和mouseout事件。
  • 两个手指放在屏幕上且页面随手指移动而滚动时会触发mousewheel和scroll事件。

4.3.10 无障碍性问题

如果你的Web应用程序或网站要确保残疾人特别是那些使用屏幕阅读嚣的人都能访问,那么在使用鼠标事件时就要格外小心。前面提到过,可以通过键盆上的回车键来触发click事件,但其他鼠标事件却无法通过键盘来触发。为此,我们不建议使用click之外的其他鼠标事件来展示功能或引发代码执行。因为这样会给盲人或视障用户造成极大不便。以下是在使用鼠标事件时应当注意的几个易访问性问题。

  • 使用click事件执行代码。有人指出通过onmousedown执行代码会让人觉得速度更快,对视力正常的人来说这是没错的。但是,在屏幕阅读器中,由于元法触发mousedown事件,结果就会造成代码无法执行。
  • 不要使用omnouseover向用户显示新的选项。原因同上,屏幕阅读器无法触发这个事件。如果确实非要-通过这种方式来显示新选项,可以考虑添加显示相同信息的键盘快捷方式。
  • 不要使用dblclick执行重要的操作。键盘无法触发这个事件。

4.4 键盘与文本事件

mdn-KeyboardEvent
有3个键盘事件。

  • keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
  • keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。按下Esc键也会触发这个事件。Safari3.1之前的版本也会在用户按下非字符键时触发keypress事件。
  • keyup;当用户释放键盘上的键时触发。

只有一个文本事件:textInput。这个事件是对keypress的补充,用意是在将文本显示给用户之前更容易拦截文本。在文本插入文本框之前会触发textInput事件。

在用户按了一下键盘上的字符键时,首先会触发keydown事件,然后紧跟着是keypress事件,最后会触发keyup事件。
其中,keydown和keypress都是在文本眶发生变化之前被触发的;而keyup事件则是在文本框已经发生变化之后被触发的。如果用户按下了一个字符键不放,就会重复触发key down和keypress事件,直到用户松开该键为止,此时会触发keyup事件。

4.4.1 键码

在发生keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符键,keyCode属性的值与ASCII码中对应小写字母或数字的编码相
同。因此,数字键7的keyCode值为55,而字母A键的keyCode值为65——与Shift键的状态无关。DOM和IE的event对象都支持keyCode属性。


1
2
3
4
5
var textbox = docuemnt.getElementById('textInput');
EventUtil.addHandler(textbox, 'keyup', function (e) {
e = EventUtil.getEvent(e);
alert(e.keyCode);
})

键码 键值
8 ‘Backspace’
9 ‘Tab’
13 ‘Enter’
16 ‘Shift’
17 ‘Ctrl’
18 ‘Alt’
19 ‘Pause’
20 ‘Caps Lock’
27 ‘Escape’
32 ‘Space’
33 ‘Page Up’
34 ‘Page Down’
35 ‘End’
36 ‘Home’
37 ‘Left’
38 ‘Up’
39 ‘Right’
40 ‘Down’
42 ‘Print Screen’
45 ‘Insert’
46 ‘Delete’
48 ‘0’
49 ‘1’
50 ‘2’
51 ‘3’
52 ‘4’
53 ‘5’
54 ‘6’
55 ‘7’
56 ‘8’
57 ‘9’
65 ‘A’
66 ‘B’
67 ‘C’
68 ‘D’
69 ‘E’
70 ‘F’
71 ‘G’
72 ‘H’
73 ‘I’
74 ‘J’
75 ‘K’
76 ‘L’
77 ‘M’
78 ‘N’
79 ‘O’
80 ‘P’
81 ‘Q’
82 ‘R’
83 ‘S’
84 ‘T’
85 ‘U’
86 ‘V’
87 ‘W’
88 ‘X’
89 ‘Y’
90 ‘Z’
91 ‘Windows’
93 ‘Right Click’
96 ‘Numpad 0’
97 ‘Numpad 1’
98 ‘Numpad 2’
99 ‘Numpad 3’
100 ‘Numpad 4’
101 ‘Numpad 5’
102 ‘Numpad 6’
103 ‘Numpad 7’
104 ‘Numpad 8’
105 ‘Numpad 9’
106 ‘Numpad *’
107 ‘Numpad +’
109 ‘Numpad -‘
110 ‘Numpad .’
111 ‘Numpad /‘
112 ‘F1’
113 ‘F2’
114 ‘F3’
115 ‘F4’
116 ‘F5’
117 ‘F6’
118 ‘F7’
119 ‘F8’
120 ‘F9’
121 ‘F10’
122 ‘F11’
123 ‘F12’
144 ‘Num Lock’
145 ‘Scroll Lock’
182 ‘My Computer’
183 ‘My Calculator’
186 ‘;’
187 ‘=’
188 ‘,’
189 ‘-‘
190 ‘.’
191 ‘/‘
192 ‘`‘
219 ‘[‘
220 ‘\‘
221 ‘]’
222 ‘\’’

无论keydown或keyup事件都会存在的一些特殊情况。在Firefox和Opera中,按分号键时keyCode值为59,也就是ASCII中分号的编码;但IE和Safari返回186,即键盘中按键的键码。

4.4.2 字符编码

发生keypress事件意味着按下的键会影响到屏幕中文本的显示。在所有浏览器中,按下能够插入或删除字符的键都会触发keypress事件;按下其他键能否触发此事件因浏览器而异。由于截止到2008年,尚无浏览器实现DOM3级事件规范,所以浏览器之间的键盘事件并没有多大的差异。

IE9、Firefox、Chrome和Safari的event对象都支持一个charCode属性,这个属性只有在发生keypress事件时才包含值,而且这个值是按下的那个键所代表字符的ASCII编码。此时的keyCode 通常等于0或者也可能等于所按键的键码。IE8及之前版本和Opera则是在keyCode中保存字符的ASCII编码。要想以跨浏览器的方式取得字符编码,必须首先检测charCode属性是否可用,如果不可用则使用keyCode,如下面的例子所示。

1
2
3
4
5
6
7
8
9
10
EventUtil = {
// ...
getCharCode: function (e) {
if (typeof e.charCode === 'number') {
return e.charCode;
} else {
return e.keyCode;
}
}
};

这个方法首先检测charCode属性是否包含数值(在不支持这个属性的浏览器中,值为undefined),如果是,则返回该值。否则就返回keyCode属性值。

在取得了字符编码之后,就可以使用String.formCharCode()将其转换成实际的字符。

4.4.3 DOM3级变化

DOM3级事件中的键盘事件,不再包含charCode属性。而是包含两个新属性:key和char。

其中,key属性是为了取代keyCode而新增的,它的值是一个字符串。在按下某个字符键时,key的值就是相应的文本字符(如’k’或’M’);在按下非字符键时,key的值是相应键的名(如 ‘Shift’或’Down’)。而char属性在按下字符键时的行为与key相同,但在按下非字符键时值为null。

IE9支持key属性,但不支持char属性。Safari和Chrome支持名为keyIdentifier的属性,在按下非字符键(例如Shift)的情况下与key的值相同。对于字符键,keyIdentifier返回一个格式 类似’U+0000’的字符串,表示Unicode值。

由于存在跨浏览器问题,因此不推荐使用key、keyIdentifier或char。

DOM3级事件还添加了一个名为location的属性,这是一个数值,表示按下了什么位置上的键:

  • 0表示默认键盘,
  • 1表示左侧位置(例如左位的Alt键),
  • 2表示右侧位置(例如右侧的Shift键)
  • 3表示数字小键盘,
  • 4表示移动设备键盘(也就是虚拟键盘),
  • 5表示手柄(如任天堂Wii控制器)。

IE9支持这个属性。Safari和Chrome支持名为keyLocation的等价属性,但有bug——值始终是0,除非按下了数字键盘(此时值为3);否则不会是1、2、4、5。

与key属性一样.支持location的浏览器也不多,所以在跨浏览器开发中不推荐使用。

最后是给event对象添加了getModifierState()方法。这个方法接收一个参数,即等于Shift、Control、AltGraph或Meta的字符串,表示要检测的修改键。如果指定的修改键是活动的(也就是处于被按下的状态),这个方法返回true,否则返回false。

4.4.4 textInput事件

“DOM3级事件”规范中引入了一个新事件.名叫textInput。根据规范,当用户在可编辑区域中输入字符时,就会触发这个事件。这个用于替代keypress的textinput事件的行为稍有不同。区别
之一就是任何可以获得焦点的元素都可以触发keypress事件,但只有可编辑区域才能触发textInput事件。区别之二是textInput事件只会在用户按下能够输入实际字符的键时才会被触发,而keypress事件则在按下那些能够影响文本显示的键时也会触发(例如退格键)。
由于textInput事件主要考虑的是字符,因此它的event对象中还包含一个data属性,这个属性的值就是用户输入的字符(而非字符编码)。
比如用户在小写模式下,按下了S键,data值就是’s’,而如果在大写模式下按下该键,data的值就是’S’


1
2
3
4
5
var textbox = document.getElementById('textInput');
EventUtil.addHandler(textbox, 'textInput', function (e) {
e = EventUtil.getEvent(e);
alert(e.data);
})

另外,event对象上还有一个属性,叫inputMethod,表示把文本输入到文本框中的方式。

  • 0,表示浏览器不确定是怎么输入的。
  • l,表示是使用键盘输入的。
  • 2,表示文本是帖贴进来的。
  • 3,表示文本是拖放进来的。
  • 4,袋示文本是使用削E输入的。
  • 5,表示文本是通过在表单中选择某一项输入的。
  • 6,表示文本是通过孚写输入的(比如使用手写笔)。
  • 7,表示文本是通过语音输入的。
  • 8,表示文本是通过几种方法组合输入的。
  • 9,表示文本是通过脚本输入的。

支持textInput属性的浏览器有IE9+、Safari和Chrome。只有IE支持inputMethod属性。

4.4.5 设备中的键盘事件

任天堂Wii会在用户按下Wii遥控器上的按键时触发键盘事件。尽管没有办法访问Wii遥控器中的所有按键,但还是有-些键可以触发键盘事件。
iOS版Safari和Android版WebKit在使用屏幕键盘时会触发键盘事件。

4.5 复合事件

mdn-CompositionEvent
复合事件(composition event)是DOM3级事件中新添加的一类事件,用于处理IME的输入序列。IME(Input Method Editor,输入法编辑器)可以让用户输入在物理键盘上找不到的字符。例如,使用拉丁文键盘的用户通过IME照样能输入日文字符。但通常需要同时按住多个键,但最终只输入一个字 符。复合事件就是针对检测和处理这种输入而设计的。有以下三种复合事件。

  • compositionstart:在IME的文本复合系统打开时触发,表示要开始输入了。
  • compositionupdate:在向输入字段中插入新字符时触发。
  • compositionend:在IME的文本复合系统关闭时触发,表示返回正常键盘输入状态。

复合事件与文本事件在很多方面都很相似。在触发复合事件时,目标是接收文本的输入字段。但它比文本事件的事件对象多一个属性data。

IE9+是到2011年唯一支持复合事件-的浏览器。由于缺少支持,对于需要开发跨浏览糕应用的开发人员,它的用处不大。

4.6 变动事件

mdn-MutationEvent
DOM2级的变动(mutation)事件能在DOM中的某一部分发生变化时给出提示。变动事件是为XML或HTMLDOM设计的,并不特定于某种语言。DOM2级定义了如下变动事件。

  • DOMSubtreeModified;在DOM结构中发生任何变化时触发。这个事件在其它也任何事件触发后都会触发。
  • DOMNodeInserted:在一个节点作为子节点被插入到另一个节点中时触发。
  • DOMNodeRemoved:在节点从其父节点中被移除时触发。
  • DOMNodeinsertedintoDocument:在一个节点被直接插入文档或通过子树间接插入文档之后触发。这个事件在DOMNodelnserted之后触发。
  • DOMNodeRemovedFromDocument: 在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发。这个事件在DOMNodeRemoved之后触发。
  • DOMAttrModified:在特性被修改之后触发。
  • DOMCharacterDataModified:在文本节点的值发生变化时触发。

IE8及更早版本不支持任何变动事件。由于DOM3级事件模块作废了很多变动事件,所以本节只介绍那些将来仍然会得到支持的事件。

4.6.1 删除节点

在使用removeChild()或replaceChild()从DOM中删除节点时,首先会触发DOMNodeRemoved事件。这个事件的目标(event.target)是被删除的节点,而event.related.Node属性中包含着对 目标节点父节点的引用。在这个事件触发时,节点尚未从其父节点删除,因此其parentNode属性仍然指向父节点(与event.relatedNode但相同)。这个事件会冒泡,因而可以在DOM的任何层次上面处理它。

如果被移除的节点包含子节点,那么在其所有子节点以及这个被移除的节点上会相继触发DOMNodeRemovedFromDocument事件。但这个事件不会冒泡,所以只有直接指定给其中一个子节点的事件处理程序才会被调用。这个事件的目标是相应的子节点或者那个被移除的节点,除此之外event对象中不包含其他信息。紧随其后触发的是DOMSubtreeModified事件。这个事件的目标是被移除节点的父节点;此时的event对象也不会提供与事件相关的其他信息。


1
2
3
4
5
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>

假设要移除<ul>元素。此时,就会依次触发以下事件。

  • 1.在<ul>元素上触发DOMNodeRemoved事件。relatedNode属性等于document.body。
  • 2.在<ul>元素上触发DOMNodeRemovedFromDocument事件。
  • 3.在身为<Ul>元素子节点的每个<li>元素及文本节点上触发DOMNodeRemovedFromDocument事件。
  • 4.在document.body上触发DOMSubtreeModified事件,因为<ul>元素是document.body的直接子元素。

4.6.2 插入节点

在使用appendChild()、replaceChild()或insertBefore()向DOM中插入节点时,首先会触发DOMNodeinserted事件。这个事件的目标是被插入的节点,而event.relatedNode属性中包含 一个对父节点的引用。在这个事件触发时,节点已经被插入到了新的父节点中。这个事件是冒泡的,因此可以在DOM的各个层次上处理它。
紧接着,会在新插入的节点上面触发DOMNodeinsertedintoDocument事件。这个事件不冒泡,因此必须在插入节点之前为它添加这个事件处理程序。这个事件的目标是被插入的节点,除此之外 event对象中不包含其他信息。
最后一个触发的事件是DOMSubtreeModified,触发于新插入节点的父节点。

4.7 HTML5事件

DOM规范没有涵盖所有浏览器支持的所有事件。很多浏览器出于不同的目的——满足用户需求或解决特殊问题,还实现了一些自定义的事件。 HTML5详尽列出了浏览器应该支持的所有事件。本节只讨论其中得到浏览器完善支持的事件,但并非全部事件。

4.7.1 contextmenu事件

为了实现上下文菜单,开发人员面临的主要问题是如何确定应该显示上下文菜单(在windows中,是右键单击;在Mac中是Ctrl+单击),以及如何屏蔽与该操作关联的默认上下文菜单。为解决这个问题,就出现了contextmenu这个事件,用以表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。

由于contextmenu事件是冒泡的,因此可以为document指定一个事件处理程序,用以处理页面中发生的所有此类事件。这个事件的目标是发生用户操作的元素。在所有浏览器中都可以取消这个事件:在兼容DOM的浏览器中,使用event.preventDefalut();在血中,将event.returnValue的值设置为false。因为contextmenu事件属于鼠标事件,所以其事件对象中包含与光标位置有关的所有属性。通常使用contextmenu事件来显示自定义的上下文菜单,而使用onclick事件处理程序来隐藏该菜单。

支持contextmenu事件的浏览器有IE、Firefox、Safari、Chrome和Opera 11+。

4.7.2 beforeunload事件

之所以有发生在window对象上的beforeunload事件,是为了让开发人员有可能在页面卸载前阻止这-操作。这个事件会在浏览器卸载页面之前触发,可以通过它采取消卸载并继续使用原有页面。但是,不能彻底取消这个事件,因为那就相当于让用户无法离开当前页面了。为此,这个事件的意图是将控制权交给用户。显示的消息会告知用户页面行将被卸载(正因为如此才会显示这个消息),询问用户是否真的要关闭页面,还是希望继续留下来。为了显示这个弹出对话框,必须将event.returnValue的值设置为要显示给用户的字符串(对IE及Firefox而言),同时作为函数的值返回(对Safari和Chrome而言)。


1
2
3
4
5
6
EventUtil.addHandler(window, 'beforeunload', function (e) {
e = EventUtil.getEvent(e);
var message = 'Are you ok?';
e.returnValue = message;
return message;
});

IE和Firefox,Safari和Chrome都支持beforeunload事件,也都会弹出这个对话框询问用户是否真想离开。Opera11及之前的版本不支持beforeunload事件。

4.7.3 DOMContentLoaded事件

window的load事件会在页面中的一切都加载完毕时触发,但这个过程可能会因为要加载的外部资源过多而颇费周折。而DOMContentLoaded事件则在形成完整的DOM树之后就会触发,不理会图像、JavaScript文件、css文件或其他资源是否已经下载完毕。与load事件不同,DOMContentLoaded支持在页面下载的早期添加事件处理程序,这也就意味着用户能够尽早地与页面进行交互。

要处理DOMContentLoaded事件,可以为document或window添加相应的事件处理程序。(尽管这个事件会冒泡到window,但它的目标实际上是document)。

DOMContentLoaded事件对象不会提供任何额外的信息(其target属性是document)。

Firefox、Chrome、Safari 3.1及更高的版本和Opera 9及更高版本都支持DOMContentLoaded事件,通常这个事件既可以添加事件处理程序,也可以执行其它DOM操作。这个事件始终都会在load事件之前触发。

对于不支持DOMContentLoaded的浏览器.我们建议在页面加载期间设置一个时间为0毫秒的超时调用。如

1
2
3
setTimeout(function () {
// ...
}, 0);

这段代码实际意思是:“在当前JavaScript处理完成后立即运行这个函数。”在页面下载和构建期间,只有一个JavaScript处理过程,因此超时调用会在该过程结束时立即触发。至于这个事件与DOMContentLoaded被触发的时间是否同步,主要还是取决与用户使用的浏览器页面中的其它代码。为了确保这个方法有效,必须将其作为页面中的第一个超时调用;即便如此,也还是无法保证在所有浏览器中该超时调用一定会遭遇load事件被触发。

4.7.4 readystatecbange事件

IE为DOM文档中的某些部分提供了readystatechange事件。这个事件的目的是提供与文档或元素的加载状态有关的信息,但这个事件的行为有时候也很难预料。支持readystatechange事件的 每个对象都有一个readyState属性,可能包含下列5个镜中的一个。

  • uninitialized (未初始化):对象存在但尚未初始化。
  • loading (正在加载):对象正在加载数据。
  • loaded (加载完毕):对象加载数据完成。
  • interactive(交互):可以操作对象了. 但还没有完全加载。
  • complete(完成):对象已经加载完毕。

这些状态看起来很直观,但并非所有对象都会经历readyState的这几个阶段。换句话说,如果某个阶段不适用某个对象,则该对象完全可能跳过该阶段;并没有规定哪个阶段适用于哪个对象。显然,这意味着readystatechange事件经常会少于4次.而readyState属性的值也不总是连续的。
对于document而言,值为”interactive”的readyState会在与DOMContentLoaded大致相同的时刻触发readystatechange事件。此时,DOM树已经加载完毕,可以安全地操作它了,因此就会进入交互(interactive)阶段。但与此同时,图像及其他外部文件不一定可用。
来看一段处理readystatechange事件的代码。

1
2
3
4
5
EventUtil.addHandler(docuemnt, 'readystatechange', function (e) {
if (docuemnt.readyState === 'interactive') {
alert('Content loaded');
}
});

在与load事件一起使用时,无法预测两个事件触发的先后顺序。在包含较多或较大的外部资源的页面中,会在load事件触发之前先进入交互阶段;而在包含较少或较小的外部资源的页商中,则很难说readystatechange事件会发生在load事件前面。

让问题变得更复杂的是,交互阶段可能会早于也可能会晚于完成阶段出现,无法确保顺序。在包含较多外部资源的页面中,交互阶段更有可能早于完成阶段出现;而在页面中包含较少外部资源的情况下,完成阶段先于交互阶段出现的可能性更大。因此,为了尽可能抢到先机,有必要同时检测交互和完成阶段。如

1
2
3
4
5
6
7
EventUtil.addHandler(document, 'readystatechange', function (e) {
if (document.readyState === 'interactive' ||
document.readyState === 'complete') {
EventUtil.removeHandler(document, 'readystatechange', arguments.callee);
alert('content loaded');
}
});

对于上面的代码来说,当readystatechange事件触发时,会检测document.readyState的值,看当前是否已经进入交互阶段或完成阶段。如果是,则移除相应的事件处理程序以免在其他阶段再执行。注意,由于事件处理程序使用的是匿名函数,因此这里使用了arguments.call回来引用该函数。然后,会显示一个警告框,说明内容已经加载完华。 这样编写代码可以达到与使用DOMContentLoaded十分相近的效果。

支持readystatechange事件的浏览器有IE、Firfox4+和Opera。

虽然使用readystatechange可以十分近似地模拟DOMContentLoaded事件,但它们本质还是不同的。不同页面中,load事件与readystatechange事件不能保证以相同的顺序触发。

另外,<script>(在lE和Opera中)和<link>(仅IE中)元素也会触发readystatechange事件,可以用来确定外部的JavaScript和css文件是否已经加载完成。与在其他浏览概中一样,除非把动态创建的元素添加到页面中,否则浏览器不会开始下载外部资源。基于元素触发的readystatechange事件也存在同样的问题,即readyState属性无论等于”loaded”还是”complete”都可以表示资源已经可用。有时候,readyState会停在”loaded”阶段而永远不会”完成”。有时候,又会跳过”loaded”阶段而直接”完成”。于是,还需要像对待document一样采取相同的编码方式。

4.7.5 pageshow和pagehide事件

Firefox和Opera有一个特性,名叫”往返缓存”(back-forward cache,或bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缰存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。如果页面位于bfcache中,那么再次打开该页面时就不会触发load事件。尽管由于内存中保存了整个页面的状态,不触发load事件也不应该会导致什么问题.但为了更形象地说明bfcache的行为,Firefox还是提供了一些新事件。

  • 第一个事件就是pageshow,这个事件在页面显示时触发,无论该页而是否来自bfcacheo在重新加载的页面中,pageshow会在load事件触发后触发;而对于bfcache中的页面,pageshow会在页面状 态完全恢复的那一刻触发。另外要注意的是,虽然这个事件的目标是document,但必须将其事件处理程序添加到window。

这个例子使用了私有作用域,以防止变量showCount进入全局作用域。当页面首次加载完成时,showcount的值为0。此后,每当触发pageshow事件,showCount的值就会递增并通过警告框显示出来。如果你在离开包含以上代码的页面之后,又单击“后退”按钮返回该页面,就会看到showCount每次递增的值。这是因为该变量的状态,乃至整个页面的状态,都被保存在了内存中,当你返回这个页面时,它们的状态得到了恢复。如果你单击了浏览器的“刷新”按钮,那么shoWCount的值就会被重置为0,因为页面已经完全重新加载了。

  • 除了通常的属性之外,pageshow事件的event对象还包含一个名为persisted的布尔值属性。 如果页面被保存在了bfcache中,则这个属性的值为true;否则,这个属性的值为false。可以像下面这样在事件处理程序中检测这个属性。
    通过检测persisted属性,就可以根据页面在bfcache中的状态来确定是否需要采取其他操作。
    与pageshow事件对应的是pagehide事件,该事件会在浏览器倒载页面的时候触发,而且是在unload事件之前触发。与pageshow事件一样,pagehide在document上面触发但其事件处理程序必须要添加到window对象。这个事件的event对象也包含persisted属性,不过用图有所不同。
    有时候,可能需要在pagehide事件触发时根据persisted的值采取不同的操作。对于pageshow事件,如果页面是从bfcache中加载的,那么persisted的值就是true;对于pagehide事件,如果 页而在卸裁之后会被保存在bfcache中,那么persisted的值也会被设置为true。因此,当第一次触发pageshow时,persisted的值一定是false,而在第一次触发pagehide时,persisted就会变成true(除非页而不会被保存在bfcache中)。

支持pageshow和pagehide事件的浏览器有Firefox、Safari 5+、Chrome和Opera。IE9及之前版本不支持这两个事件。

指定了onunload事件处理程序的页面会被自动排除在 “往返缓存”之外,即使事件处理程序是空的。原因在于,onunload 最常用于撤销在onload中所执行的操作,而跳过onload 后再次显示页面很可能就会导致页面不正常

4.7.6 hashchange事件

HTMLS新增了haschange事件,以便在URL的参数列表(及URL中“#”号后面的所有字符串)发生变化时通知开发人员。之所以新增这个事件,是因为在Ajax应用中,开发人员经常要利用URL参数列来保存状态或导航信息。必须要把hashchange事件处理程序添加给window对象,然后URL参数列表只要变化就会调用它。此时的event对象应该额外包含两个属性:oldURL和newURL。这两个属性分别保存着参数列表变化前后的完整URL。如

1
2
3
EventUtil.addHandler(window, 'hashchange', function (e) {
alert(e.oldURL + ',' + e.newURL);
})

支持haschange事件的浏览器有IE8+、Firefox 3.6+、Safari 5+、Chrome和Opera 10.6+。在这些 浏览器中,只有Firefox、Chrome和Opera支持oldURL和 newURL属性。为此,最好是使用location对象来确定当前的参数列表。

4.8 设备事件

设备事件(device event)可以让开发人员确定用户在怎样使用设备。

mdn-检测设备方向

4.8.1 orientationchange事件

苹果公司为移动Safari中添加了orientationchange事件,以便开发人员能够确定用户何时将设备由横向查看模式切换为纵向查看模式。移动Safari的window.orientation属性中可能包含 3个值:

  • 0 表示肖像模式,
  • 90 表示向左旋转的横向模式(“主屏幕”按钮在右侧),
  • -90 表示向右旋转的横向模 式(“主屏幕”按钮在左侧)。

相关文档中还提到一个值,即180表示iPhone头朝下;但这种模式至今尚未得到支持。如图展示了window.orientation 的每个值的含义。

orientationchange

只要用户改变了设备的查看模式,就会触发orientationchange事件。此时的event对象不包含任何有价值的信息,因为唯一相关的信息可以通过window.orientation访问到。下面是使用这个 事件的典型示例。

1
2
3
4
5
6
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 属性。

由于可以将orientationchange看成window事件,所以也可以通过指定<body>元素的onorientationchange特性来指定事件处理程序。

4.8.2 MozOrientation 事件

Firefox 3.6为检测设备的方向引入了一个名为MozOrientation的新事件(前缀Moz表示这是特定于浏览器开发商的事件,不是标准事件。)当设备的加速计检测到设备方向改变时,就会触发这个事 件。但这个事件与iOS中的orientationchange事件不同,该事件只能提供一个平面的方向变化。由于MozOrientation事件是在window对象上触发的,所以可以使用以下代码来处理。

因为不是很有必要,所以不在此详细描述。

4.8.3 deviceorientation 事件

本质上,DeviceOrientation Event规范定义的deviceorientation事件与MozOrientation事件类似。它也是在加速计检测到设备方向变化时在window对象上触发,而且具有与MozOrientation事件相同的支持限制。不过,deviceorientation事件的意图是告诉开发人员设备在空间中朝向哪儿,而不是如何移动。
设备在三维空间中是靠x、y和z轴来定位的。当设备静止放在水平表面上时,这三个值都是0。x轴方向是从左往右,y轴方向是从下往上,z轴方向是从后往前(如图所示)。

orientationchange

触发deviceorientation事件时,事件对象中包含着每个轴相对于设备静止状态下发生变化的信息。事件对象包含以下5个属性。

  • alpha:在围绕 z轴旋转时(即左右旋转时),y轴的度数差;是一个介于0到 360之间的浮点数。
  • beta:在围绕 x轴旋转时(即前后旋转时), z轴的度数差;是一个介于-180到180之间的浮点数。
  • gamma:在围绕y轴旋转时(即扭转设备时),z轴的度数差;是一个介于-90到90之间的浮点数。
  • absolute:布尔值,表示设备是否返回一个绝对值。
  • compassCalibrated:布尔值,表示设备的指南针是否校准过。如图是alpha、beta和gamma值含义的示意图。

orientationchange

下面是一个输出alpha、beta和gamma值的例子。

1
2
3
4
EventUtil.addHandler(window, "deviceorientation", function(event){ 
var output = document.getElementById("output");
output.innerHTML = "Alpha=" + event.alpha + ", Beta=" + event.beta + ", Gamma=" + event.gamma + "<br>";
});

通过这些信息,可以响应设备的方向,重新排列或修改屏幕上的元素。要响应设备方向的改变而旋 转元素,可以参考如下代码。         
1
2
3
4
EventUtil.addHandler(window, "deviceorientation", function(event){ 
var arrow = document.getElementById("arrow");
arrow.style.webkitTransform = "rotate(" + Math.round(event.alpha) + "deg)";
});

4.8.4 devicemotion 事件

DeviceOrientation Event规范还定义了一个devicemotion事件。这个事件是要告诉开发人员设备什么时候移动,而不仅仅是设备方向如何改变。例如,通过devicemotion能够检测到设备是不是正在往下掉,或者是不是被走着的人拿在手里。
触发 devicemotion 事件时,事件对象包含以下属性。

  • acceleration:一个包含 x、y 和 z 属性的对象,在不考虑重力的情况下,告诉你在每个方向上的加速度。
  • accelerationIncludingGravity:一个包含 x、y 和z属性的对象,在考虑z轴自然重力加速度的情况下,告诉你在每个方向上的加速度。
  • interval:以毫秒表示的时间值,必须在另一个devicemotion事件触发前传入。这个值在每个事件中应该是一个常量。
  • rotationRate:一个包含表示方向的alpha、beta和gamma属性的对象。如果读取不到acceleration、accelerationIncludingGravity和rotationRate值,则它们的值为null。因此,在使用这三个属性之前,应该先检测确定它们的值不是null。


1
2
3
4
5
6
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 触摸与手势事件

4.9.1 触摸事件

触摸事件会在用户手指放在屏幕上面时、在屏幕上滑动时或从屏幕上移开时触发。具体来说,有以下几个触摸事件。

  • touchstart:当手指触摸屏幕时触发;即使已经有一个手指放在了屏幕上也会触发。
  • touchmove:当手指在屏幕上滑动时连续地触发。在这个事件发生期间,调用preventDefault()可以阻止滚动。
  • touchend:当手指从屏幕上移开时触发。
  • touchcancel:当系统停止跟踪触摸时触发。关子此事件的确切触发时间,文档中没有明确说明。

上面这几个事件都会冒泡,也都可以取消。虽然这些触摸事件没有在DOM规范中定义,但它们却是以兼容DOM的方式实现的。因此,每个触摸事件的event对象都提供了在鼠标事件中常见的属性:bubbles、cancelable、view、clientX、clientY、screenx、screenY、detail、altKey、shiftKey、ctrlKey和metaKey。

除了常见的DOM属性外,触摸事件还包含下列三个用于跟踪触摸的属性

  • touches:表示当前跟踪的触摸操作的Touch对象的数组。
  • targetTouchs:特定于事件目标的Touch对象的数组。
  • changeTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组。

每个Touch对象包含下列属性。

  • clientX:触摸目标在视口中的x坐标。
  • clientY:触摸目标在视IJ中的y坐标。
  • identifier:标识触摸的唯一ID。
  • pageX:触摸目标在页函中的x坐标。
  • pageY:触摸目标在页函中的y坐标。
  • screenX:触摸目标在屏幕中的x坐标。
  • screenY:触摸目标在屏幕中的y坐标。
  • target:触摸的DOM节点目标。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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].clientY + ")";
break;
case "touchend":
output.innerHTML += "<br>Touch ended (" + event.changedTouches[0].clientX + "," + event.changeTouches[0].clientY + ")";
break;
case "touchmove":
event.preventDefault(); //阻止滚动
output.innerHTML += "<br>Touch moved (" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")";
break;

}

}

}
document.addEventListener("touchstart", handleTouchEvent, false);
document.addEventListener("touchend", handleTouchEvent, false);
document.addEventListener("touchmove", handleTouchEvent, false);

当touchstart事件发生时。会将触摸的位置信息输出到元素中。当touchmove事件发生时,会取消其默认行为,阻止滚动(触摸移动的默认行为是滚动页面),然后输出触摸操作的变化信息。而touched事件则会输出有关触摸操作的最终信息。注意,在touched事件发生时,touched集合中就没有任何Touch对象了,因为不存在活动的触摸操作;此时,就必须转而使用changeTouchs集合。
这些事件会在文档的所有元素上面触发,因而可以分别操作页面的不同部分。在触摸屏幕上的元素时,这些事件发生的数序如下:

  • 1.touchstart
  • 2.mouseover
  • 3.mousemove
  • 4.mousedown
  • 5.mouseup
  • 6.click
  • 7.touchend

4.9.2 手势事件

两个手指触摸屏幕时就会产生手势,手势通常会改变显示项的大小,或者旋转显示项。有三个手势事件,分别介绍如下。

  • gesturestart:当一个手指已经按在屏幕上而另一个手指又触摸屏幕时触发。
  • gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。
  • gestureend:当任何一个手指从屏幕上面移开时触发。

只有两个手指都触摸到事件的接收容器时才会触发这些事件。在一个元素上设置事件处理程序,意味着两个手指必须同时位于该元素的范围之内,才能触发手势事件(这个元素就是目标)。 由于这些事件冒泡,所以将事件处理程序放在文档上也可以处理所有手势事件。此时,事件的目标就是两个手指都位于其范围内的那个元素。

触摸事件和手势事件之间存在某种关系。当一个手指放在屏幕上时,会触发touchstart事件。 如果另一个手指又放在了屏幕上,则会先触发gesturestart事件,随后触发基于该手指的touchstart事件。 如果一个或两个手指在屏幕上滑动,将会触发gesturechange事件。但只要有-个手指移开,就会触发gestureend事件,紧接着又会触发基于该手指的touchend事件。

触发gestureend事件,紧接着又会触发基于该手指的touchend事件。
与触摸事件一样,每个手势事件的 event 对象都包含着标准的鼠标事件属性:bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、ctrlKey和metaKey。此外,还包含两个额外的属性:rotation和scale。其中,rotation属性表示手指变化引起的旋转角度,负值表示逆时针旋转,正值表示顺时针旋转(该值从0开始)。而scale属性表示两个手指间距离的变化情况(例如向内收缩会缩短距离);这个值从1开始,并随距离拉大而增长,随距离缩短而减小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function handleGestureEvent(event) {
var output = document.getElementById("output");
switch (event.type) {
case "gesturestart":
output.innerHTML = "Gesture started (rotation=" + event.ratation + ",scale=" + event.scale + ")";
break;
case "gestureend":
output.innerHTML += "<br>Gesture ended (rotation+" + event.rotation + ",scale=" + event.scale + ")";
break;
case "gesturechange":
output.innerHTML += "<br>Gesture changed (rotation+=" + event.rotation + ",scale+" + event.scale + ")";
break;
}
}

document.addEventListener("gesturestart", handleGestureEvent, false);
document.addEventListener("gestureend", handleGestureEvent, false);
document.addEventListener("gesturechange", handleGestureEvent, false);

触摸事件也会返回rotation和scale属性,但这两个属性只会在两个手指与屏幕保持接触时才会发生变化。一般来说,使用基于两个手指的手势事件,要比管理触摸事件中的所有交互要容易的多。

温习:

  • UI事件:load事件/unload事件、resize事件、scroll事件;
  • 焦点事件:focus;
  • 鼠标与滚轮事件:pageX/pageY、clientX/clientY、screenX/screenY、修改键、相关元素、鼠标按钮、滚轮事件、触摸事件;
  • 键盘与文本事件:键码和字符编码的概念、textInput事件
  • 复合事件:概念
  • 变动事件:增/删节点
  • HTML5事件:contextmenu事件、beforeunload事件、DOMContentLoaded事件、readyetatecbange事件、pageshow和pagehide事件、hashchange事件
  • 设备事件:orientationchange事件、MozOrientation 事件、deviceorientation 事件 、devicemotion 事件
  • 触摸与手势事件:触摸事件、手势事件

(完)