JavaScript 与 HTML 之间的交互是通过事件实现的。事件,就是文档或者浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。类似于设计模式中的观察员模式。支持页面的行为与页面的外观之间的松散耦合。
事件流
事件流描述的是从页面中接收事件的顺序。
IE和 Netscape 开发团队居然提出了差不多是完全相反的事件流的概念。IE 的事件流是事件冒泡流,而Netscape Communicator 的事件流是事件捕获流。
1.事件冒泡
IE的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
<!DOCTYPE html> <html> <head> <title>event js</title> </head> <body> <div id="myDiv">Click Me</div> </body> </html>
上例中,如果你点击了页面中的div 元素, 那么这个click 事件会按照如下顺序传播:
- <div>
- <body>
- <html>
- document
也就是说,click事件首先在<div> 元素上发生,而这个元素就是我们单机的元素。然后,click事件沿着DOM树向上传播,在每一级节点上都会发生,知道传播到 document 对象。
所有现代浏览器都支持事件冒泡,但在具体实现上还有一些差别。IE 5.5 及更早的版本中的事件冒泡会跳过<html> 元素。IE9、firefox、 Chrome 和 Safari 则将事件一直冒泡到 window 对象。
2.事件捕获
Netscape Communicator 团队提出的另一种事件流叫做事件捕获(event capturing)。事件波活的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件事件捕获的用意在于它事件到达预定目标之前捕获它。仍然以上面的html 页面作为演示事件捕获的例子。那么单击<div>元素就会以下列顺序除法click 事件。
- document
- <html>
- <body>
- <div>
在事件捕获过程中,document 对象首先接收到click 事件,然后事件沿着 DOM 树依次向下,一直传播到事件的实际目标,即<div> 元素。
虽然事件捕获是 Netscape Communicator 唯一支持的事件流模型,但IE9、Safari、Chrome、Opera 和 Firefox 目前也支持这种事件流模型。进管DOM2 及事件规范要求事件应该从document 对象开始传播,但这些浏览器都是从 window 对象开始捕获事件的。
由于老版本的浏览器不支持,因此很少有人使用事件捕获。所以建议使用事件冒泡,在有特殊需要是在使用事件捕获。
3.DOM事件流
DOM2级事件 规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出相应。
事件处理程序
事件就是用户或浏览器自身执行的某种动作。诸如 click、load、mouseover,都是事件的名字,而相应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以on 开头,因此 click 事件的事件处理程序就是 onclick,以此类推。
1.某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML 特性来制定。这个特性的值应该是能够执行的 JavaScript 代码。
<div id="myDiv" onclick="alert('Clicked')">Click Me</div>
也可以调用其他地方定义的脚本:
<div id="myDiv" onclick="showMessage()">Click Me</div> <script type="text/javascript"> function showMessage(){ alert("Hello world!"); } </script>
在HTML 中指定事件处理程序存在一个时差问题:因为用户可能会在HTML 元素一出现在页面上就触发相应的事件,但当时的事件可能尚不具备执行条件。如上例中,假设代码还没有加载进来用户就点击了div,就会引发错误。为此,很多HTML 事件处理程序都会被封装在一个try-catch 块中,以便错误不会浮出水面。
<div id="myDiv" onclick="try{showMessage();}catch(ex){}">Click Me</div>
还有一个缺点是HTML 与 JavaScript 代码紧密耦合。如果要更换事件处理程序,就要改动两个地方:HTML 代码 和 JavaScript 代码。这正是许多开发人员摒弃HTML 事件处理程序,转而使用JavaScript 制定事件处理程序的原因。
2.DOM0 级事件处理程序
每个元素(包括window 和 document)都有自己的事件处理程序属性,这些属性通常全部小写,例如:onclick。将这种属性设置为一个函数,就可以指定事件处理程序。
var div = document.getElementById("myDiv");
div.onclick = function(){
alert("click");
}
使用DOM0 级方法制定的事件处理程序被认为是元素的方法。因此这时候的事件处理程序是在元素的作用域中运行,所以承诺须中的this 引用当前元素。
var div = document.getElementById("myDiv"); div.onclick = function(){ alert(this.id); //myDiv }
可以通过将事件处理程序属性的值设置为null 来删除通过DOM0 级方法制定的事件处理程序。
div.onclick = null;
3.DOM2 级事件处理程序
DOM2级事件 定义了两个方法,用于处理制定和删除事件处理程序的操作:addEventListener() 和 removeEventListener()。所有DOM节点都包含者两个方法,并且他们都接受三个参数:要处理的事件名、作为事件处理程序的函数 和 一个布尔值。最后这个布尔值参数如果是true,表示在捕获节点调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
var div = document.getElementById("myDiv"); div.addEventListener("click", function(){ alert(this.id); }, false);
使用DOM2 级 方法添加事件处理程序的主要好处是可以添加多个事件处理程序。
var div = document.getElementById("myDiv"); div.addEventListener("click", function(){ alert(this.id); }, false); div.addEventListener("click", function(){ alert("hello world"); }, false);
通过addEventListener() 方法添加的事件处理程序只能使用 removeEventListener() 来移除,移除时传入的参数与添加处理程序使用的参数相同。这也意味着 通过 addEventListener() 添加的匿名函数将无法移除。
var div = document.getElementById("myDiv"); var handler = function(){ alert(this.id); } div.addEventListener("click", handler, false); div.removeEventListener("click", handler, false);
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最好只在需要在事件到达目标之前截获它的时候才将事件处理程序添加到捕获阶段。如果不是特别需要,不建议在事件捕获阶段注册事件处理程序。
4.IE 事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent() 和 detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。由于IE8 及更造版本只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
div.attachEvent("onclick", function(){ alert("clicked"); });
在IE中使用attachEvent() 与使用DOM0 级方法的主要区别在于事件处理程序的作用域。在使用DOM0级方法的情况下,事件处理程序会在其所属元素作用域中运行;在使用attachEvent() 方法的情况下,事件处理程序会在全局作用域中运行,因此this 等于 window。
同样,通过attachEvent() 方法添加的匿名函数无法通过detachEvent() 来移除,detachEvent() 同attachEvent() 方法的参数是一样的。
5.跨浏览器的事件处理程序
只要恰当的使用能力检测,就可以编写自己的跨浏览器的方式处理事件。只需要关注冒泡阶段。
第一个要创建的方法是addHandler(),它的职责是是情况分别使用DOM0 级方法、DOM2级方法或者IE方法来添加事件。这个方法属于一个名为EventUtil 的对象。addHandler() 方法接收三个参数:要操作的元素、事件名称 和 事件处理程序函数。
与 addHandler() 对应的方法是 removeHandler() ,它也接受相同的参数。这个方法的职责是移除之前添加的事件处理程序。
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; } }, removeHandler: 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; } } };
事件对象
在触发DOM上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件类型 以及 其他与特定事件相关的信息。例如鼠标操作导致的事件对象中,会包含鼠标位置信息,而键盘操作导致的事件对象中,会包含与按下的键有关的信息。所有的浏览器都支持event对象,但支持的方式不同。
1.DOM中的事件对象
兼容DOM的浏览器会将一个 event 对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。
event 对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。不过所有事件都会有下列成员。
属性/方法 | 类型 | 读/写 | 说明 |
currentTarget | Element | 只读 | 其事件处理程序当前正在处理事件的那个元素。 |
target | Element | 只读 | 事件的目标。 |
type | String | 只读 | 被触发的事件类型。 |
bubbles | Boolean | 只读 | 表明事件是否冒泡。 |
stopImmediatePropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡,同时组织任何事件处理程序被调用(DOM3 级事件中新增) |
stopPropagation() | Function | 只读 | 取消事件的进一步捕获或冒泡。如果bubbles 为 true,则可以使用这个方法。 |
eventPhase | Integer | 只读 | 调用事件处理程序的阶段:1表示捕获阶段,2表示“处于目标”,3表示冒泡阶段。 |
cancelable | Boolean | 只读 | 表明是否可以取消事件的默认行为。 |
preventDefault() | Function | 只读 | 取消事件的默认行为。如果cancelable 是 true,则可以使用这个方法。 |
defaultPrevented | Boolean | 只读 | 为true 表示已经调用了 preventDefault() (DOM3 级事件中新增)。 |
detail | Integer | 只读 | 与事件相关的细节信息。 |
trusted | Element | 只读 | 为true 表示见见是浏览器生成的。为false 表示事件是由开发人员通过调用JavaScript创建的(DOM3级事件中新增)。 |
view | AbstractView | 只读 | 与事件关联的抽象视图。等同于发生事件的 window 对象。 |
在事件处理程序内部,对象this 始终等于 currentTarget 的值,而 target 则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget 和 target 包含相同的值。
var div = document.getElementById("myDiv"); div.onclick = function(event){ alert(event.currentTarget === this); //true alert(event.target === this); //true };
如果事件处理程序存在于节点的父节点中,那么currentTarget、this 将代表父节点,而target 表示的是子节点。
var div = document.getElementById("myDiv"); document.body.onclick = function(event){ alert(event.currentTarget === this); //true alert(this === document.body); //true alert(event.target === div); //true };
要组织特定事件的默认行为,可以使用 preventDefault() 方法。例如,链接的默认行为就是在被单击时会导航到其 href 特性制定的URL。如果你想组织链接导航这一默认行为,那么通过链接的onclick 事件处理程序可以取消它。
var link = document.getElementById("myLink"); link.onclick = function(event){ event.preventDefault(); };
使用 stopPropagation() 方法可以立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或者冒泡。例如,直接添加到一个节点的事件处理程序可以调用 stopPropagation(),从而避免除法注册在document.body 上面的事件处理程序。
var div = document.getElementById("myDiv"); document.body.onclick = function(event){ alert("hello"); //不会执行 }; div.onclick = function(event){ event.stopPropagation(); alert("propagation stoped"); };
只有在事件处理程序执行期间,event对象才会存在,一旦事件处理程序执行完成,event对象就会被销毁。
2.IE中的事件对象
与访问DOM中的event 对象不同,要访问IE中的event对象有集中不同的方式,取决于指定事件处理程序的方法。在使用DOM0 级方法添加事件处理程序时, event 对象作为window 对象的一个属性存在。
var div = document.getElementById("myDiv"); btn.onclick = function(){ var event = window.event; alert(event.type); //click };
如果事件处理程序是使用attachEvent() 添加的,那么会有一个event 对象作为参数被传入事件处理程序函数中。
var div = document.getElementById("myDiv"); div.attachEvent("onclick", function(event){ alert(event.type); });
在像这样使用attachEvent() 的情况下,也可以通过window 对象来访问event 对象,就像使用DOM0 级方法时一样。不过方便起见,同一个对象也会作为参数传递。
IE 的 event 对象同样也包含与创建它的事件相关的属性和方法。
属性/方法 | 类型 | 读/写 | 说明 |
cancelBubble | Boolean | 读/写 | 默认值为false,但将其设置为true就可以取消事件冒泡(与DOM中的 stopPropagation() 方法的作用相同)。 |
returnValue | Boolean | 读/写 | 默认值为true,但将其设置为false 就可以取消事件的默认行为(与DOM中的preventDefault() 方法的作用相同)。 |
srcElement | Element | 只读 | 事件的目标(与DOM 中的target 属性相同)。 |
type | String | 只读 | 被触发的事件的类型。 |
因为事件处理程序的作用域是根据制定它的方式来确定的,所以不能认为this 会始终你等于事件目标,故而,最好还是使用 event.srcElement 比较保险。
var div = document.getElementById("myDiv"); div.onclick = function(){ alert(window.event.srcElement === this); // true }; div.attachEvent("onclick", function(event){ alert(event.srcElement === this); //false });
3.跨浏览器的事件对象
继续补充EventUtil对象,增加对event 对象的支持。
var EventUtil = { addHandler: function(element, type, handler){ //... }, removeHandler: function(element, type, handler){ //... }, getEvent: function(event){ return event ? event : window.event; }, getTarget: function(event){ return event.target || event.srcElement; }, preventDefault: function(event){ if(event.preventDefault){ event.preventDefault(); }else{ event.returnValue = false; } }, stopPropagation: function(event){ if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true; } } };
使用时需要在页面中加入引入js文件的代码,使用方法如下:
<!DOCTYPE html> <html> <head> <title>event js</title> </head> <body> <div id="myDiv">Click Me</div> <script type="text/javascript" src="./eventutil.js"></script> <script type="text/javascript"> var div = document.getElementById("myDiv"); div.onclick = function(event){ event = EventUtil.getEvent(event); alert(event.type); //click alert(EventUtil.getTarget(event).id); //myDiv }; </script> </body> </html>
事件类型
Web浏览器中可能发生的事件有很多类型。不同的事件类型具有不同的信息,而DOM3级事件规定了一下几类事件。
- UI(User Interface,用户界面)事件,当用户与页面上的元素交互时触发。
- 焦点事件,当元素获得或失去焦点时触发。
- 鼠标事件,当用户通过鼠标在页面上执行操作时触发。
- 滚轮事件,当使用鼠标滚轮(或类似设备)时触发。
- 文本事件,当在文档中输入文本时触发。
- 键盘事件,当用户通过键盘在页面上执行操作时触发。
- 合成事件,当为IME(Input Method Editor,输入法编辑器)输入字符时触发。
- 变动(mutation)事件,当底层DOM结构发生变化是触发。
- 变动名称事件,当元素或属性名变动时触发。此类事件已被废弃,没有任何浏览器实现它们。
除了这几类事件之外,HTML5 也定义了一组事件,而有些浏览器还会在DOM 和BOM 中实现其他专有事件。这些转悠事件一般都是根据开发人员需求定制的,没有什么规范,因此不同浏览器实现有可能不一致。
1.UI事件
UI事件指的是那些不一定与用户操作有关的事件。这些事件在DOM规范出现之前,都是以这种或那种形式存在的,而在DOM 规范中保留是为了向后兼容。现有的UI 事件如下:
- load:当页面完全加载后在window 上触发,当所有框架都加载完毕是在框架集上面触发,当图像加载完毕时在<img>元素上触发,或者当嵌入的内容加载完毕时在<object> 元素上面触发。
- unload:当页面完全卸载后在window 上面触发,当所有框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在<object>元素上面触发。
- abort:当用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上触发。
- error:当发生JavaScript 错误时在window 上面触发,当无法加载图像时在<img>元素上面触发,当无法加载嵌入内容时在<object> 元素上面触发,或者当有一个或多个框架无法加载时在框架集上触发。
- select:当用户选择文本框(<input> 或 <texterea>)中的一个或多个字符时触发。
- resize:当窗口或框架的大小变化时在window 或 框架上触发。
- scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。
以上这些事件在DOM2级事件中都被归为HTML 事件,要确定浏览器是否支持DOM2级事件规定的HTML事件,可以使用以下代码:
var isSupported = document.implementation.hasFeature("HTMLEvents", "2.0");
要确定浏览器是否支持DOM3级事件 定义的事件,可以使用如下代码:
var isSupported = document.implementation.hasFeature("UIEvent", "3.0");
1.1 load事件
当页面完全加载后(包括所有图像、JavaScript文件、CSS文件等外部资源),就会触发window 上面的load事件。有两种定义onload 事件处理程序的方式。第一种使用JavaScript 代码:
EventUtil.addHandler(window, "load", function(event){ alert("Loaded!"); });
这里的event对象不包含有管这个事件的任何附加信息,但在兼容DOM的浏览器中,event.target 属性的值会被设置为document,而IE并不会为这个事件设置srcElement 属性。
第二种制定onload事件处理程序的方式是为body元素添加一个onload 特性,
<!DOCTYPE html> <html> <head> <title>event js</title> </head> <body onload="alert('Loaded!')"> <div id="myDiv">Click Me</div> </body> </html>
一般来说在window 上面发生的任何事件都可以在<body> 元素中通过相应的特性来指定,因为在HTML中无法访问window 元素。但是建议尽可能的使用JavaScript 方式。
在创建新的<img>元素时,可以为其制定一个事件处理程序,以便图像加载完毕后给出提示。此时最重要的是要在指定src属性之前先指定事件:
EventUtil.addHandler(window, "load", function(event){ var image = document.createElement("img"); EventUtil.addHandler(image, "load", function(event){ event = EventUtil.getEvent(event); alert(EventUtil.getTarget(event).src); //file:///home/zzl/Study/nodejs/studyjs/snal.jpg }); document.body.appendChild(image); image.src = "snal.jpg"; });
新图像元素不一定要从添加到文档后才开始下载,只要设置了sr属性就会开始下载。
同样的功能也可以通过使用DOM0 级的Image对象实现。在DOM出现之前,开发人员经常使用Image 对象在客户端预先加载图像。可以像使用<img>元素一样使用Image对象,只不过无法将其添加到DOM树中。
EventUtil.addHandler(window, "load", function(event){ var image = new Image(); //document.createElement("img"); EventUtil.addHandler(image, "load", function(event){ event = EventUtil.getEvent(event); alert(EventUtil.getTarget(event).src); //file:///home/zzl/Study/nodejs/studyjs/snal.jpg }); document.body.appendChild(image); image.src = "snal.jpg"; });
有的浏览器将Image对象实现为<img>元素,但并非所有浏览器都如此,所以最好将他们区别对待。
1.2 unload 事件
这个事件在文档被完全卸载后触发。只要用户从一个页面切换到另一个页面,就会触发unload 事件。而利用这个事件最多的情况是清除引用,以避免内存泄露。
同load事件类似,也有两种制定onunload 事件处理程序的方式。第一种是使用JavaScript:
EventUtil.addHandler(window, "unload", function(event){ alert("Unloaded!"); });
具体来说,当发生以下情况时,会发出 unload 事件:
- 点击某个离开页面的链接
- 在地址栏中键入了新的 URL
- 使用前进或后退按钮
- 关闭浏览器
- 重新加载页面
但是实际上,这行代码并没有执行,查了很多资料发现,因为onunload函数是在body已经关闭后才调动的,所以如果使用alert的话(alert父窗口是body)就会报错或不显示!
第二种方法仍然是在<body>元素添加一个特性(与load事件类似)。
<!DOCTYPE html> <html> <head> <title>event js</title> </head> <body onunload="alert('Unloaded!')"> <div id="myDiv">Click Me</div> </body> </html>
1.3. resize事件
当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize 事件。这个事件是在window 窗口上触发。
关于何时会触发resize 事件,不同浏览器有不同的机制,IE、Safari、Chrome和Opera 会在浏览器窗口变化了1像素时就触发resize事件,然后随着变化不断重复触发。Firefox则最实惠在用户停止调整窗口大小是才会触发resize事件。所以应该注意不要在这个事件中添加大计算量的代码,因为这些代码有可能被频繁执行,从而导致浏览器反应明显变慢。
2.焦点事件
焦点事件会在页面元素获得或时区焦点是触发。利用这个事件并与 document.hasFocus() 方法及 document.activeElement 属性配合,可以直销用户在页面上的行踪。
- blur:在元素失去焦点是触发。这个事件不会冒泡,所有的浏览器都支持它。在失去焦点的元素上触发。
- focus:在元素获得焦点时触发。这个事件不会冒泡,所有浏览器都支持它。在获得焦点的元素上触发。
- focusin:在元素获得焦点时触发。这个事件与HTML 事件 focus 等价,但它冒泡。支持这个事件的浏览器有IE5.5+、Safari 5.1+、Opera 11.5+ 和 Chrome。在获得焦点的元素上触发。
- focusout:在元素失去焦点时触发。这个事件是HTML 事件 blur 的通用版本。冒泡。支持这个事件的浏览器有IE5.5+、Safari 5.1+、Opera 11.5+ 和 Chrome。在失去焦点的元素上触发。
3.鼠标与滚轮事件
鼠标事件是Web开发中最常用的一类事件,毕竟鼠标还是最主要的定位设备。DOM3 级事件中定义了9个鼠标事件:
- click:在用户单机主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对于确保易访问性很重要,意味着onclick 事件处理程序既可以通过键盘可以通过鼠标执行。
- dblclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发。
- mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。
- mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。
- mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发。
- mouseleave:在位于元素上方的鼠标光标移动元素范围之外时触发。这个事件不冒泡,而且在光标移动的后代元素上不会触发。
- mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入另一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。
- mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。不能通过键盘触发这个事件。
- mousemove:当鼠标指针在元素内部移动是重复地触发。不能通过键盘触发这个事件。
只有在同一个元素上相机触发mousedown 和 mouseup 事件,才会触发 click 事件;如果mousedown 或 mouseup 中的一个被取消,就不会触发click 事件。类似的,只有触发两次click 事件,才会触发一次 dblclick 事件,如果有代码阻止了连续两次触发click,那么就不会触发dblclick 事件了。这四个事件触发的顺序始终如下:
- mousedown
- mouseup
- click
- mousedown
- mouseup
- click
- dblclick
<!DOCTYPE html> <html> <head> <title>event js</title> </head> <body> <div id="myDiv">Click Me</div> <div id="consol"></div> <script type="text/javascript" src="./eventutil.js"></script> <script type="text/javascript"> var click = document.getElementById("myDiv"); var info = document.getElementById("consol"); EventUtil.addHandler(click, "click", function(event){ var p = document.createElement("p"); p.appendChild(document.createTextNode("clicked")); info.appendChild(p); }); EventUtil.addHandler(click, "mousedown", function(event){ var p = document.createElement("p"); p.appendChild(document.createTextNode("mousedowned")); info.appendChild(p); }); EventUtil.addHandler(click, "mouseup", function(event){ var p = document.createElement("p"); p.appendChild(document.createTextNode("mouseuped")); info.appendChild(p); }); EventUtil.addHandler(click, "dblclick", function(event){ var p = document.createElement("p"); p.appendChild(document.createTextNode("dblclicked")); info.appendChild(p); }); </script> </body> </html>
双击后输出样式如下:
鼠标事件中还有一类滚轮事件,其实就是一个mousewheel 事件。稍后介绍。
3.1. 客户区坐标位置
鼠标事件都是在浏览器视口中特定位置上发生的。这个位置信息保存在事件对象的clientX 和clientY 属性中。所有浏览器都支持这两个属性,他们的值表示事件发生时,书白哦指针在视口中的水平和垂直坐标。
EventUtil.addHandler(click, "click", function(event){ var p = document.createElement("p"); event = EventUtil.getEvent(event); p.appendChild(document.createTextNode("Client coordinates: " + event.clientX + ", " + event.clientY)); info.appendChild(p); });
3.2. 页面坐标位置
页面坐标位置表示的坐标是从页面本身而非视口左边和顶边计算的,用pageX 和 pageY 表示。
EventUtil.addHandler(click, "click", function(event){ var p1 = document.createElement("p"); event = EventUtil.getEvent(event); p1.appendChild(document.createTextNode("Client coordinates: " + event.clientX + ", " + event.clientY)); var p2 = document.createElement("p"); p2.appendChild(document.createTextNode("Page coordinates: " + event.pageX + ", " + event.pageY)); info.appendChild(p1); info.appendChild(p2); });
在页面没有滚动的情况下,pageX 和pageY 的值与 clientX 和 clientY 的值相等。
3.3. 屏幕坐标位置
这个位置是相对于整个电脑屏幕的位置。通过screenX 和 screenY 属性表示。
3.4.修改键
虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。这些修改键就是 Shift、Ctrl、Alt、Meta(在windows键盘中是windows键,在苹果机中是 Cmd键),他们经常被用来修改鼠标事件的行为。DOM为此规定了四个属性,表示这些修改键的状态:shiftKey、ctrlKey、altKey 和 metaKey。这些属性包含的都是布尔值,true表示相应的键被按下,反之则为false。
var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "click", function(event){ event = EventUtil.getEvent(event); var keys = new Array(); if(event.shiftKey){ keys.push("shift"); } if(event.ctrlKey){ keys.push("ctrl"); } if(event.altKey){ keys.push("alt"); } if(event.metaKey){ keys.push("meta"); } alert("keys: " + keys.join(", ")); });
3.5. 相关元素
在发生mouseover 和 mouseout 事件时,还会涉及更多的元素。这两个事件都会涉及把鼠标指针从一个元素的边界之内移动到另一个元素的边界之内。
对mouseover事件而言,事件的主要目标是获得光标的元素,而相关元素就是哪个失去光标的元素。类似的,对mouseout事件而言,事件的主目标是失去光标的元素,而相关的元素则是获得光标的元素。
DOM通过event 对象的 relatedTarget 属性提供了相关元素的信息。这个属性只对于mouseover 和 mouseout 事件才包含值;对于其他事件,这个属性的值是null。 IE8 及之前的版本不支持relatedTarget属性,但提供了保存着同样信息的不同属性。在mouseover事件中 fromElement 属性中保存了相关元素;在mouseout 事件中 toElement 属性中保存着相关元素。
添加到EventUtil 对象中,以实现跨浏览器取得相关元素:
var EventUtil = { //。。。。 getRelatedTarget: function(event){ if(event.relatedTarget){ return event.relatedTarget; }else if(event.toElement){ return event.toElement; }else if(event.fromElement){ return event.fromElement; }else{ return null; } } };
使用方法如下:
var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "mouseover", function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); var relatedTarget = EventUtil.getRelatedTarget(event); alert("Mouse move from " + relatedTarget.tagName + " to " + target.tagName); //Mouse move from HTML to DIV });
3.6. 鼠标按钮
只有在主鼠标按钮被单击(或键盘回车键被按下)才会触发click 事件,因此检测按钮的信息并不必要。但对于mousedown 和 mouseup 事件来说,则在其event 对象存在一个button 属性,表示按下或释放的按钮。DOM的button 属性可能有如下3个值:
- 0:表示主鼠标按钮;
- 1:表示中间的鼠标按钮(鼠标滚轮按钮);
- 2:表示次鼠标按钮。
IE8 及之前版本也提供了button 属性,但这个属性的值与DOM 的button 属性大相径庭。
- 0:表示没有按下按钮;
- 1:表示按下了主鼠标按钮;
- 2:表示按下了次鼠标按钮;
- 3:表示同时按下了主次鼠标按钮;
- 4:表示按下了中间的鼠标按钮;
- 5:表示同时按下了主鼠标按钮和中间的鼠标按钮;
- 6:表示同时按下了次鼠标按钮和中间的鼠标按钮;
- 7:表示同事按下了三个鼠标按钮。
由于单独使用能力检测无法确定差异(两种模型有同名的button 属性),因此可以通过检测浏览器是否支持DOM版鼠标事件来判断是否IE浏览器。
var EventUtil = { //。。。 getButton: function(event){ if(document.implementation.hasFeature("MouseEvents", "2.0")){ return event.button; }else{ switch(event.button){ case 0: case 1: case 3: case 5: case 7: return 0; case 2: case 6: return 2; case 4: return 1; } } } };
3.7 触摸设备
iOS 和 Android 设备的实现非常特别,因为这些设备没有鼠标。在面向iPhone和 iPad 中的Safari 开发是,要记住:
- 不支持dblclick 事件。双击浏览器窗口会放大画面,而且没有办法改变该行为;
- 轻击可单击元素会触发 mousemove 事件。如果此操作会导致内容变化,将不在有其他事件发生;如果屏幕没有因此变化,那么会一次发生mousedown、mouseup 和 click 事件。轻击不可单击的元素不会触发任何事件。可单击的元素是指那些单击可产生默认操作的元素(如链接),或者那些已经被制定了onclick 事件处理程序的元素。
- mousemove 事件也会触发mouseover 和 mouseout 事件。
- 两个手指放在屏幕上且页面随手指移动而滚动时会触发mousewheel 和 scroll 事件。
4.键盘与文本事件
有3个键盘事件:
- keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
- keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。
- keyup:当用户释放键盘上的键时触发。
虽然所有的元素都支持以上3个事件,但是只有在用户通过文本框输入文本时才最常用到。
只有一个文本事件:textInput。这个事件是对keypress 的补充,用意是将文本显示给用户之前更容易拦截文本。在文本插入文本框之前会触发textInput 事件。
在用户按了一下键盘上的字符键时,首先会触发keydown 事件,然后紧跟着是 keypress 事件,最后会触发keyup 事件。其中keydown和keypress 都是在文本框发生变化之前被触发的;而keyup事件则是在文本框已经发生变化之后被触发的。
如果用户按下的是一个非字符键,那么首先会触发keydown 事件,然后就是keyup 事件。
键盘事件与鼠标事件一样,都支持相同的修改键。而且键盘事件的事件对象中也有 shiftKey、ctrlKey、altKey 和 metaKey 属性。
4.1 键码
在发生keydown 和 keyup 事件时, event对象的 keyCode 属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符键,keyCode 属性的值与 ASCII 码中对应小写字母或数字的编码相同。因此数字键7的keyCode 值是 55, 而字母 A键的keyCode 值为 65 ----与Shift键的状态无关。
4.2 字符编码
发生keypress 事件意味着按下的键会影响到屏幕中文本的显示。在所有的浏览器中,按下能够插入或删除的字符的键都会触发keypress 事件;按下其他键能否触发keypress 事件因浏览器而异。
event 对象还支持一个 charCode 属性,这个属性只有在发生keypress 事件时才包含值,而且这个值是按下的那个键所代表字符的ASCII码。此时的keyCode通常等于0或者也可能等于所按键的键码。IE8 及之前的版本和Opera 则是在keyCode 中保存字符的ASCII编码。想要以跨浏览器的方式取得字符编码,必须首先检测 charCode属性是否可用,如果不可用则使用keyCode。
var EventUtil = { //。。。。 getCharCode: function(event){ if(typeof event.charCode == "number"){ return event.charCode; }else{ return event.keyCode; } } };
获取案件的ASCII码之后,通过String.fromCharCode() 方法就可以转换成实际的字符。
var textbox = document.getElementById("myText"); EventUtil.addHandler(textbox, "keypress", function(event){ event = EventUtil.getEvent(event); alert(String.fromCharCode(EventUtil.getCharCode(event))); });
4.3 DOM3级变化
尽管所有浏览器都实现了某种形式的键盘事件,DOM3级事件还是做出了一些改变。比如DOM3级事件中的键盘事件,不在包含charCode属性,而是包含两个新属性:key 和 char。
其中,key 属性是为了取代 keyCode 而新增的,它的值是一个字符串。在按下某个字符键时,key 的值就是相应的文本字符;在按下非字符键时,key 的值是相应键的名(如“Shift”或“Down”)。而char属性在按下字符键时的行为与key相同,但在按下非字符键时值为null。
IE9支持key 属性,但不支持char 属性。Safari5 和 Chrome 支持名为 keyIdentifier的属性,在按下非字符键(如shift)的情况下与key的值相同。对于字符键,keyIdentifier 返回一个格式类似“U+0000”的字符串,表示Unicode 值。
var textbox = document.getElementById("myText"); EventUtil.addHandler(textbox, "keypress", function(event){ event = EventUtil.getEvent(event); var identifier = event.key || event.keyIdentifier; if(identifier){ alert(identifier); } });
由于存在跨浏览器的问题,因此不推荐使用key、keyIdentifier 或者 char。
DOM3级事件还添加了一个名为location的属性,这是一个数值,表示按下了什么位置上的键:0表示默认键盘,1表示左侧位置(例如左侧的Alt键),2表示右侧位置(例如右侧的Shift键),3表示数字小键盘,4表示移动设备键盘(也就是虚拟键盘),5表示手柄(如任天堂wii控制器)。IE9支持这个属性。Safari 和Chrome 支持名为keyLocation的等价属性,但有Bug----值始终是0,除非按下了数字键盘返回3,否子不会是1245
同key属性一样,支持location 的浏览器也不多,所以在跨浏览器开发中不推荐使用。
4.4. textInput 事件
DOM3级事件规范中引入了一个新事件,名叫textInput。根据规范,当用户在可编辑区域中输入字符时,会触发这个事件。这个用于替代keypress的textInput 事件的行为稍有不同,区别之一就是任何可以获得焦点的元素都可以触发keypress 事件,但只有可编辑区才能触发textInput 事件。区别之二是 textInput 事件只会在用户按下能够输入实际字符的键时才被触发,而keypress 事件则在按下那些能够影响文本显示的键是也会触发(例如退格键)。
由于textInput 事件主要考虑的是字符,因此它的event对象中还包括一个data属性,这个属性的值就是用户输入的字符(而非字符编码)。例如:
var textbox = document.getElementById("myText"); EventUtil.addHandler(textbox, "textInput", function(event){ event = EventUtil.getEvent(event); alert(event.data); });
5.变动事件
DOM2级的变动事件能在DOM中的某一部分发生变化是给出提示。变动事件是给XML或 HTML DOM 设计的,兵不特定于某种语言。DOM2 级定义了如下变动事件:
- DOMSubtreeModified:在DOM结构中发生任何变化时触发。这个事件在其他任何事件触发后都会触发。
- DOMNodeInserted:在一个节点作为子节点被插入另一个节点中时触发。
- DOMNodeRemoved:在节点从其父节点中被移除时触发。
- DOMNodeInseredIntoDocument:在一个节点被直接插入文档或通过子树间接插入文档之后触发。这个事件在DOMNodeInserted之后触发。
- DOMNodeRemovedFromDocument:在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发。这个事件在DOMNodeRemoved 之后触发。
- DOMAttrModified:在特性被修改之后触发。
- DOMCharacterDataModified:在文本节点的值发生变化时触发。
使用下面的代码可以检测出浏览器是否支持变动事件:
var isSupported = document.implementation.hasFeature("MutationEvents", "2.0");
IE8 及更早版本不支持任何变动事件。
6.HTML5事件
HTML5 详尽列出了浏览器应该支持的所有事件。其中部分已经得到完善支持。
6.1. contextmenu 事件
为了实现上下文菜单,开发人员面临的一个主要问题是如何确定应该显示上下文菜单(在windows中是右键单击,在Mac中是Ctrl + 单击),以及如何屏蔽与该操作关联的默认上下文菜单。为了解决这个问题,,就出现了contextmenu 这个事件,用于表示合适应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。
由于contextmenu 事件是冒泡的,因此可以为document 指定一个事件处理程序,用于处理页面中发生的所有此类事件。这个事件的目标是发生用户操作的元素。在所有浏览器中都可以取消这个事件:在兼容DOM 的浏览器中,使用 event.preventDefault(); 在IE 中,将event.returnValue 设为false。
因为contextmenu 事件 属于鼠标事件,所以其事件对象中包含与光标位置有关的所有属性。通常使用contextmenu 事件来显示自定义的上下文菜单,是使用onclick 事件处理程序来隐藏该菜单。co
<!DOCTYPE html> <html> <head> <title>Right Click </title> </head> <body> <div id="myDiv">right click</div> <ul id="myMenu" style="position:absolute;visibility:hidden;background-color:silver"> <li><a href="http://www.baidu.com">baidu</a></li> <li><a href="http://www.yahoo.com.cn">yahoo</a></li> <li><a href="http://www.163.com">163</a></li> </ul> <script type="text/javascript" src="eventutil.js"></script> <script type="text/javascript"> EventUtil.addHandler(window, "load", function(event){ var div = document.getElementById("myDiv"); EventUtil.addHandler(div, "contextmenu", function(event){ event = EventUtil.getEvent(event); EventUtil.preventDefault(event); var menu = document.getElementById("myMenu"); menu.style.left = event.clientX +"px"; menu.style.top = event.clientY + "px"; menu.style.visibility = "visible"; }); EventUtil.addHandler(document, "click", function(event){ document.getElementById("myMenu").style.visibility = "hidden"; }); }); </script> </body> </html>
支持contextmenu 事件的浏览器有IE、Firefox、Safari、Chrome 和 Opera 11+.
6.2beforeunload 事件
之所以有发生在window对象上的beforeunload 事件,是为了让开发人员有可能在页面卸载前阻止这一操作。这个事件会在浏览器卸载页面之前触发,可以通过它来取消卸载并继续使用原有页面。但是不恩能够彻底取消这个事件,因为那就相当于让用户无法离开当前页面了。
对于IE 及 Firefox 而言,通过将event.returnValue 设置为要显示给用户的字符串可以返回确认关闭对话框;对于Safari 和 Chrome 而言,需要将处理函数返回该字符串
EventUtil.addHandler(window, "beforeunload", function(event){ event = EventUtil.getEvent(event); var message = "I'm really going to miss you if you go."; event.returnValue = message; //for IE & Firefox return message; //for Safari & Chrome });
Chrome 弹出对话框如下:
6.3. DOMContentLoaded 事件
window 的load 事件会在页面中的一切都加载完毕时触发,但这个过程可能会因为要加载的外部资源过多而颇费周折。而DOMContextLoaded 事件在形成完整的 DOM树之后就会触发,不理会图像、JavaScript 文件、CSS 文件或其他资源是否已经下载完毕。与load 事件不同,DOMContentLoaded 支持在页面下载的早期添加事件处理程序,这也意味着用户可以尽早地与页面进行交互。
处理DOMContentLoaded 事件,可以为document 或 window 添加相应是事件处理程序,其event不会提供额外的信息,target属性是document。 这个事件始终都会在load 事件之前触发。
EventUtil.addHandler(document, "DOMContentLoaded", function(event){ alert("Content loaded"); });
IE9+、FireFox、Chrome、Safari 3.1+ 和 Opera 9+ 都支持DOMContentLoaded 事件。
6.4. readystatechange 事件
readystatechange 事件的目的是提供与文档或元素加载状态有关的信息,支持此事件的每个对象都有一个 readyState 属性,可能包含下列5个值中的一个:
- uninitialized(未初始化):对象存在但尚未初始化。
- loading(正在加载):对象正在加载数据。
- loaded(加载完毕):对象加载数据完成。
- interactive(交互):可以交互对象了,但还没有完全加载。
- complete(完成):对象已经加载完毕。
6.5. pageshow 和 pagehide 事件
Firefox 和 Opera 有一个特性,名叫“往返缓存”(back-forward cache 或 bfcache),可以在用户使用浏览器的“后退”和“前进”按钮是加快页面的转换速度。这个缓存中不仅保存者页面数据,还保存了DOM 和 JavaScript 的状态;实际上是将整个页面都保存在了内存里。如果页面位于bfcache里,那么再次打开该页面就不会触发load事件。为了更形象地说明bfcache 的行为, Firefox 还是提供了一些新事件。
第一个事件就是pageshow,这个事件在页面显示时触发,无论该页面是否来自bfcache。在重新加载的页面中,pageshow 会在load 事件触发后触发;而对于bfcache 中的页面,pageshow 会在页面状态完全回复的那一刻触发。虽然这个事件的目标是document, 但必须将其事件处理程序添加到window。
第二个事件是pagehide 事件,该事件会在浏览器卸载页面的时候触发。
支持pageshow 和 pagehide 事件的浏览器有 FIrefox、Safari 5+、Chrome 和 Opera。
6.6. hashchange 事件
HTML5 增加了 hashchange事件,以便在URL的参数列表(及URL中“#”后面的所有字符串)发生变化时通知开发人员。之所以新增这个事件,是因为在Ajax应用中,开发人员经常要利用URL参数列表来保存状态或导航信息。
内存还性能
由于事件处理程序可以为现代Web应用提供交互能力,因此很多开发人员会部分青红皂白地向页面中添加大量的处理程序。在JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。导致这一问题的原因是多方面的。首先每个函数都是对象,都会占用内存;内存中对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的DOM 访问次数,会延迟整个页面的交互就绪事件。事实上,从如何利用好事件处理程序的角度出发,还是有一些方法能够提升性能的。
1.事件委托
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如,click 事件会一直冒泡到document 层次。也就是说,我们可以为整个页面制定一个onclick 事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。
<!DOCTYPE html> <html> <head> <title>title</title> </head> <body> <ul> <li id="goBaidu">baidu</li> <li id="changeTitle">title</li> <li id="sayHi">sayHi</li> </ul> <script type="text/javascript" src="eventutil.js"></script> <script type="text/javascript"> var goBaidu = document.getElementById("goBaidu"); var changeTitle = document.getElementById("changeTitle"); var sayHi = document.getElementById("sayHi"); EventUtil.addHandler(goBaidu, "click", function(event){ location.href = "http://www.baidu.com"; }); EventUtil.addHandler(changeTitle, "click", function(event){ document.title = "I changed the title"; }); EventUtil.addHandler(sayHi, "click", function(event){ alert("hi"); }); </script> </body> </html>
如果在一个负载的Web 应用程序中,对所有可点击的元素都采用这种方式,呢么结果就会有数不清的代码用户添加事件处理程序。此时,可以利用事件委托技术解决这个问题。使用事件委托,只需要在DOM树中尽量最高的层次上添加一个事件处理程序。
var list = document.getElementById("myLink"); EventUtil.addHandler(list, "click", function(event){ event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); switch(target.id){ case "goBaidu": location.href = "http://www.baidu.com"; break; case "changeTitle": document.title = "I changed the title"; break; case "sayHi": alert("hi"); break; } });
如果可行的话,也可以考虑为document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型的事件。这样做与采取传统的做法相比具有以下优点:
- document 对象很快就可以访问,而且可以在页面生命周期的任何时点上为他添加事件处理程序(无需等待DOMContentLoaded 或 load 事件)。换句话说,只要可点击的元素呈现在页面上,就可以立即具备适当的功能。
- 在页面中设置事件处理程序所需要的事件更少,至添加一个事件处理程序所需的DOM引用更少,所花的时间也更少。
- 整个页面占用的内存空间更少,能够提升整体性能。
最适合采用事件委托技术的事件包括 click、mousedown、mouseup、keydown、keyup 和 keypress。虽然mouseover 和mouseout 事件也冒泡,但要适当处理他们并不容易,而且经常需要计算元素的位置。
2.移除事件处理程序
每当事件处理程序制定给元素时,运行中的浏览器代码与支持页面交互的JavaScript 代码 之间就会建立一个连接。这种连接越多,页面执行起来就越慢。如前所述可以采用事件委托技术,限制建立的连接数量。另外在不需要的时候移除事件处理程序,也是解决这个问题的一种方案。内存中留有那些过时不用的“空事件处理程序”(dangling event handler),也是造成Web应用程序内存与性能问题的主要原因。
一般来说,最好的做法是在页面卸载之前,先通过onunload 事件处理程序移除所有事件处理程序。在此,事件委托技术再次表现出它的优势,需要跟踪的事件处理程序越少,移除他们就越容易。对这种类似撤销的操作,我们可以把它想象成:只要通过onload 事件处理程序 添加的东西,最后都要通过onunload 事件处理程序将它移除。
模拟事件
事件,就是网页中某个特别值得关注的瞬间。事件经常有用户操作或通过其他浏览器功能来触发。其实,也可以使用JavaScript 在任意时刻触发特定的事件,而此时的事件就如同浏览器创建的事件一样。也就是说这些事件该冒泡还会冒泡,而且照样能够导致浏览器执行已经制定的处理他们的事件处理程序。
1.DOM中的事件模拟
可以造document对象上使用 createEvent() 方法创建event 对象。这个方法接收一个参数,即表示要创建的事件类型的字符串。在DOM2级中,所有的这些字符串都是使用英文复数形式,而在DOM3级中都变成了单数。这个字符串可以是下列几个字符串之一
- UIEvents:一般化的UI事件。鼠标事件和键盘事件都继承自UI 事件。DOM3 级中是UIEvent。
- MouseEvents:一般化的鼠标事件。DOM3级中是MouseEvent。
- MutationEvents:一般化的DOM变动事件。DOM3级中是MutationEvent。
- HTMLEvents:一般化的HTML 事件。 对应的DOM3级事件。
在创建了event 对象之后,还需要使用与事件有关的信息对其进行初始化,每种类型的event对象都有一个特殊的方法,为他传入适当的数据u,就可以初始化该event 对象。不同类型的这个方法的名字也不相同,具体要取决与 createEvent() 中使用的参数。
模拟事件的最后一部就是触发事件。这一步需要使用dispatchEvent() 方法,所有支持事件的DOM节点都支持这个方法。调用dispatchEvent() 方法时,需要传入一个参数,即表示要触发事件的event对象。触发事件后,改时间就跻身“官方事件”之列了,因而能够照样冒泡并引发相应事件处理程序的执行。
1.1.模拟鼠标事件
创建新的鼠标事件对象并为其指定必要的信息,就可以模拟鼠标事件。创建鼠标事件对象的方法是为createEvent() 传入字符串MouseEvents。返回的对象有一个名为 initMouseEvent() 方法,用于制定与该鼠标事件有关的信息,这个方法接收15个参数,分别与鼠标事件中每个典型的属性一一对应:
- type(字符串):表示要触发的事件类型,例如click。
- bubbles(布尔值):表示事件是否应该冒泡。为精确地模拟鼠标事件,应该把这个参数设置为true。
- cancelable(布尔值):表示事件是否可以取消,为精确地模拟鼠标事件,应该吧这个参数值为true。
- view(AbstractView):与事件关联的师徒。这个参数几乎总是要设置为 document.defaultView。
- detail(整数):与事件有关的详细信息。这个值一般只有事件处理程序使用,但通常都设置为0。
- screenX(整数):事件相对于屏幕的X坐标。
- screenY(整数):事件相对于屏幕的Y坐标。
- clientX(整数):事件相对于视口的X坐标。
- clientY(整数):事件相对于视口的Y坐标。
- ctrlKey(布尔值):表示是否按下了Ctrl键。默认为false。
- altKey(布尔值):表示是否按下了Alt 键。默认为false。
- shiftKey(布尔值):表示是否按下了Shift键。默认为false。
- metaKey(布尔值):表示是否按下了Meta键。默认值为false。
- button(整数):表示按下了哪个鼠标键。默认为0.
- relatedTarget(对象):表示与事件相关的对象。这个参数只在模拟mouseover 或 mouseout 时使用。
其中前四个参数对正确触发事件至关重要,因为浏览器要用到这些参数。
var sayHi = document.getElementById("sayHi"); var event = document.createEvent("MouseEvents"); event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); sayHi.dispatchEvent(event);
小结
事件是将JavaScript 与网页联系在一起的主要方式。DOM3级事件规范和HTML5 定义了常见的大多数事件。即使有规范定义了基本事件,但很多浏览器仍然在规范之外实现了自己的转悠事件,从而为开发人员提供更多掌握用户交互的手段。
在使用事件时,需要考虑如下一些内存与性能方面的问题。
- 有必要限制一个页面中事件处理程序的数量,数量太多会导致占用大量内存,而且也会让用户感觉页面反应不够灵敏。
- 建立在事件冒泡机智上的事件委托技术,可以有效地减少事件处理程序的数量。
- 建议在浏览器泻早页面之前移除页面中的所有事件处理程序。
事件是JavaScript 中最重要的主题之一,深入理解事件的工作机制以及他们对性能的影响至关重要。