
事件
一、事件流
事件流描述的是从页面中接收事件的顺序。
一)事件冒泡
IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。
如:div------>body------->html
二)事件捕获
Netscape的事件流叫做事件捕获,与事件冒泡的顺序相反。
虽然事件捕获是 Netscape Communicator 唯一支持的事件流模型,但 IE9、 Safari、 Chrome、 Opera
和 Firefox 目前也都支持这种事件流模型。尽管“DOM2 级事件”规范要求事件应该从 document 对象
开始传播,但这些浏览器都是从 window 对象开始捕获事件的。
由于老版本的浏览器不支持,因此很少有人使用事件捕获。我们也建议读者放心地使用事件冒泡,
在有特殊需要时再使用事件捕获。
三)DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
即使“DOM2 级事件”规范明确要求捕
获阶段不会涉及事件目标,但 IE9、 Safari、 Chrome、 Firefox 和 Opera 9.5 及更高版本都会在捕获阶段触
发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。
二、事件处理程序
而响应某个事件的函数就叫做事件处理程序 ,事件处理程序名字多以"on"开头。
一)HTML事件处理程序
不用它
二)DOM0级事件处理程序
通过 JavaScript 指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
每个元素(包括 window 和 document)都有自己的事件处理程序属性,
将这种属性的值设置为一个函数,就可以指定事件处理程序
<head>
<meta charset="UTF-8">
<title>Test</title>
<script>
window.onload = function (ev) {
var btn = document.getElementById("bt1");
btn.onclick = function () {
alert(this.id); //this指向当前元素
} //删除事件处理程序
btn.onclick = null;
}
</script>
</head>
<body>
<form action="post">
<input type="button" id="bt1" value="button">
</form>
</body>
以这种方式添加的事件处理程序,会在事件的冒泡阶段被处理。
三)DOM2级事件处理程序
DOM2级事件定义了两个方法:
1.addEventListener(eventName,func,boolean)
eventName:事件名
func:事件处理程序
boolean:当为true时候表示在捕获阶段调用事件处理程序。使用DOM2级方法的好处是可以添加多个事件处理程序,被添加的程序会一次执行。
window.onload = function (ev) {
var btn = document.getElementById("bt1");
btn.addEventListener("click", function () {
alert(this.id);
},true); btn.addEventListener("click", function () {
alert("Ha li lu ya!");
},true);
}
2.removeEventListener(eventName,func,boolean)
注意:该函数"不删无名之辈"
其他略
三、事件对象
在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的
信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。例如,鼠标操作导致的事件
对象中,会包含鼠标位置的信息,而键盘操作导致的事件对象中,会包含与按下的键有关的信息。所有
浏览器都支持 event 对象,但支持方式不同。
一)DOM中的事件对象
在事件处理程序内部,对象 this 始终等于 currentTarget 的值,而 target 则只包含事件的实
际目标。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
<script>
window.onload = function () {
//点击bt1时得到
document.body.onclick = function (event) {
alert(event.currentTarget === document.body); //true
alert(this === document.body); //true
alert(event.target === document.getElementById("bt1")); //true 点击bt2时为false
}
}
</script>
</head>
<body>
<form action="post">
<input type="button" id="bt1" value="button">
<input type="button" id="bt2" value="button2">
</form>
</body>
</html>
只有 cancelable 属性设置为 true 的事件,才可以使用 preventDefault()来取消其默认行为。
另外, stopPropagation()方法用于立即停止事件在 DOM 层次中的传播,即取消进一步的事件
捕获或冒泡。
window.onload = function () {
var bt = document.getElementById("bt1");
bt.onclick = function (ev) {
alert("Clicked button!");
ev.stopPropagation(); //阻止事件进一步传播到body元素
} document.body.onclick = function (ev) {
alert("Clicked body !");
}
}
事件对象的 eventPhase 属性,可以用来确定事件当前正位于事件流的哪个阶段。如果是在捕获阶
段调用的事件处理程序,那么 eventPhase 等于 1;如果事件处理程序处于目标对象上,则 eventPhase 等于 2;如果是在冒泡阶段调用的事件处理程序, eventPhase 等于 3 .
只有在事件处理程序执行期间, event 对象才会存在;一旦事件处理程序执行完
成, event 对象就会被销毁
四、事件类型
DOM3级有以下几类事件
一)UI事件
UI事件是指那些不一定与用户操作有关的事件。
1)DOMActivate:表示元素被激活,不建议使用。
2)load事件:
当页面完全加载(包括所有图像、js文件、CSS外部文件)后,事件会在window上触发,所有框架加载完成后在框架元素上触发,
当图像加载完毕后在图像上触发,当嵌入的内容加载完毕后在<object>元素上触发。
3)unload事件:
unload事件在页面被完全卸载后触发,当用户从一个页面切换到另一个页面,就会触发该事件。
而利用这个情况最多的是清除引用,以避免内存泄漏。
3)resize事件:
当浏览器窗口调整到一个新的宽度或者高度时,就会触发resize事件。该事件在window上触发。
二)焦点事件
在页面元素获得或失去焦点时触发。
1)blur:元素失去焦点时触发,该事件不会冒泡,所有浏览器支持。
2)focus: 在元素获得焦点时触发,该事件不会冒泡,所有浏览器支持。
3)focusin:同上,但会冒泡。
4)focusout:元素失去焦点时触发,会冒泡。
三)鼠标与滚轮事件
DOM3级事件:
1)click:在用户单击主鼠标键或者按下回车键时触发。
2)dbclick:双击鼠标时触发。
3)mousedown:按下任意鼠标键时触发,不能通过键盘触发该事件。
4)mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且
在光标移动到后代元素上不会触发。
5)mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且
在光标移动到后代元素上不会触发。
6)mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。
7)mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另
一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。
8)mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触
发。不能通过键盘触发这个事件。
9)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
显然, click 和 dblclick 事件都会依赖于其他先行事件的触发;而 mousedown 和 mouseup 则
不受其他事件的影响。
1.客户区坐标位置
鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的 clientX 和
clientY 属性中。
2.页面坐标位置
通过客户区坐标能够知道鼠标是在视口中什么位置发生的,而页面坐标通过事件对象的 pageX 和
pageY 属性,能告诉你事件是在页面中的什么位置发生的。换句话说,这两个属性表示鼠标光标在页面中的位置,
因此坐标是从页面本身而非视口的左边和顶边计算的。在页面没有滚动的情况下 pageX 和 pageY 的值与 clientX 和 clientY 的值相等。
3.屏幕坐标位置
screenX 和 screenY 属性
4.修改键
虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要
采取的操作。这些修改键就是 Shift、 Ctrl、 Alt 和 Meta(在 Windows 键盘中是 Windows 键,在苹果机中
是 Cmd 键),它们经常被用来修改鼠标事件的行为。 DOM 为此规定了 4 个属性,表示这些修改键的状
态: shiftKey、 ctrlKey、 altKey 和 metaKey。这些属性中包含的都是布尔值,如果相应的键被按
下了,则值为 true,否则值为 false。
5.相关元素
在发生 mouseover 和 mouserout 事件时,还会涉及更多的元素。这两个事件都会涉及把鼠标指
针从一个元素的边界之内移动到另一个元素的边界之内。对 mouseover 事件而言,事件的主目标是获
得光标的元素,而相关元素就是那个失去光标的元素。类似地,对 mouseout 事件而言,事件的主目标
是失去光标的元素,而相关元素则是获得光标的元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
<script>
window.onload = function () {
var d = document.getElementById("myDiv");
d.onmouseout = function (ev) {
var t = ev.target;
var rt = ev.relatedTarget;
alert("Mouse out of " + t.tagName + " to " + rt.tagName);
}
}
</script>
</head>
<body>
<div id="myDiv" style="background-color: aquamarine; height: 100px; width: 100px;"></div>
</body>
</html>
这个例子会在页面上显示一个<div>元素。如果鼠标指针一开始位于这个<div>元素上,然后移出
了这个元素,那么就会在<div>元素上触发 mouseout 事件,相关元素就是<body>元素。与此同时,
<body>元素上面会触发 mouseover 事件,而相关元素变成了<div>。
DOM 通过 event 对象的 relatedTarget 属性提供了相关元素的信息
五、内存和性能
在 JavaScript 中,添加到页面上
的事件处理程序数量将直接关系到页面的整体运行性能。导致这一问题的原因是多方面的。首先,每个
函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程
序而导致的 DOM 访问次数,会延迟整个页面的交互就绪时间。
提升性能方法:
一)事件委托
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事
件处理程序,就可以管理某一类型的所有事件。例如, click 事件会一直冒泡到 document 层次。也就
是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必给每个可单击的元素分别添加事
件处理程序。
二)移除事件处理程序
在不需要的时候移除事件处理程序也是一种解决方法。
btn.onclick = null //移除事件处理程序
如果在页面被卸载之前没有清理干净事件处理程序,那它们就会滞留在内存中。每次加载完页面再卸载页面时(可能是在两个页
面间来回切换,也可以是单击了“刷新”按钮),内存中滞留的对象数目就会增加,因为事件处理程序
占用的内存并没有被释放。 最好的做法是在页面卸载之前,先通过 onunload 事件处理程序移除所有事件处理程序。