事件基础
JavaScript事件,又称DOM事件,指用户对DOM节点进行各种操作时,能触发相应的处理函数。这些操作包括获取焦点、点击、键盘输入、鼠标悬浮、窗口变化等等。
一个JavaScript事件,包括事件绑定、事件触发、事件冒泡、事件捕获、事件处理。下面将详细描述。
事件绑定
事件有类型,大类可以分为鼠标事件、键盘事件、窗口事件、表单事件等,细分可以分为鼠标悬浮、鼠标移动、鼠标点击、鼠标按下等等,这里不一一阐述,详情可以去翻阅官方文档。类型有对应的写法,例如鼠标点击事件为click(注意不是onclick),鼠标按下为mousedown。
事件需要绑定在某个DOM节点(包括顶层对象window),下面给出一段兼容各大主流浏览器的事件绑定代码:
var addEvent = function(type, callback) {
if (window.attachEvent) { // IE
this.attachEvent.call(this, "on" + type, callback);
} else if (window.addEventListener) {
this.addEventListener.call(this, type, callback, false);
} else {
alert("无法绑定事件,请将浏览器版本发至评论");
}
return this;
}
注意addEventListener的第三个参数是设置事件是在事件捕获阶段进行还是在事件冒泡阶段进行,true表示在事件捕获阶段进行。事件捕获跟事件冒泡将在下文详细解说。
事件捕获VS事件冒泡
两者是亲兄弟一家亲,所以放一块来讲。它们发生在DOM节点嵌套并且父子(祖先-后代)都有事件绑定函数时。事件捕获从这些节点的最顶层开始,一直到最底层。而事件冒泡则相反,从最底层的目标开始,一直到最顶层。
我们看以下情况:
<div id="first">
<span id="second">
<b id="third">111</b>
</span>
</div>
我们先给它们绑定函数
addEvent.call(document.getElementById("first"), "click", function(event) {
console.log("first");
});
addEvent.call(document.getElementById("second"), "click", function(event) {
console.log("second");
});
addEvent.call(document.getElementById("third"), "click", function(event) {
console.log("third");
});
在谷歌中运行,我们可以看到,浏览器将依次输出third,second, first。这就是事件冒泡。
即使我们将上述addEvent里的addEventListener第三个参数去掉,结果仍然不变。说明谷歌默认采用事件冒泡机制。实际上绝大多数浏览器默认采用事件冒泡机制(本人只测试了chrome,FF,IE,不过opera一般也会随大流),而IE只支持事件冒泡,IE的绑定事件 函数attachEvent甚至没有设置是否使用事件捕获的参数。
而如果我们将addEventListener第三个参数设为true,结果将刚好相反,输出first,second,third。这就是事件捕获。当然,IE不支持。
事件的冒泡有时不是我们需要的,也就是说,我们只需要子节点的事件,而不希望事件冒泡到父节点。我们可以取消冒泡。
event.cancelBubble = true;// IE
event.stopPropagation();// 非IE
其中,event为事件回调函数的参数,后文会详细说明。
事件委托
事件委托不是W3C标准定义的内容,而是一种利用事件冒泡机制的技巧应用。事件委托指的是将子节点的事件委托给祖先元素来执行,也即是触发子节点事件后,等到事件冒泡到某个祖先节点,触发该祖先节点同类型事件,并判断是否为目标子节点,如果是,执行它。
下面给出一段简易的代码:
<div id="first">
<span id="second">
<b class="third">111</b>
<b class="third">222</b>
</span>
</div>
function delegate(node, childNodeClass, eventType, func) {
addEvent.call(node, eventType, function(event) {
var classes = event.target.className.split(" ");
if(classes.indexOf(childNodeClass) > -1) {
func(event);
}
})
}
调用时,
var node = document.getElementById("first");
delegate(node, "third", "click", function(event) {
console.log(event.target);
})
在这里,我们是用的类名来判断,因为实际中用类名的比较频繁。当然,你也可以扩展此函数。
事件委托有很多好处,当然,这些好处在当需要给很多同类节点添加事件,比如菜单栏的悬浮事件时,会更明显:
- 减少程序猿负担,不需要写DOM树遍历,优化性能
- 绑定事件很消耗性能,写委托能减少绑定事件的次数(只绑定一个祖先节点)
- 当新增节点的时候,不需要重新绑定事件
event属性
我们经常见到类似以下的代码:
node.onclick = function(event) {
}
在事件处理函数中,event为其唯一的参数,包含了事件所有的信息和方法。这里我们挑几个常见的说一下:
- type:事件类型,和IE类似,但是没有“on”前缀,例如单击事件只是“click”。
- target:发生事件的节点。
- currentTarget:发生当前正在处理的事件的节点,可能是Target属性所指向的节点,也可能由于捕捉或者起泡,指向Target所指节点的父节点。
- eventPhase:指定了事件传播的阶段。是一个数字。
- timeStamp:事件发生的时间。
- bubbles:指明该事件是否冒泡。
- cancelable:指明该事件是否可以用preventDefault()方法来取消默认的动作。
- preventDefault()方法:取消事件的默认动作;
- stopPropagation()方法:停止事件传播。
- altKey,ctrlKey,shiftKey,meta:顾名思义,这些属性是指键盘、鼠标事件发生的时候,是否同时按住了Alt、Ctrl或者Shift键,返回的是一个布尔值。
- clientX、clientY:是指事件发生的时候,鼠标的横、纵坐标,返回的是整数,它们的值是相对于包容窗口的左上角生成的,在DOM标准中,这两个属性值都不考虑文档的滚动情况,也就是说,无论文档滚动到哪里,只要事件发生在窗口左上角,clientX和clientY都是0,但是在IE中,要想得到事件发生的坐标相对于文档开头的位置,要加上 document.body.scrollLeft和document.body.scrollTop。
- keyCode:返回keydown和keyup事件发生的时候,按键的代码以及keypress事件的Unicode字符。比如event.keyCode=13代表按下了回车键
- button:声明了被按下的鼠标键,是一个整数。0代表鼠标左键,1代表鼠标的中间键,2代表鼠标右键
- 还有很多属性,有兴趣的可以把event整个变量console出来看下