The Event Of JavaScript

当我们打开网页时,我们会触发各种各样的事件,比如加载页面完成就会触发onload事件,点击某个元素就会触发其click事件。那么我们应该如何为我们的DOM元素绑定事件?一般操作事件会有三种方式:

HTML事件处理程序 - 直接HTML内联属性(不建议这么做)

1
2
3
4
<!--
这种方式使代码耦合,将javascript代码直接嵌入到了html中,不利于后期的代码维护,也不利于书写较长代码。
-->
<button onclick="alert('你点击了按钮');">点击按钮</button>

DOM 0级事件处理程序 - DOM属性的绑定

1
2
3
4
// 采用属性赋值的形式,比较简单,但容易被覆盖
element.onclick = function(event){
alert('你点击了按钮');
};

DOM 2级事件处理程序 - 使用标准的事件监听函数

为DOM元素添加事件:

element.addEventListener(event, function, useCapture)

为DOM元素移出事件:

element.removeEventListener(event, function, useCapture)

event为事件名称,function为事件触发后的回调函数,useCapture为一个用户选项

useCapture指定事件是在捕获阶段执行还是冒泡阶段执行,默认为false在冒泡阶段执行,true则为事件句柄在捕获阶段执行

这里就不得不提DOM的事件流,”DOM2级事件”规定事件流包括3个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段

当我们在DOM树上的某个节点发生操作时,就会有一个事件发出。这个事件从window发出,通过DOM树到达目标节点,也就是发生操作的节点。捕获过程中,凡是经过节点上有该事件是定义在捕获阶段执行的,就会被触发。

冒泡阶段与捕获阶段原理基本相同,到达目的节点,目标阶段结束后,会依据冒泡阶段的DOM树路径返回,触发经过节点对于该事件在冒泡阶段的执行。

利用事件冒泡的机制,我们能够减少对于元素的大量绑定,只需统一绑定在父元素上即可,也称为事件委托,有利于页面整体运行的性能。同时我们需要进行一些DOM节点的插入与删除,仅仅绑定在一个节点上往往会由于插入删除而失效,事件冒泡也能很好地解决这个问题。

当然你如果只想触发特定元素上的绑定事件,可以在回调函数中使用event.stopPropagation()来阻止事件的冒泡,注意:该函数并非是来阻止冒泡阶段发生的,而是执行后终止该事件继续传播,无论其处于什么阶段

在触发DOM上的某个事件时,会产生一个事件对象event,之前已经略有提及。其包含了许多与事件相关的属性与方法,比较常见的有:

event.type 被触发的事件类型,比如’click’, ‘mouseover’;

event.target 事件的目标,注意与event.currentTarget的区别,currentTarget为当前正在处理事件的元素,可能为冒泡或是捕获阶段,target目标元素的父元素;

event.clientX/Y 是在事件发生时,鼠标指针与可视区域网页顶部和左部的距离。event.pageX/Y与其稍稍不同,是与整个网页的顶部和左部距离。在没有滚动条的情况下两者的值是一致的,有滚动条的情况下,pageX >= clientX,pageY >= clientY;

event.preventDefault() 取消事件的默认行为,比如a标签的超链接;

在javascript的事件处理中,我们有时还需要考虑到IE的兼容性。比如,上文提及的DOM方法中addEventListener对应IE的attachEventremoveEventListener对应IE的detachEvent,在此方法下,事件处理程序会在全局作用域中运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert(this === window) // true
});
// 将addEventListener与attachEvent集成到addEvent上
if (document.addEventListener) {
this.addEvent = function(elem, type, fn) {
elem.addEventListener(type, fn, false);
return fn;
};
} else if (document.attachEvent) {
this.addEvent = function(elem, type, fn) {
var bound = function() {
// 将this绑定在elem上
return fn.apply(elem, arguments);
}
elem.attachEvent("on" + type, bound);
return bound;
};
};

IE中的事件对象与DOM的也略有不同,我们也建议在书写时考虑浏览器的兼容性

1
2
3
4
5
6
7
8
9
if ( !event.target ) {
event.target = event.srcElement || document;
}
event.preventDefault = function() {
event.returnValue = false;
};
event.stopPropagation = function() {
event.cancelBubble = true;
};

上周做了拉钩网首页一个小效果的demo,其中需要根据鼠标移入移出元素来实现js效果,这里我留意到了鼠标移动事件。

目前存在两组鼠标移动事件,mouseovermouseout均会冒泡,并且不论鼠标指针穿过被选元素或其子元素,都会触发事件,而且无法阻止其冒泡;mouseentermouseleave则不冒泡,只有在鼠标指针穿过被选元素时,才会触发事件。一般我们为了防止事件反复触发,以及动画的抖动,选择mouseenter/mouseleave事件。