0%

高程笔记5-事件

高程笔记5

事件冒泡

IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深
的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。以下面的 HTML 页面为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<title>Event Bubbling Example</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>
如果你单击了页面中的<div>元素,那么这个 click 事件会按照如下顺序传播:
(1) <div>
(2) <body>
(3) <html>
(4) document
也就是说,click 事件首先在<div>元素上发生,而这个元素就是我们单击的元素。然后,click
事件沿 DOM 树向上传播,在每一级节点上都会发生,直至传播到 document 对象。图 13-1 展示了事件
冒泡的过程。

事件捕获

1
2
3
4
5
6
7
8
9
10
Netscape Communicator 团队提出的另一种事件流叫做事件捕获(event capturing)。事件捕获的思想
是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在
事件到达预定目标之前捕获它。如果仍以前面的 HTML 页面作为演示事件捕获的例子,那么单击<div>
元素就会以下列顺序触发 click 事件。
(1) document
(2) <html>
(3) <body>
(4) <div>
在事件捕获过程中,document 对象首先接收到 click 事件,然后事件沿 DOM 树依次向下,一直
传播到事件的实际目标,即<div>元素。

HTML事件处理程序

1
2
3
4
5
6
7
8
9
10
11
12
13
在 HTML 中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的
脚本,如下面的例子所示:
<script type="text/javascript">
function showMessage(){
alert("Hello world!");
}
</script>
<input type="button" value="Click Me" onclick="showMessage()" />


通过 HTML 指定事件处理程序的最后一个缺点是 HTML 与 JavaScript 代码紧密耦合。如果要更换事
件处理程序,就要改动两个地方:HTML 代码和 JavaScript 代码。而这正是许多开发人员摒弃 HTML 事
件处理程序,转而使用 JavaScript 指定事件处理程序的原因所在。

DOM2 级事件处理程序

“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()
和 removeEventListener()。所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处
理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是 true,表示在捕获
阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。
要在按钮上为 click 事件添加事件处理程序,可以使用下列代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
上面的代码为一个按钮添加了 onclick 事件处理程序,而且该事件会在冒泡阶段被触发(因为最
后一个参数是 false)。与 DOM0 级方法一样,这里添加的事件处理程序也是在其依附的元素的作用域
中运行。

//移除removeEventListener,函数不能匿名
var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click", handler, false);
//这里省略了其他代码
btn.removeEventListener("click", handler, false); //有效!

事件类型

Web 浏览器中可能发生的事件有很多类型。如前所述,不同的事件类型具有不同的信息,而“DOM3
级事件”规定了以下几类事件。

1
2
3
4
5
6
7
8
9
10
 UI(User Interface,用户界面)事件,当用户与页面上的元素交互时触发;
 焦点事件,当元素获得或失去焦点时触发;
 鼠标事件,当用户通过鼠标在页面上执行操作时触发;
 滚轮事件,当使用鼠标滚轮(或类似设备)时触发;
 文本事件,当在文档中输入文本时触发;
 键盘事件,当用户通过键盘在页面上执行操作时触发;
 合成事件,当为 IME(Input Method Editor,输入法编辑器)输入字符时触发;
 变动(mutation)事件,当底层 DOM 结构发生变化时触发。
 变动名称事件,当元素或属性名变动时触发。此类事件已经被废弃,没有任何浏览器实现它们,
因此本章不做介绍

UI事件

UI 事件指的是那些不一定与用户操作有关的事件。这些事件在 DOM 规范出现之前,都是以这种或
那种形式存在的,而在 DOM 规范中保留是为了向后兼容。现有的 UI 事件如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 DOMActivate:表示元素已经被用户操作(通过鼠标或键盘)激活。这个事件在 DOM3 级事
件中被废弃,但 Firefox 2+和 Chrome 支持它。考虑到不同浏览器实现的差异,不建议使用这个
事件。
 load:当页面完全加载后在 window 上面触发,当所有框架都加载完毕时在框架集上面触发,
当图像加载完毕时在<img>元素上面触发,或者当嵌入的内容加载完毕时在<object>元素上面
触发。
 unload:当页面完全卸载后在 window 上面触发,当所有框架都卸载后在框架集上面触发,或
者当嵌入的内容卸载完毕后在<object>元素上面触发。
 abort:在用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上面触发。
 error:当发生 JavaScript 错误时在 window 上面触发,当无法加载图像时在<img>元素上面触
发,当无法加载嵌入内容时在<object>元素上面触发,或者当有一或多个框架无法加载时在框
架集上面触发。第 17 章将继续讨论这个事件。
 select:当用户选择文本框(<input>或<texterea>)中的一或多个字符时触发。第 14 章将
继续讨论这个事件。
 resize:当窗口或框架的大小变化时在 window 或框架上面触发。
 scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。<body>元素中包含所加
载页面的滚动条。
  1. load 事件

    JavaScript 中最常用的一个事件就是 load。当页面完全加载后(包括所有图像、JavaScript 文件、
    CSS 文件等外部资源),就会触发 window 上面的 load 事件。有两种定义 onload 事件处理程序的方式。
    第一种方式是使用如下所示的 JavaScript 代码:
    EventUtil.addHandler(window, “load”, function(event){
    alert(“Loaded!”);
    });

  2. unload 事件

    与 load 事件对应的是 unload 事件,这个事件在文档被完全卸载后触发。只要用户从一个页面切
    换到另一个页面,就会发生 unload 事件。而利用这个事件最多的情况是清除引用,以避免内存泄漏。
    与 load 事件类似,也有两种指定 onunload 事件处理程序的方式。第一种方式是使用 JavaScript,如
    下所示:
    EventUtil.addHandler(window, “unload”, function(event){
    alert(“Unloaded”);
    });

  3. resize 事件

    当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件。这个事件在window(窗口)上面触发,因此可以通过JavaScript或者元素中的onresize特性来指定事件处理程序。如前所述,我们还是推荐使用如下所示的 JavaScript 方式:
    EventUtil.addHandler(window, “resize”, function(event){
    alert(“Resized”);
    });

4.scroll 事件

虽然 scroll 事件是在 window 对象上发生的,但它实际表示的则是页面中相应元素的变化。在混
杂模式下,可以通过元素的 scrollLeft 和 scrollTop 来监控到这一变化;而在标准模式下,
除 Safari 之外的所有浏览器都会通过元素来反映这一变化(Safari 仍然基于跟踪滚动位
置),如下面的例子所示:
EventUtil.addHandler(window, “scroll”, function(event){
if (document.compatMode == “CSS1Compat”){
alert(document.documentElement.scrollTop);
} else {
alert(document.body.scrollTop);
}
});

焦点事件

 blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
 focus:在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它。

鼠标与滚轮事件

鼠标事件是 Web 开发中最常用的一类事件,毕竟鼠标还是最主要的定位设备。DOM3 级事件中定
义了 9 个鼠标事件,简介如下。

 click:在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对确保
易访问性很重要,意味着 onclick 事件处理程序既可以通过键盘也可以通过鼠标执行。
 dblclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发。从技术上说,这个事件并不
是 DOM2 级事件规范中规定的,但鉴于它得到了广泛支持,所以 DOM3 级事件将其纳入了标准。
 mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。
 mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且
在光标移动到后代元素上不会触发。DOM2 级事件并没有定义这个事件,但 DOM3 级事件将它
纳入了规范。IE、Firefox 9+和 Opera 支持这个事件。
 mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且
在光标移动到后代元素上不会触发。DOM2 级事件并没有定义这个事件,但 DOM3 级事件将它
纳入了规范。IE、Firefox 9+和 Opera 支持这个事件。
 mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。
 mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。又移入的另
一个元素可能位于前一个元素的外部,也可能是这个元素的子元素。不能通过键盘触发这个事件。
 mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触
发。不能通过键盘触发这个事件。
 mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件。

键盘与文本事件

有 3 个键盘事件,简述如下。

 keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
 keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。
按下 Esc 键也会触发这个事件。Safari 3.1 之前的版本也会在用户按下非字符键时触发 keypress
事件。
 keyup:当用户释放键盘上的键时触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var textbox = document.getElementById("myText");
EventUtil.addHandler(textbox, "keyup", function(event){
event = EventUtil.getEvent(event);
alert(event.keyCode);
});

//字符编码
var EventUtil = {
//省略的代码
getCharCode: function(event){
if (typeof event.charCode == "number"){
return event.charCode;
} else {
return event.keyCode;
}
},
//省略的代码
};

事件委托

对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事
件处理程序,就可以管理某一类型的所有事件。例如,click 事件会一直冒泡到 document 层次。也就
是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必给每个可单击的元素分别添加事
件处理程序。以下面的 HTML 代码为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>


var list = document.getElementById("myLinks");
EventUtil.addHandler(list, "click", function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
switch(target.id){
case "doSomething":
document.title = "I changed the document's title";
break;
case "goSomewhere":
location.href = "http://www.wrox.com";
break;
case "sayHi":
alert("hi");
break;
}
});

最适合采用事件委托技术的事件包括 click、mousedown、mouseup、keydown、keyup 和 keypress。
虽然 mouseover 和 mouseout 事件也冒泡,但要适当处理它们并不容易,而且经常需要计算元素的位置。
(因为当鼠标从一个元素移到其子节点时,或者当鼠标移出该元素时,都会触发 mouseout 事件。)

自定义 DOM 事件

DOM3 级还定义了“自定义事件”。自定义事件不是由 DOM 原生触发的,它的目的是让开发人员
创建自己的事件。要创建新的自定义事件,可以调用 createEvent(“CustomEvent”)。返回的对象有
一个名为 initCustomEvent()的方法,接收如下 4 个参数。

 type(字符串):触发的事件类型,例如”keydown”。
 bubbles(布尔值):表示事件是否应该冒泡。
 cancelable(布尔值):表示事件是否可以取消。
 detail(对象):任意值,保存在 event 对象的 detail 属性中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var div = document.getElementById("myDiv"),
event;
EventUtil.addHandler(div, "myevent", function(event){
alert("DIV: " + event.detail);
});
EventUtil.addHandler(document, "myevent", function(event){
alert("DOCUMENT: " + event.detail);
});
if (document.implementation.hasFeature("CustomEvents", "3.0")){
event = document.createEvent("CustomEvent");
event.initCustomEvent("myevent", true, false, "Hello world!");
div.dispatchEvent(event);
}

这个例子创建了一个冒泡事件"myevent"。而 event.detail 的值被设置成了一个简单的字符串,
然后在<div>元素和 document 上侦听这个事件。因为 initCustomEvent()方法已经指定这个事件应
该冒泡,所以浏览器会负责将事件向上冒泡到 document。
支持自定义 DOM 事件的浏览器有 IE9+和 Firefox 6+。