JavaScript事件处理

来源:互联网 发布:从微信里打开淘宝 编辑:程序博客网 时间:2024/06/16 17:41

客户端JavaScript程序采用了异步事件驱动编程模型。在这种程序设计风格下,当文档、浏览器、元素或与之相关的对象发生某些有趣的事情时,Web浏览器就会产生事件(event)。例如,当Web浏览器加载完文档、用户把鼠标指针移到超链接上或敲击键盘时,Web浏览器都会产生事件。如果 JavaScript应用程序关注特定类型的事件,那么它可以注册当这类事件发生时要调用的一个或多个函数。请注意,这种风格并不只应用于Web编程,所有使用图形用户界面的应用程序都采用了它,它们静待某些事情发生(即,它们等待事件发生),然后它们响应。

事件类型(event type)是一个用来说明发生什么类型事件的字符串。例如“mouseover”表示用户移动鼠标,“keydown”表示键盘上某个键被按下,而“load”表示文档(或某个其他资源)从网络上加载完毕。由于事件类型只是一个字符串,因此实际上有时会称之为事件名字(event name),我们用这个名字来标识所谈论的特定类型的事件。现代浏览器支持许多事件类型。
事件目标(event target)是发生的事件或与之相关的对象。当讲事件时,我们必须同时指明类型和目标。例如, window上的load事件或<button>元素的click事件。在客户端的JavaScript应用程序中, Window、Document和Element对象是最常见的事件目标,但某些事件是由其他类型的对象触发。例如由XmlHttpRequest对象触发的readystatechange事件。

事件处理程序(event handler)或事件监听程序(event listener)是处理或响应事件的函数。应用程序通过指明事件类型和事件目标,在Web浏览器中注册它们的事件处理程序函数。当在特定的目标上发生特定类型的事件时,浏览器会调用对应的处理程序。当对象上注册的事件处理程序被调用时,我们有时会说浏览器“触发”(fire、 trigger)和“派发”( dispatch)了事件。有很多注册事件处理程序的方法。
事件对象(event object)是与特定事件相关且包含有关该事件详细信息的对象。事件对象作为参数传递给事件处理程序函数(不包括IE8及之前版本,在这些浏览器中有时仅能通过全局变量event才能得到)。所有的事件对象都有用来指定事件类型的type属性和指定事件目标的target属性。(在IE8及之前版本中用srcelement而非target。)每个事件类型都为其相关事件对象定义一组属性。例如,鼠标事件的相关对象会包含鼠标指针的坐标,而键盘事件的相关对象会包含按下的键和辅助键的详细信息。许多事件类型仅定义了像type和 target这样少量的标准属性,就无法获取许多其他有用的信息。对于这些事件而言,只是事件简单地发生,无法得到事件的详细信息。

事件传播(event propagation)是浏览器决定哪个对象触发其事件处理程序的过程。对于单个对象的特定事件(比如Window对象的load事件),必须是不能传播的。当文档元素上发生某个类型的事件时,然而,它们会在文档树上向上传播或“冒泡”( bubble)。如果用户移动鼠标指针到超链接上,在定义这个链接的元素上首先会触发mousemove事件,然后是在容器元素上触发这个事件。有时,在Document或其他容器元素上注册单个事件处理程序比在每个独立的目标元素上都注册处理程序要更方便。事件处理程序能通过调用方法或设置事件对象属性来阻止事件传播,这样它就能停止冒泡且将无法在容器元素上触发处理程序。
事件传播的另外一种形式称为事件捕获(event capturing),在容器元素上注册的特定处理程序有机会在事件传播到真实目标之前拦截(或“捕获”)它。IE8及之前版本不支持事件捕获,所以不常用它。但是,当处理鼠标拖放事件时,捕获或“夺取”鼠标事件的能力是必需的。

一些事件有与之相关的默认操作。例如,当超链接上发生click事件时的默认操作是按照链接加载新页面。事件处理程序可以通过返回一个适当的值、调用事件对象的某个方法或设置事件对象的某个属性来阻止默认操作的发生。这有时称为“取消”事件。

注册事件处理程序

注册事件处理程序最简单的方式就是通过设置事件目标的属性为所需事件处理程序函数。按照约定,事件处理程序属性的名字由“on”后面跟着事件名组成: onclick、onchange、onload、onmouseover等。注意这些属性名是区分大小写的,所有都是小写,即使事件类型是由多个词组成(比如“ readystatechange”)。下面是两个事件处理程序注册示例:

//设置 Window对象的 unload属性为一个函数,//该函数是事件处理程序:当文档加载完毕时调用它window.onload = function ( ) {//查找一个<form>元素var elt = document.getelementbyid("shipping_address");//注册事件处理程序函数,//在表单提交之前调用它elt.onsubmit = function ( ) { return validate(this); }}

这种事件处理程序注册技术适用于所有浏览器的所有常用事件类型。一般情况下,所有广泛实现的Web API定义的事件都允许通过设置事件处理程序属性来注册处理程序。

事件处理程序属性的缺点是其设计都是围绕着假设每个事件目标对于每种事件类型将最多只有一个处理程序。如果想编写能够在任意文档中都能使用的脚本库代码,更好的方式是使用一种不修改或覆盖任何已有注册处理程序的技术(比如addEventListener())。

用于设置的文档元素事件处理程序属性( property)也能换成对应HTML标签的属性(attribute)。如果这样做,属性值应该是 JavaScript代码字符串。这段代码应该是事件处理程序函数的主体,而非完整的函数声明。也就是说,HTML事件处理程序代码不应该用大括号包围且使用 function关键字作为前缀。例如:

<button onclick="alert('Thank you');")>点击这里</button>
在除IE8及之前版本外的所有浏览器都支持的标准事件模型中,任何能成为事件目标的对象----这些对象包括 Window对象、 Document对象和所有文档元素----都定义了一个名叫addEventListener()的方法,使用这个方法可以为事件目标注册事件处理程序。addEventListener()接受三个参数。第一个是要注册处理程序的事件类型,这个事件类型(或名字)是字符串,但它不应该包括用于设置事件处理程序属性的前缀“on”。第二个参数是当指定类型的事件发生时应该调用的函数。最后一个参数是布尔值。通常情况下,会给这个参数传递fa1se。如果相反传递了true,那么函数将注册为捕获事件处理程序,并在事件不同的调度阶段调用。第三个参数可以省略,默认值为false。

下面这段代码在<button>元素上注册了click事件的两个处理程序。注意所用两个技术之间的不同:

<button id="mybutton">click me</button><script>var b = document.getElementById("mybutton");b.onclick = function(){alert("thanks for clicking me!")};b.addEventListener("click", function(){alert("thanks again!")}, false);</script>

用“click"作为第一个参数调用addEventListener()不会影响 onclick属性的值。在前面的代码中,单击按钮会产生两个alert()对话框。更重要的是,能通过多次调用addEventListener()为同一个对象注册同一事件类型的多个处理程序函数。当对象上发生事件时,所有该事件类型的注册处理程序都会按照注册的顺序调用。使用相同的参数在同一个对象上多次调用addEventListener()是没用的,处理程序仍然只注册一次,同时重复调用也不会改变调用处理程序的顺序。

相对addEventListener()的是removeEventListener()方法,它同样有三个参数,从对象中删除事件处理程序函数而非丰添加,它常用于临时注册事件处理程序,然后不久就删除它。例如,当你要得到mousedown事件时,可以为mousemove和mouseup事件注册临时捕获事件处理程序来看看用户是否拖动鼠标。当mouseup事件到来后,可以注销这些事件处理程序。在这种情况下,事件处理程序移除代码如下所示:

document.removeEventListener("mousemove", handleMouseMove, true);document.removeEventListener("mouseup", handleMouseUp, true);
事件处理程序的调用

一旦注册了事件处理程序,浏览器就会在指定对象上发生指定类型的事件时自动调用它。

通常调用事件处理程序时把事件对象作为它们的一个参数,事件对象的属性提供了有关事件的详细信息。例如button.addEventListener('click', function(eve){console.log(eve);}, false);将会在按钮被点击时打印一个MouseEvent类型的事件对象。
在事件处理程序内,this关键字指示的是事件目标。甚至当使用addEventListener()注册时,调用的处理程序使用事件目标作为它们的this值。

事件处理程序的返回值

通过设置对象属性或HTML属性注册事件处理程序的返回值有时是非常有意义的。通常情况下,返回值 false就是告诉浏览器不要执行这个事件相关的默认操作。例如,表单提交按钮的onclick事件处理程序能返回false阻止浏览器提交表单。(当用户的输入在客户端验证失败时,这是有用的。)类似地,如果用户输入不合适的字符,输入域上的onkeypress事件处理程序能通过返回false来过滤键盘输入。

调用顺序

文档元素或其他对象可以为指定事件类型注册多个事件处理程序。当适当的事件发生时,浏览器必须按照如下规则调用所有的事件处理程序:

1. 通过设置对象属性或者HTML属性注册的处理程序一直优先调用;

2. 使用addEventListener()注册的处理程序按照它们的注册顺序调用。

事件传播

当事件目标是Window对象或其他一些单独对象(比如XmlHttpRequest)时,浏览器简单地通过调用对象上适当的处理程序响应事件。当事件目标是文档或文档元素时,情况比较复杂。

在调用在目标元素上注册的事件处理函数后,大部分事件会“冒泡”到DOM树根。调用目标的父元素的事件处理程序,然后调用在目标的祖父元素上注册的事件处理程序。这会一直到Document对象,最后到达Window对象。事件冒泡为在大量单独文档元素上注册处理程序提供了替代方案,即在共同的祖先元素上注册一个处理程序来处理所有的事件。

发生在文档元素上的大部分事件都会冒泡,值得注意的例外是focus、blur和scroll事件。文档元素上的load事件会冒泡,但它会在Document对象上停止冒泡而不会传播到Window对象。只有当整个文档都加载完毕时才会触发Window对象的load事件。
事件冒泡是事件传播的第三个“阶段”。目标对象本身的事件处理程序调用是第二个阶段。第一个阶段甚至发生在目标处理程序调用之前,称为“捕获”阶段。回顾之前addEventListener()把一个布尔值作为其第三个参数。如果这个参数是true,那事件处理程序被注册为捕获事件处理程序,它会在事件传播的第一个阶段调用。事件冒泡得到广泛的支持,它能用在包括IE在内的所有浏览器中,且无论事件处理程序用哪种方式注册(除非它们被注册为捕获事件处理程序)。而事件捕获只能用于以addEventListener()注册且第三个参数是true的事件处理程序中。

事件传播的捕获阶段像反向的冒泡阶段。最先调用Window对象的捕获处理程序,然后是Document对象的捕获处理程序,接着是body对象的,再然后是DOM树向下,以此类推,直到调用事件目标的父元素的捕获事件处理程序。在目标对象本身上注册的捕获事件处理程序不会被调用。
事件捕获提供了在事件没有送达目标之前查看它们的机会。事件捕获能用于程序调试或用于后面介绍的事件取消技术,过滤掉事件从而使目标事件处理程序绝不会被调用。事件捕获常用于处理鼠标拖放,因为要处理拖放事件的位置不能是这个元素内部的子元素。

事件取消

在支持addEventListener()的浏览器中,可以通过调用事件对象的preventDefault()方法取消事件的默认操作。

取消事件相关的默认操作只是事件取消中的一种,我们也能取消事件传播。在支持addEventListener()的浏览器中,可以调用事件对象的一个 stopPropagation()方法以阻止事件的继续传播。如果在同一对象上定义了其他处理程序,剩下的处理程序将依旧被调用,但调用 stopPropagation()之后任何其他对象上的事件处理程序将不会被调用。stopPropagation()方法可以在事件传播期间的任何时间调用,它能工作在捕获期阶段、事件目标本身中和冒泡阶段。

原创粉丝点击