JavaScript与HTML之间的交互是通过事件实现的。
一、事件流:
事件流描述的是从页面中接收事件的顺序。
IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。
1、事件冒泡:
IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
事件冒泡的终点可能是document对象,也可能是window对象,这与用户使用的浏览器有关。
所有现代浏览器都支持事件冒泡,但在具体实现上还是有一些差别。
IE9、Firefox、Chrome和Safari则将事件一直冒泡到window对象。
2、事件捕获:
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。
虽然事件捕获是Netscape Communicator唯一支持的事件流模型,但IE9、Safari、Chrome、Opera和Firefox目前也都支持这种事件流模型。尽管“DOM2级事件”规范要求事件应该从document对象开始传播,但这些浏览器都是从window对象开始捕获事件的。
由于老版本的浏览器不支持,因此很少有人使用事件捕获。我们也建议读者放心地使用事件冒泡,在有特殊需要时再使用事件捕获。
3、DOM事件流:
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
多数支持DOM事件流的浏览器都实现了一种特定的行为:即使“DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。
二、事件处理程序:
事件就是用户或浏览器自身执行的某种动作。
而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以“on”开头。
1,HTML事件处理程序:
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执行的JavaScript代码。
在HTML中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本。
事件处理程序的代码在执行时,有权访问全局作用域中的任何代码。
这样指定事件处理程序具有一些独到之处。首先,这样会创建一个封装着元素属性值的函数。这个函数中有一个局部变量event,也就是事件对象。
通过事件变量,可以直接访问事件对象。
在这个函数内部,this值等于事件的目标元素。
<input type = "button"
name = "button1"
value = "lucas's first button"
onclick = "console.log(this.value)">
//lucas's first button
<input type = "button"
name = "button2"
value = "lucas's second button"
onclick = "console.log(event.type)">
//click
关于这个动态创建的函数,另一个有意思的地方是它扩展作用域的方式。在这个函数内部,可以像访问局部变量一样访问document及该元素本身的成员。
扩展的原理是使用了with语句。
如果当前元素是一个表单输入元素,则作用域中还会包含访问表单元素(父元素)的入口。
实际上,这样扩展作用域的方式,无非就是想让事件处理程序无需引用表单元素就能访问其他表单字段。
举个例子帮助理解(在前面代码的基础上):
<input
type = "button"
name = "button3"
value = "lucas's last button"
onclick = "console.log(button1.value)">
//lucas's first button
不过,在HTML中指定事件处理程序有两个缺点。
首先,存在一个时差问题。
为此,很多HTML事件处理程序都会被封装在一个try-catch块中,以便错误不会浮出水面。
另一个缺点是,这样扩展事件处理程序的作用域链在不同浏览器中会导致不同错误。
通过HTML指定事件处理程序的最后一个缺点是HTML与JavaScript代码紧密耦合。
2、DOM0级事件处理程序:
通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
要使用JavaScript指定事件处理程序,首先必须取得一个要操作的对象的引用。
每个元素(包括window和document)都有自己的事件处理程序属性,这些属性通常全部小写,例如onclick。将这种属性的值设置为一个函数,就可以指定事件处理程序。
注意,在这些代码运行以前不会指定事件处理程序。
使用DOM0级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行;换句话说,程序中的this引用当前元素。
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。
同样地,可以用这种方式来删除指定的事件处理程序,只要将事件处理程序属性的值设为null就可以了。
3、DOM2级事件处理程序:
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
与DOM0级方法一样,这里添加的事件处理程序也是在其依附的元素的作用域中运行。
使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着通过addEventListener()添加的匿名函数将无法移除。
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。如果不是特别需要,我们不建议在事件捕获阶段注册事件处理程序。
4、IE事件处理程序: