事件
JavaScript 与HTML 之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。
Part1 事件流
事件流描述的是从页面中接收事件的顺序。有以下三种类型:
- 事件冒泡
IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。 - 事件捕获
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它。 - DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
举个栗子:
<!DOCTYPE html>
<html>
<head>
<title>Event Bubbling Example</title>
</head>
<body>
<div id="myDiv">Click Me</div>
</body>
</html>
上述代码在以上三种类型事件流中的实现过程分别如下:
事件冒泡
事件捕获
DOM事件流
Part2 事件处理程序
事件就是用户或浏览器自身执行的某种动作。诸如click、load 和mouseover,都是事件的名字。而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以”on”开头,因此click 事件的事件处理程序就是onclick,load 事件的事件处理程序就是onload。为事件指定处理程序的方式有好几种。
1.HTML事件处理程序
HTML事件处理程序,顾名思义就是在HTML中定义的事件处理程序。例如:
<input type="button" value="Click Me" onclick="showMessage()" />
<script type="text/javascript">
function showMessage(){
alert("Hello world!");
}
</script>
2.DOM事件处理程序
DOM事件是通过JavaScript 指定事件处理程序。先获取要操作的DOM对象的引用,再给该对象绑定事件。 例如,在按钮上为click事件添加事件处理程序:
- DOM0级事件处理程序
var btn = document.getElementById("myBtn");
btn.onclick = function(){
alert("Clicked");
};
若想删除通过DOM0 级方法指定的事件处理程序,只要像下面这样将事件处理程序属性的值设置为null 即可:
btn.onclick = null; //删除事件处理程序
Tips: DOM0级事件模型只支持1个DOM事件处理函数,若绑定多个事件处理函数,前面的函数都会被覆盖掉!
- DOM2级事件处理程序
“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()
和removeEventListener()
。所有DOM节点中都包含这两个方法,并且它们都接受3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
alert(this.id);
}, false);
btn.addEventListener("click", function(){
alert("Hello world!");
}, false);
移除事件处理程序:通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。
var btn = document.getElementById("myBtn");
var handler = function(){
alert(this.id);
};
btn.addEventListener("click", handler, false);
//这里省略了其他代码
btn.removeEventListener("click", handler, false); //有效!
Tips:
1.使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序,并按照它们添加的顺序触发;
2.DOM0级、DOM2级方法指定的事件处理程序中this
引用的是当前DOM元素;
3.DOM2级事件中通过addEventListener()
添加的匿名函数将无法除
3.IE事件处理程序
IE 实现了与DOM 中类似的两个方法:attachEvent()
和detachEvent()
。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。
要使用attachEvent()为按钮添加一个事件处理程序,可以使用以下代码。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(){
alert("Clicked");
});
在IE 中使用attachEvent()与使用DOM0 级方法的主要区别在于事件处理程序的作用域。在使用DOM0 级方法的情况下,事件处理程序会在其所属元素的作用域内运行;在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this 等于 window
。
使用attachEvent()添加的事件可以通过detachEvent()来移除,条件是必须提供相同的参数。这也意味着添加的匿名函数将不能被移除。与DOM 方法一样,这里就不再举例啦。
4.跨浏览器的事件处理程序
这两个方法首先都会检测传入的元素中是否存在DOM2 级方法。如果存在DOM2 级方法,则使用该方法:传入事件类型、事件处理程序函数和第三个参数false(表示冒泡阶段)。如果存在的是IE 的方法,则采取第二种方案。注意,为了在IE8 及更早版本中运行,此时的事件类型必须加上”on”前缀。最后一种可能就是使用DOM0 级方法(在现代浏览器中,应该不会执行这里的代码)。此时,我们使用的是方括号语法来将属性名指定为事件处理程序,或者将属性设置为null。
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;
}
}
};
Part3 事件对象
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。例如,鼠标操作导致的事件对象中,会包含鼠标位置的信息,而键盘操作导致的事件对象中,会包含与按下的键有关的信息。所有浏览器都支持event 对象,但支持方式不同。
1.DOM中的事件对象
兼容DOM 的浏览器会将一个event 对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0 级或DOM2 级),都会传入event 对象。来看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(event){ //DOM 0级
alert(event.type); //"click"
};
btn.addEventListener("click", function(event){ //DOM 2级
alert(event.type); //"click"
}, false);
event 对象包含与创建它的特定事件有关的属性和方法。常用的有:
type
:被触发的事件类型currentTarget
:事件处理程序注册到的那个元素target
:事件真正的目标preventDefault()
:取消事件的默认行为stopPropagation()
:用于立即停止事件在DOM 层次中的传播,即取消进一步的事件捕获或冒泡。
在事件处理程序内部,对象this 始终等于currentTarget 的值,而target 则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则this、currentTarget 和target 包含相同的值。
2.IE中的事件对象
与访问DOM中的event 对象不同,要访问IE 中的event 对象有几种不同的方式,取决于指定事件处理程序的方法。在使用DOM0 级方法添加事件处理程序时,event 对象作为window 对象的一个属性存在。来看下面的例子。
var btn = document.getElementById("myBtn");
btn.onclick = function(){
var event = window.event;
alert(event.type); //"click"
};
在此,我们通过window.event 取得了event 对象,并检测了被触发事件的类型(IE 中的type属性与DOM中的type 属性是相同的)。可是,如果事件处理程序是使用attachEvent()添加的,那么就会有一个event 对象作为参数被传入事件处理程序函数中,如下所示。
var btn = document.getElementById("myBtn");
btn.attachEvent("onclick", function(event){
alert(event.type); //"click"
});
IE 的event 对象同样也包含与创建它的事件相关的属性和方法。
3.跨浏览器的事件对象
var EventUtil = {
addHandler: 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;
}
},
removeHandler: function(element, type, handler){
//省略的代码
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
Part4 事件委托
问题:在JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能。导致这一问题的原因是多方面的。首先,每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。
对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
例如,click 事件会一直冒泡到document 层次。也就是说,我们可以为整个页面指定一个onclick 事件处理程序,而不必给每个可单击的元素分别添加事件处理程序。以下面的HTML 代码为例。
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>
其中包含3 个被单击后会执行操作的列表项。按照传统的做法,需要像下面这样为它们添加3 个事件处理程序。
var item1 = document.getElementById("goSomewhere");
var item2 = document.getElementById("doSomething");
var item3 = document.getElementById("sayHi");
EventUtil.addHandler(item1, "click", function(event){
location.href = "http://www.wrox.com";
});
EventUtil.addHandler(item2, "click", function(event){
document.title = "I changed the document's title";
});
EventUtil.addHandler(item3, "click", function(event){
alert("hi");
});
使用事件委托,只需在DOM 树中尽量最高的层次上添加一个事件处理程序,如下面的例子所示。
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;
}
});
所有用到按钮的事件(多数鼠标事件和键盘事件)都适合采用事件委托技术。
今天看的内容就这么多啦,后续有新的发现我再来修改和补充~~