JavaScript与HTML的交互是通过事件实现的,事件就是文档或浏览器窗口中发生的一些特定的交互瞬间,是用户或浏览器自身执行的某种动作,例如click,mouseover等都是事件名。
(一)事件流
事件流描述的是从页面中接收事件的顺序。
1)事件冒泡
IE中的事件流叫做事件冒泡,即事件开始是由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。如下例1.1所示:
- <html>
- <head>
- <title> Event Bubbling Example </title>
- </head>
- <body>
- <div id="myDiv">Click Me</div>
- </body>
- </html>
<html> <head> <title> Event Bubbling Example </title> </head> <body> <div id="myDiv">Click Me</div> </body> </html>
若单击<div>元素,那么click事件会按照如下顺序传播:
a)<div>
b)<body>
c)<html>
d)<document>
click事件会在<div>元素上发生,然后沿着DOM树向上传播,在每一级节点上都发生,直到传播到document对象。如下图所示:
所有现代浏览器都支持事件冒泡,但有区别。IE5.5及更早的版本的事件冒泡跳过<html>元素,直接从<body>到<document >,Firefox、Chrome和Safari则将事件一直冒泡到window对象。下面为一个事件冒泡实例,使用jQuery实现:
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>Event Bubbling Example</title>
- <style type="text/css">
- *{margin:0;padding:0;}
- body { font-size: 13px; line-height: 130%; padding: 60px; }
- #content { 220px; border: 1px solid #0050D0;background: #96E555 }
- span { 200px; margin: 10px; background: #666666; cursor: pointer;color:white;display:block;}
- p {200px;background:#888;color:white;height:16px;}
- </style>
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js" type="text/javascript"></script>
- <script type="text/javascript">
- $(function(){
- // 为span元素绑定click事件
- $('span').bind("click",function(){
- var txt = $('#msg').html() + "<p>内层span元素被点击.<p/>";
- $('#msg').html(txt);
- });
- // 为div元素绑定click事件
- $('#content').bind("click",function(){
- var txt = $('#msg').html() + "<p>外层div元素被点击.<p/>";
- $('#msg').html(txt);
- });
- // 为body元素绑定click事件
- $("body").bind("click",function(){
- var txt = $('#msg').html() + "<p>body元素被点击.<p/>";
- $('#msg').html(txt);
- });
- })
- </script>
- </head>
- <body>
- <div id="content">
- 外层div元素
- <span>内层span元素</span>
- 外层div元素
- </div>
- <div id="msg"></div>
- </body>
- </html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Event Bubbling Example</title> <style type="text/css"> *{margin:0;padding:0;} body { font-size: 13px; line-height: 130%; padding: 60px; } #content { 220px; border: 1px solid #0050D0;background: #96E555 } span { 200px; margin: 10px; background: #666666; cursor: pointer;color:white;display:block;} p {200px;background:#888;color:white;height:16px;} </style> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js" type="text/javascript"></script> <script type="text/javascript"> $(function(){ // 为span元素绑定click事件 $('span').bind("click",function(){ var txt = $('#msg').html() + "<p>内层span元素被点击.<p/>"; $('#msg').html(txt); }); // 为div元素绑定click事件 $('#content').bind("click",function(){ var txt = $('#msg').html() + "<p>外层div元素被点击.<p/>"; $('#msg').html(txt); }); // 为body元素绑定click事件 $("body").bind("click",function(){ var txt = $('#msg').html() + "<p>body元素被点击.<p/>"; $('#msg').html(txt); }); }) </script> </head> <body> <div id="content"> 外层div元素 <span>内层span元素</span> 外层div元素 </div> <div id="msg"></div> </body> </html>
单击最内层<span>元素时,显示结果如下:
2)事件捕获
事件捕获的思想是不太具体的节点应该更早接收事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预定目标之前捕获它。在例1.1中,那么单击<div>元素会以下列顺序触发click事件:
a)document
b)<html>
c)<body>
d)<div>
事件捕获中,document对象首先接收到click事件,然后沿着DOM树依次向下,一直传播到事件的实际目标,即<div>元素。如下图所示:
3)DOM事件流
“DOM2级事件”规定了事件流的三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。例1.1中单击<div>元素会按照下图触发事件:
在DOM事件流中,实际的目标(<div>元素)在捕获阶段不会接收事件。在捕获阶段,事件从document到<html>再到<body>后就停止了;下一个阶段是“处于目标”阶段,于是事件在<div>上发生,并在事件处理中被看成冒泡的一部分;然后是冒泡阶段,事件又传播回文档。
除了IE不支持DOM事件流,其余四大主流浏览器都支持。
(二)事件处理程序
响应某个事件的函数就叫做事件处理程序,或称为事件侦听器。事件处理程序都是以"on"开头的。例如onclick、onload事件。
下面介绍为事件指定处理程序的几种方法:
1)HTML事件处理程序
某个元素支持每种事件,都可以使用一个与相应事件处理程序同名的HTML特性表示。该特性的值是能够执行的JavaScript代码。如下例所示:
<input type="button" value="Click Me" onclick="alert('Clicked')"/>
也可以使用下述方法,先定义事件处理程序执行的具体动作:
- <script type="text/javascript">
- function showMessage(){
- alert("hello world!");
- }
- </script>
- <input type="button" value="Click Me" onclick=" showMessage()" />
<script type="text/javascript"> function showMessage(){ alert("hello world!"); } </script> <input type="button" value="Click Me" onclick=" showMessage()" />
事件处理程序也可以被包含在独立的外部文件中,事件处理程序中的代码在执行时,有权利访问全局作用域中的任何代码。
HTML事件处理程序缺点:
a)时差问题。用户可能在HTML元素一出现就触发相应事件,而此时事件处理程序有可能还不具备执行条件。
上例中若showMessage()函数在按钮的下方,页面的最底部定义时,若用户在解析showMessage()函数之前就单击按钮,就会引发错误。需将事件错误处理程序放在一个try-catch块中。
<input type="button" value="Click Me" onclick="try{showMessage();} catch(ex){}"/>
b)HTML与JS代码紧密耦合。
2)DOM0级事件处理程序
通过js指定事件处理程序的传统方法,就是将一个函数赋值给一个事件处理程序的属性。该方法比较简单,而且具有跨浏览器的优势。
在使用js指定事件处理程序时,需要指定一个操作对象的引用。每个元素(包括window和document)都有自己的事件处理程序属性,属性通常全部小写,例如onclick。
var btn=document.getElementById("myBtn"); btn.onclick=function(){ alert("Clicked"); };
该方法指定的事件处理程序被认为是元素的方法,在元素的作用域中运行的。
var btn=document.getElementById("myBtn"); btn.onclick=function(){ alert(this.id); //"myBtn" };
可以在任何事件处理程序中通过this访问元素的任何属性和方法。以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。
也可以删除DOM0级方法中指定的事件处理程序。
btn.onclick=null; //删除事件处理程序
HTML事件处理程序中。onclick属性就是一个包含着同名HTML特性中指定的代码的函数,也可设置相应的属性为null,删除这种指定的事件处理程序。
3)DOM2级事件处理程序
定义两种方法用于指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。
这两种方法介绍三个参数:要处理的事件名,作为事件处理程序的函数和一个布尔值。
布尔值为true表示在捕获阶段调用事件处理程序,如果为false表示在冒泡阶段调用事件处理程序。如下例所示:
a)addEventListener()
- var btn=document.getElementById("myBtn");
- btn.addEventListener("click",function(){
- alert("this.id");
- },false);
var btn=document.getElementById("myBtn"); btn.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);
var btn=document.getElementById("myBtn"); btn.addEventListener("click",function(){ alert("this.id"); },false); btn.addEventListener("click",function(){ alert("Hello World!"); },false);
这两种事件处理程序会按照添加它们的顺序触发。
b)removeEventListener()
通过addEventListener()添加的事件处理程序只能通过removeEventListener()来删除,而且需要传入的参数与添加事件处理程序的参数相同
- var btn=document.getElementById("myBtn");
- btn.addEventListener("click",function(){
- alert("this.id");
- },false);
- //省略其他的代码
- btn.removeEventListener("click",function(){ //没有用!传入的是完全不同的函数
- alert("this.id");
- },false);
var btn=document.getElementById("myBtn"); btn.addEventListener("click",function(){ alert("this.id"); },false); //省略其他的代码 btn.removeEventListener("click",function(){ //没有用!传入的是完全不同的函数 alert("this.id"); },false);
可使用下例方法:
- var btn=document.getElementById("myBtn");
- var handler=function(){
- alert("this.id");
- };
- btn.addEventListener("click",handler,false);
- //省略其他的代码
- btn.removeEventListener("click",handler,false);
var btn=document.getElementById("myBtn"); var handler=function(){ alert("this.id"); }; btn.addEventListener("click",handler,false); //省略其他的代码 btn.removeEventListener("click",handler,false);
大多数都是将事件处理程序添加到事件流中的冒泡阶段,可以更好的兼容更多的浏览器。
4)IE事件处理程序
IE中使用与DOM类似的两种方法:attachEvent()和detachEvent()。只接受相同的两个参数:事件处理程序名称和事件处理程序函数。IE只支持冒泡。
a)attachEvent()
- var btn=document.getElementById("myBtn");
- btn.attachEvent("onclick",function(){ //参数为onclick,与DOM中addEventListener中的参数不同
- alert("Clicked");
- });
var btn=document.getElementById("myBtn"); btn.attachEvent("onclick",function(){ //参数为onclick,与DOM中addEventListener中的参数不同 alert("Clicked"); });
此方法中的事件处理程序会在全局作用域中运行,因此this等于window,如下例所示:
- var btn=document.getElementById("myBtn");
- btn.attachEvent("onclick",function(){
- alert(this==window); //true
- });
var btn=document.getElementById("myBtn"); btn.attachEvent("onclick",function(){ alert(this==window); //true });
attachEvent()也可以为一个元素添加多个事件处理程序。但添加的事件处理程序不是以添加的顺序执行,而是以相反的顺序被触发。
b)detachEvent()
两种方法的参数必须是相同的,意味着添加的匿名函数将不能被移除。可使用下述方法,将保存在变量handler中的函数作为事件处理程序:
- var btn=document.getElementById("myBtn");
- var handler=function(){
- alert("Clicked");
- };
- btn.attachEvent("onclick",handler);
- btn.detachEvent("onclick",handler);
var btn=document.getElementById("myBtn"); var handler=function(){ alert("Clicked"); }; btn.attachEvent("onclick",handler); btn.detachEvent("onclick",handler);
5)跨浏览器的事件处理程序
创建方法addHandler()方法和removeHandler()方法,方法属于一个EventUtil对象,用于添加和移除事件处理成程序。如下所示:
- //跨浏览器的事件处理程序
- var EventUtil={
- addHandler:function(element,type,handler){
- if(element.addEventListener){ //检测DOM2级方法
- element.addEventListener(type,handler,false);
- } else if(element.attachEvent){ //检测IE方法
- element.attachEvent("on"+type,handler);
- } else{
- element["on"+type]=handler; //使用DOM0方法
- }
- },
- removeHandler:function(element,type,handler){
- if(element.removeEventListener){ //检测DOM2级方法
- element.removeEventListener(type,handler,false);
- } else if(element.detachEvent){ //检测IE方法
- element.detachEvent("on"+type,handler);
- } else{
- element["on"+type]=null; //使用DOM0方法
- }
- }
- };
//跨浏览器的事件处理程序 var EventUtil={ addHandler:function(element,type,handler){ if(element.addEventListener){ //检测DOM2级方法 element.addEventListener(type,handler,false); } else if(element.attachEvent){ //检测IE方法 element.attachEvent("on"+type,handler); } else{ element["on"+type]=handler; //使用DOM0方法 } }, removeHandler:function(element,type,handler){ if(element.removeEventListener){ //检测DOM2级方法 element.removeEventListener(type,handler,false); } else if(element.detachEvent){ //检测IE方法 element.detachEvent("on"+type,handler); } else{ element["on"+type]=null; //使用DOM0方法 } } };
可以像下面这样使用EventUtil对象:
- var btn=document.getElementById("myBtn");
- var handler=function(){
- alert("Clicked");
- };
- EventUtil.addHandler(btn,"click",handler);
- //省略其他代码
- EventUtil.removeHandler(btn,"click",handler);
var btn=document.getElementById("myBtn"); var handler=function(){ alert("Clicked"); }; EventUtil.addHandler(btn,"click",handler); //省略其他代码 EventUtil.removeHandler(btn,"click",handler);
该两种方法仅用来添加和移除事件处理程序,并没有考虑所有的浏览器问题,例如IE中作用域问题。
未完待续(*^__^*)
参考:《JavaScript高级程序设计(第二版)》