五、事件处理
客户端javascript程序采用了异步事件驱动编程模型。在这种程序设计风格下,当文档,浏览器,元素或与之相关的对象发生某些有趣的事情时,web浏览器就会产生事件(event)。例如web浏览器加载完文档时。如果javascript程序关注特定类型的事件,那么它可以注册当这类事件发生时要调用的一个或多个函数。
事件类型(event type):是一个用来说明发生什么类型事件的字符串。例如“mousemove”表示用户移动鼠标。由于事件类型只是一个字符串,因此实际上有时会称之为事件名字(event name)。注意事件名字无论是多少个单词组成都是小写。
事件目标(event target):是发生的事件或与之相关的对象。当讲事件时,必须同时指明类型和目标。例如,window上的load事件或button上的click事件,XMLHttpRequest对象的readystatechange事件。
事件处理程序(event handler)或事件监听程序(event listener)是处理或响应事件的函数。需要先注册事件处理程序函数到指定目标的指定事件上,然后当在特定目标上发生特定类型的事件时,浏览器会调用对应的处理程序。
事件对象(event object)是与特定事件相关且包含有关该事件详细信息的对象。事件对象作为参数传递给事件处理程序函数(不包括IE8及之前的版本,这些浏览器中有时仅能通过全局变量event才能得到)。所有的事件对象都有用来指定时间类型的type属性和指定事件目标的target属性。如,鼠标事件的相关对象会包含鼠标指针的坐标等信息。
事件传播(event propagation)是浏览器决定哪个对象触发其事件处理程序的过程。对于单个对象的特定事件(比如window对象的load事件),必须是不能传播的。当文档元素上发生某个类型的事件时,他们会在文档树上向上传播(称为冒泡,bubble).
事件传播的另外一种形式称为事件捕获(event capturing),在容器元素上注册的特定处理程序由机会在事件传播到真实目标之前拦截它。
注册事件处理程序
注册事件处理程序由两种基本方式。
第一种方式,给事件目标对象或文档元素设置事件属性。
第二种方式更新且更通用,是将事件处理程序传递给对象或元素的一个方法。
1. 设置javascript对象属性为事件处理程序
按照约定,事件处理程序属性的名字由“on”后面跟着事件名:onclick,onchange,onload等。都是小写。如:
Window.onload = function(){ … };
Element.onsubmit = function(){ … };
这种方式很方便,但有一个限制,每种事件类型将最多只有一个处理程序。如果想有不只一种处理程序则可以用addEventListener()等方法。
2. 设置html标签属性为事件处理程序(不推荐)
也可以在html元素定义时用html的属性来定义事件处理程序,这时,属性的值就是javascript代码字符串,多条语句用分号分隔就可以了。如:
<button onclick=”alert(‘thanks.’);”>hit me</button>
客户端编程的通用风格是保持html内容和脚本分开,所以尽量不要用这种方式。
3. addEventListener()
在除了IE8及之前的版本的所有浏览器(这些浏览器提供了另外的方法attachEvent())都支持的标准事件模型中,任何能称为事件目标的对象都定义了一个addEventListener()的方法,使用这个方法可以为事件目标注册多个处理程序。
如:
element.addEventListener(“click”, function(){…}, false);
相对addEventListener()的是removeEventListener()方法,它从对象中删除事件处理函数。
4. attachEvent()
IE9之前的浏览器不支持addEventListener()和removeEventListener(), IE5及以后的浏览器提供了类似的方法attachEvent()和detachEvent()。这两个方法只有两个参数。
事件处理程序的调用
一旦注册了事件处理程序,浏览器会在指定对象上发生指定类型事件时自动调用它。
1. 事件处理程序的参数
通常调用事件处理程序时把事件对象作为他们的一个参数,但也有例外。IE8及之前版本中,通过设置属性注册事件处理程序,当调用他们时并未传递事件对象,需要通过全局对象window.event获得事件对象。
2. 事件处理程序的运行环境
当通过设置属性注册事件处理程序时,看起来好像是在文档元素上定义了新方法:
e.onclick = function(){};
事件处理程序在事件目标上定义,在处理程序内,this关键字指的是事件目标。
当调用addEventListener()注册时,处理程序也是使用事件目标作为他们的this值。
但是,使用html属性注册和attachEvent()注册的处理程序作为函数调用,他们的this值是全局(window)对象,这根事件对象event的例外一致。
3. 事件处理程序的返回值
通常返回值false就是告诉浏览器不要执行这个事件相关的默认操作。如,表单提交按钮的onclick事件能返回false阻止浏览器提交表单。
4. 调用顺序
文档元素或其他对象可以为指定类型事件注册多个事件处理程序,调用处理程序的顺序如下:
1) 通过设置对象属性或html属性的处理程序一直优先调用。
2) 使用addEventListener注册的处理程序按照它们的注册顺序调用。
3) 使用attachEvent注册的处理程序可能按照任何顺序调用,所以代码不应该依赖调用顺序。
5. 事件传播
当事件目标是window对象或其他一些单独对象(如XMLHttpRequest)时,浏览器简单的通过调用对象上的处理程序响应事件。
当事件目标是文档或文档元素时,情况比较复杂。大部分会冒泡到dom树根,调用目标的父元素的事件处理程序,然后调用祖父元素上的事件处理程序,直到window对象。事件冒泡为在大量单独文档元素上注册吹程序提供了替代方案,即在共同的祖父元素上注册一个处理程序来处理所有的事件,例如,在form上注册change来取代在表单的每个元素上注册change事件处理程序。
发生在文档元素上的大部分事件都会冒泡,例外的是focus,blur和scroll事件不会。
事件冒泡是事件传播的第三个阶段,目标对象本身的事件处理程序调用时第二个阶段。第一个阶段发生在目标程序处理之前,称为捕获阶段。之前addEventListener方法的第三个参数设置为true,则这个事件处理程序被注册为捕获处理程序,在第一阶段就触发。所有的浏览器都支持事件冒泡,但是事件捕获却不是都支持,一般addEventListener的第三个参数都设置为false.
捕获的顺序与冒泡相反,是window对象先捕获,document对象,接着body对象,再后是dom树向下。直到事件目标的父元素位置。事件目标本身不会捕获事件,即使注册了捕获处理程序。