JavaScript —— 事件

JavaScript与HTML之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发送的一些特定的交互瞬间。使用侦听器(或处理程序)来预定事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为观察员模式的模型,支持页面的行为与页面的外观(HTML,CSS)之间的松散耦合。

事件流
在单击按钮的同时,你也单击了按钮的容器元素,甚至也单击了整个页面。
事件流描述的是从页面中接收事件的顺序。
但,IE的事件流是事件冒泡,NetScape Communicator的事件流是事件捕获流。

事件冒泡

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

<!DOCTYPE html>
<html>
<head>
	<title>Event</title>
</head>
<body>
	<div>Click Me</div>
</body>
</html>

单击页面中的div元素。click事件会按如下顺序传播:

  1. div
  2. body
  3. html
  4. document对象
    click事件在div元素上发生,click事件沿DOM树向上传播,在每一级节点上都会发生,直到document对象。

所有浏览器都支持事件冒泡,IE5.5及更早版本中事件冒泡跳过html元素,IE9,Firefox,Chrome,Safari,则将事件一直冒泡到window对象。

事件捕获

事件捕获的思想是不太具体的节点应该更早接收事件,最具体的节点应该最后接收到事件。
事件捕获的用意在于在事件到达预定目标之前捕获它。
上述例子,在事件捕获的情况下,单击div元素以下顺序触发事件

  1. document
  2. html
  3. body
  4. div
    事件捕获是Netscape Communicator唯一支持的事件流模型,IE9,Safari,Chrome,Opera,FireFox都支持,但这些浏览器都是从window对象开始捕获事件的,版本浏览器不支持,所以尽可能使用事件冒泡

DOM事件流

"DOM2级事件"规定事件流包括三个阶段:事件捕获阶段,处于目标阶段,事件冒泡。
首先发生的是事件捕获,为截获事件提供了机会,然后是实际的目标接收到事件。最后一个阶段冒泡阶段,也可以在这个阶段对事件做出响应。
前面html页面为例,触发事件的顺序:

捕获阶段

  1. Document
  2. Element html
  3. Element body

冒泡阶段
4. Element div
5. Element body
6. Element html
7. Document

在DOM事件流中,实际的目标(div元素)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从document到html到body后就停止了。下一阶段是"处于目标"阶段,于是事件div上发生,并在事件处理中被看成冒泡阶段的一部分,然后,冒泡阶段发生,事件又传播回文档。

IE8以及更早版本不支持DOM事件流,即使"DOM2级事件"规定明确要求捕获阶段不会涉及事件目标,但IE9,Safari,Chrome,Opera9.5及更高版本会在捕获阶段触发事件对象上的事件。结果就是有两个机会在目标对象上面操作事件。

事件委托

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

<ul>
	<li id="goSomewhere">Go somewhere</li>
	<li id="doSomething">Do something</li>
	<li id="sayHi">Say Hi</li>
</ul>

按照传统的做法,需要为它们添加3个事件处理程序
使用事件委托,只需要在DOM树中尽量最高的层次上添加一个事件处理程序

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

由于所有列表项都是这个元素的子节点,而且它们的事件会冒泡,所以单击事件最终会被这个函数处理,事件目标是被单击的列表项,故而可以通过检查id属性来决定采取适当的操作。适合采用事件委托技术的事件:click,mouseup,keydown,keyup

移除事件处理程序

每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的JavaScript代码之间就会建立一个连接,这种连接越多,页面执行起来越慢。事件委托限制连接数量,另外,在不需要的时候移除事件处理程序,也是解决这个问题的一种方案。
内存中留有那些过时不用的"空事件出程序",也是造成web应用程序内存与性能问题的主要原因。
两种情况,造成上述问题:一、从文档中移除带有事件处理程序的元素时,可能是通过纯粹的DOM操作,例:removeChild(),replaceChild()方法,但更多地是发生在使用innerHTML替换页面中的某一部分的时候,如果带有事件处理程序的元素innerHTML删除了,那么原来添加到元素。例:

<div id="myDiv">
	<input type="button" value="Click Me" id="mybutton">
</div>
<script type="text/javascript">
	var btn = document.getElementById("myBtn");
	btn.onclick = function(){
		//先执行某些操作
		btn.onclick = null;//手工移除事件处理程序
		document.getElementById("myDiv").innreHTML = 'Processing ...';
		}
	}
</script>

在div元素上设置innerHTML可以把按钮移走,但事件处理程序仍然与按钮保持着引用关系。有的浏览器(尤其IE
)在这种情况下不会作出恰当地处理,很可能将对元素,事件处理程序的引用保存在内存中。最好手工移除事件处理程序。

二、导致"空事件处理程序"另一种情况,写在页面的时候
如果在页面被卸载之前没有清理干净事件处理程序,那么它们就会滞留在内存中。每次加载完页面在卸载页面时(可能是在页面来回切换,也可以是刷新),内存中滞留的对象数目就会增加,因为事件处理程序占用的内存并没有被释放。

最好的做法是在页面卸载之前,先通过onunload事件处理程序移除事件处理程序。只要通过onload事件处理程序添加的东西,最后都要通过onunload事件处理程序将它们移除。

事件处理程序

事件就是用户或浏览器自身执行的某种动作。click,load和mouseover都是事件名字。响应某个事件的函数就叫做事件处理程序(或事件侦听器),事件处理程序的名字以"on"开关,因此click事件的事件处理程序就是onclick。

HTML事件处理程序

某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特定来指定。这个特性值应该是能够执行的JavaScript代码。

< input type=“button” value=“Click me” οnclick=“alert(‘clicked’);”>
单击这个按钮时,就会显示一个警告框,这个操作是通过指定onclick特性并将一些JavaScript代码作为它的值来定义的。这个值是JavaScript,因为不能在其中使用未经转义的HTML语法字符。
在HTML中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本。例:
< input type=“button” value=“Click me” οnclick=“show()”>
这样指定事件处理程序具有一些独到之处。首先,这样会创建一个封装着元素属性值的函数。这个函数中有一个局部变量event,也就是事件对象。
< input type=“button” value=“Click me” οnclick=“alert(event,type)”>
通过event变量,可以直接访问事件对象。this值等于事件的目标元素。(函数内部)

在HTML中指定事件处理程序有两个缺点。①时差问题 ②扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。 ③ HTML与JavaScript代码紧密耦合
① 因为用户可能会在HTML元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件。

DOM0级事件处理程序

通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序的属性。至今浏览器仍然支持,原因一、简单,二、跨浏览器的优势
要使用JavaScript指定事件处理程序,首先必须取得一个要操作的对象的引用。
每个元素(包括window,document)都有自己的事件处理程序属性。例:onclick
将这种属性的值设置为一个函数,就可以指定事件处理程序。例

var btn = doxument.getElementById("myBtn");
btn.onclick = function(){
	alert("clicked");
}
使用DOM0级方法指定的事件处理程序被认为是元素的方法。这时候的事件处理程序是在元素的作用域中运行,程序中的this引用当前元素。例:
var btn = document.getElementById("myBtn");
btn.onclick = function(){
	alert(this.id);
}
btn.onclick = null;//删除事件处理程序

DOM2级事件

两个方法,用于处理指定和删除事件处理程序的操作:addEventLister()和removeEventListener()。所有DOM节点中都包含这两个方法,3个参数:要处理的事件名,作为事件处理程序的函数和一个布尔值。true,捕获阶段调用处理程序,false,冒泡
var btn = document.getElementById(“myBtn”);
bn.addEventListener(“click”,function(){alert(this.id)},false);
好处:可以添加多个事件处理程序。例:

var btn = document.getElementById("myBtn");
btn.addEventListener("click",function(){alert(this.id);}.false);
btn.addEventListener("click",function(){alert("Hello World");}.false);

按顺序触发
只能使用removeEventListener()来移除,移除时传入的参数与添加的相同。意味着通过addEventListener()添加的匿名函数将无法移除。

var btn = document.getElementById("myBtn");
var handler = function(){alert(this.id)};
btn.addEventListener("click",handler,false);
btn.removeEventListener("click",hander,false);//有效

大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。最后只在需要在事件到达目标之前截获它的时候将事件处理程序添加到捕获阶段。

IE事件处理程序

attachEvent()和datachEvent()。两个参数:事件处理程序名称,事件处理程序函数
var btn = document.getElementById(“myBtn”);
btn.attachEvent(“onclick”,function(){alert(“clicked”)});//第一个参数是onclick
在使用attachEvent()方法,事件处理程序会在全局作用域中运行,this等于window。
attachEvent()方法也可以用来为一个元素添加多个事件处理程序。相反的顺序被触发
提供相同的参数,用detchEvent()移除attachEvent()

var btn = document.getElementById("myBtn");
var handler = function(){alert("clicked")};
btn.attachEvent("onclick",handler);
btn.detchEvent("onclick",handler);//移除事件处理程序

跨浏览器的事件处理程序

要保证处理的代码能在大多数浏览器下一致,只需要关注冒泡阶段。
第一要创建的方法是addHandler(),它的职责是视情况分别使用DOM0级方法,DOM2级方法,IE方法来添加事件。这个方法属于叫EventUtil对象。
3个参数:要操作的元素,事件名称和事件处理程序函数
像下面这样使用EventUtil对象(详细看354页)

var btn = document.getElementById("myBtn");
var handler = function(){
	alert("clicked");
}
Event.addHandler(btn,"click",handler);
Event.removeHandler(btn,"click",handler);

事件对象

触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素,事件的类型,其他与特定事件相关的信息

DOM中的事件对象

兼容DOM的浏览器会将一个event对象传入到事件处理程序中,无论DOM0,DOM2级都会传入event对象

var btn = document.getElementById("myBtn");
btn.onclick = function(){
	alert(event.type);
}//click
btn.addEventListener("click",function(event){alert(event.type)},false);//"click"

在事件处理程序内部,对象this始终等于currentTarget的值,而target则只包含事件的实际目标currentTarget:其事件处理程序当前正在处理事件的那个元素。
target:事件目标

document.body.onclick = function(event){
	alert(event.currentTarget === document.body);//true
	alert(this === document.body);//true
	alert(event.target === document.getElementById("myBtn"));//true
}

事件处理程序是注册在document.body这个元素上的,click真正目标是按钮元素
在需要通过一个函数处理多个事件时,可以使用type属性。

阻止特定事件的默认行为,可以使用preventDefalut()方法
只有cancaelable属性设为true的事件,才可以使用preventDefalut()来取消默认行为。
stopPropagation()方法用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。
例:

var link = document.getElementById("myLink");
link.onclick = function(event){
	event.preventDefault();
}
var btn = document.getElementById("myBtn");
btn.onclick = function(event){
	alert("clicked");
	event.stopPropagation();
}

事件对象的eventPhase属性,可以用来确定事件当前正位于事件流的哪个阶段。
event.eventPhase 1.捕获阶段 2.事件处理程序处于目标对象上 3.冒泡阶段

IE中的事件对象

DOM级方法添加事件处理器时,event对象作为window对象的一个属性存在

btn.onclick = function(){
	var event = window.event;
	alert(event.type);//"clicl"
}

attachEvent()添加的,那么会有一个event对象作为参数被传入事件处理程序函数中。
btn.attachEvent(“onclick”,function(event){alert(event.type)})//click
event.srcElement 事件目标(与DOM中的target作用相同)
returnValue等于false取消默认行为,window.event/event.returnValue = false;
cancelBubble停止事件冒泡 window.event/event.cancelBubble = true;

跨浏览器的事件对象

前面EventUtil对象加以增强
getEvent():返回对event对象的引用event = EventUtil.getEvent(event);//DOM->event IE->window.event
getTarget():返回事件的目标 var target = EventUtil.getTarget(event);
preventDefailt():取消事件的默认行为 检测是否存在preventDefault()存在调用,不存在将returnValue设为false

link.onclick = function(event){
	event = EventUtil.getEvent(event);
	EventUtil.preventDefault(event);
}

stopPropagation():停止冒泡,尝试使用DOM方法阻止,否则使用cancelBubble


此文来源于《JavaScript高级程序设计》的总结摘取

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值