JAVASCRIPT与HTML之间的交互是通过事件来实现的。事件,就是文档或浏览器窗口中发生的一些特定交互瞬间。可以使用侦听器( 或处理程序 )来预定事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为观察员模式的模型,支持页面的行为与页面的外观之间的松散耦合。
事件最早是在IE3和Netscape Navigator 2中出现的,当时是作为分担服务器运算负载的一种手段。在IE4和Navigator 4发布时,这两种浏览器都提供了相似但是不同的API, 这些API并存经历了好几个主要版本。DOM2 级规范开始尝试以一种符合逻辑的方式来标准化DOM事件。IE9, Firefox,Opera,Safari和Chrome全都已经实现了“DOM2级事件”模块的核心部分。IE8是最后一个仍然使用专用事件系统的主要浏览器。
<一> 事件流
事件流描述的是从页面中接受事件的顺序。但有意识的是,IE和Navigator 开发团队居然提出了差不多是完全相反的事件流概念。IE的事件流是冒泡流, 而Navigator 的事件流是捕获流。
1.1 事件冒泡
IE的事件流叫做事件冒泡(event bubbling), 即事件开始时由最具体的元素( 文档中嵌套层次最深的那个节点 )接受,然后逐级向上传播到较为不具体的节点(文档)。
案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div onclick="test()">事件</div>
</body>
<script type="text/javascript">
function test() {
alert('test event bubbling')
}
</script>
</html>
click事件传播顺序: div --> body --> html --> document
click事件首先在<div>元素上发生, 而这个元素就是我们单击的元素。然后,click事件沿着DOM树向上传播,在每一个节点上都发生,直到传播到document对象。
备注:
所有现代浏览器都支持事件冒泡,但是在具体实现上还是有一些差别。IE5.5 及更早版本中的事件冒泡会跳过<html>元素 (从<body>直接跳转到<document>)。IE9,Firefox,Chrome和Safari则将事件一直冒泡到window对象。
1.2 事件捕获
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预定目标之前捕获它。
虽然事件捕获是Navigator Communcator唯一支持的事件流模型,但是IE9,Safari,Chrome,Opera和Firefox目前也都支持这种事件流模型。
1.3 DOM事件流
DOM2级事件: 规定的事件流包括三个阶段:
事件捕获阶段,处于目标阶段和事件冒泡阶段
。
首先发生的是事件捕获,为截取事件提供了机会。然后是实际目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件作出响应。具体流经如下图所示。
<二> 事件处理程序
事件就是用户或浏览器自身执行的某种动作。诸如click,load和mouseover,都是事件的名字。而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以‘on’,因此click事件的事件处理程序就是onclick。为事件指定处理程序的方式有好几种
2.1 HTML事件处理程序
直接绑定在元素上,或者引入函数名
案例:
<body>
<input type="button" onclick="alert('test')" value="按钮">
<div onclick="test()">事件</div>
</body>
<script type="text/javascript">
function test() {
alert('test event bubbling')
}
</script>
2.2 DOM0级事件处理程序
通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值方法是在第四代web浏览器中出现的,而且至今仍然为所有浏览器所支持。原因一:简单;原因二:具有跨浏览器的优势。要使用JavaScript指定事件处理程序,首先必须取得一个要操作的对象的引用。
每个元素(包括window和document)都有自己的事件处理程序,这些属性通常全部小写,例如
<body>
<div id="btn">提示</div>
</body>
<script type="text/javascript">
var btn = document.getElementById('btn');
btn.onclick = function () {
alert('事件绑定成功')
}
</script>
如果要解除事件绑定
btn.onclick = null;
2.3.1 DOM2级事件处理程序
"DOM2级事件" 定义了两个方法,用于处理指定事件和删除事件处理程序的操作,addEventListener()和removeEventListener()。所有DOM节点中都包含这两个方法,并且他们都接受3个参数:要处理事件名,作为事件处理程序的函数和一个布尔值。 最后这个参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调动事件处理程序。
可以为一个元素添加多个事件处理程序,事件处理程序会按照它们添加的顺序依次执行。
<body>
<div id="btn">提示</div>
</body>
<script type="text/javascript">
var btn = document.getElementById('btn');
btn.addEventListener('click',function () {
console.log('第一个事件');
},false);
btn.addEventListener('click',function () {
console.log('hello world');
},false);
</script>
IE事件处理程序
IE实现了与DOM中类型的两个方法:attachEvent()和detachEvent()。这两个方法都接受相同的参数:事件处理程序名称,事件处理程序函数。由于IE8及更早版本只支持冒泡事件,所以通过attachEvent()添加的事件出现程序都会被添加到冒泡阶段
在IE中使用attachEvent()与DOM0级方法的主要区别在于事件处理程序的作用域。
在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域下运行,因此this等于window。
注意:
1. IE事件处理程序不是以添加它们的顺序执行,而是以相反的顺序执行;
2. 绑定事件时,IE要添加 ‘on’。
2.3.3 移除事件处理程序
要移除事件处理程序,要传入函数名。不能移除匿名函数,移除匿名函数时无效的。
<body>
<div id="btn">提示</div>
</body>
<script type="text/javascript">
var btn = document.getElementById('btn');
var handler = function () {
console.log('第一个事件');
};
btn.addEventListener('click',handler,false);
btn.removeEventListener('click',handler,false);
</script>
跨浏览器的事件处理程序。
var EventUtil = {
addHandler:function (element, type, handler) {
if(element.addEventListener){
element.addEventListener(type,handler,false)
}else if(element.attachEvent){
element.attachEvent('on'+type,handler)
}else {
element['on'+type] = handler;
}
},
rmoveHandler:function (element, type, handler) {
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent('on'+type,handler);
}else {
element['on'+type] = null;
}
},
};