你的位置:首页 > Java教程

[Java教程]JavaScript高级程序设计(第三版)学习笔记13、14章


第13章,事件

事件冒泡

IE的事件叫做事件冒泡:由具体到不具体
<!DOCTYPE html>
<html>
<head>
     <title>Event Bubbling Example</title>
</head>
<body>
     <div id="myDiv">Click Me</div>
</body>
</html>
如果你单击了<div>元素,那么这个click事件按如下顺序传播:
<div> --> <body> --> <html> --> document
事件冒泡过程:

事件捕获:

Netscape Communicator团队提出事件流:事件捕获:由不具体到具体
依然以上面的页面为例,如果你单击了<div>元素,那么这个click事件按如下顺序传播:
document --> <html> --> <body> --> <div>

注:由于老版本浏览器不支持,所以较少使用事件捕获,建议使用事件冒泡,在特殊需要时再用事件捕获
DOM2级事件规定事件流三阶段:事件捕获阶段,处于目标阶段,事件冒泡阶段
以前面的HTML页面为例,单击<div>触发事件顺序:

事件:用户或浏览器在自身执行的某种动作,click、load、mouseover
事件处理程序(事件侦听器):响应某个事件的函数,以on开头,例:onclick,onload

HTML事件处理程序

例:
<input type="button" value="Click Me" onclick="alert('Clicked')" />

单击,显示警告框。onclick特性的值不能使用未经转移的HTML语法字符:&,",<,>。若想使用双引号:

<input type="button" value="Click Me" onclick="alert(&quot;Clicked&quot;)" /><!-- 输出 “click” --><input type="button" value="Click Me" onclick="alert(event.type)" />

通过event变量,可以直接访问事件对象,且,在函数内部,this值等于事件的目标元素,例:

<!-- 输出 “Click Me” --><input type="button" value="Click Me" onclick="alert(this.value)" />

还可以使用扩展作用域,在函数内部可以像访问局部对象一样访问document及该元素本身,so可以如下使用with扩展作用域:

function(){   with(document){     with(this){        //元素属性值     }   }}

如此一来,事件处理程序访问属性就简单多了:

<!-- 输出 “Click Me” --><input type="button" value="Click Me" onclick="alert(value)" />

可能是form表单输入元素,则作用域还会包含表单元素(父元素)的入口:

function(){   with(document){     with(this.form){        with(this){          //元素属性值        }     }   }}

实际上只是为了让事件处理程序无需引用表单元素就能访问其他表单字段:

<form>  <input type="text" name="username" value="">  <input type="button" value="Echo Username" onclick="alert(username.value)"></form>

缺点:
1、存在时差问题,也许用户在HTML元素一出现就点击,但事件处理程序也许还不具备执行条件,则点击会出错,为此HTML事件处理程序都会装在一个try-catch语句中,例:
<input type="button" value="Click Me" onclick="try{showMessage();}catch(ex){}" />

2、这样扩展作用域,在不同浏览器会导致不同结果,容易出错
3、HTML与js代码紧密耦合,不易修改

DOM0级事件处理程序

传统方式:将函数赋值给事件处理程序属性,原因:1、简单,2、有跨浏览器优势
每个元素都有自己的事件处理程序属性,通常全部小写,例onclick,使用示例:
var btn = document.getElementById("myBtn");btn.onclick = function(){  alert("Clicked");};

使用DOM0级事件处理程序被认为是元素的方法,为此是在元素作用域中运行,即,程序中的this引用当前元素,例:

var btn = document.getElementById("myBtn");btn.onclick = function(){  alert(this.id); //"myBtn"};

以上方式会在事件流的冒泡阶段处理
删除程序:
btn.onclick = null;   //删除事件处理程序

DOM2级事件处理程序


定义两个方法用于处理指定和删除事件处理程序的操作:addEventListener(),removeEventListener(),所有DOM节点都包含这两个方法,且都接收3个参数:要处理的事件名,作为事件处理程序的函数,布尔值。布尔值为true,捕获阶段调用函数,false,冒泡阶段调用函数

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

使用DOM2级好处:添加多个程序,触发顺序按添加顺序

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

通过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");var handle = function(){  alert(this.id);}btn.addEventListener("click",handle,false);//其他代码btn.removeEventListener("click",handle,false); //有效

注:大多数情况下添加到冒泡阶段,可以最大限度兼容浏览器,不建议在捕获阶段注册处理程序

IE事件处理程序

实现与DOM类型的方法:attachEvent,detachEvent,接收相同的两个参数:程序名称,程序函数

var btn = document.getElementById("myBtn");btn.attachEvent("onclick",function(){  alert(this.id);});

attachEvent是在全局作用域运行,因此this指向window
attachEvent也可以添加多个处理程序,不过,触发按添加相反顺序触发
attachEvent添加的只能detachEvent移除,一样不能使用匿名函数

跨浏览器的事件处理程序

//跨浏览器事件处理程序var EventUtil = {  addHandler:function(element,type,handler){    if(element.addEventListener){      element.addEventListener(type,handler,false);  //DOM2级    }else if(element.attachEvent){      element.attachEvent("on" + type,handler);    //兼容IE8及更早版本,加上“on”,IE方法    }else{      element["on" + type] = handler;         //DOM0    }  },  removeHandler:function(element,type,handler){    if(element.removeEventListener){      element.removeEventListener(type,handler,false);//DOM2级    }else if(element.detachEvent){      element.detachEvent("on" + type,handler)    //兼容IE8及更早版本,加上“on”,IE方法    }else{      element["on" + type] = null;    }  }};

使用示例:

//使用示例var btn = document.getElementById("myBtn");var handler = function(){  alert("hello");};EventUtil.addHandler(btn,"click",handler);//其他代码EventUtil.removeHandler(btn,"click",handler);

事件对象

兼容DOM的浏览器会传入一个event对象到事件处理程序中
event对象包含与创建它的特定事件有关的属性和方法,触发类型不一样,可用属性方法不一样,但都有下表成员
属性/方法类型读/写说明
bubblesBoolean只读是否冒泡
cancelableBoolean只读会否可取消默认行为
currentTargetElement只读当前处理的元素
defaultPreventedBoolean只读true表示已调用preventDefault方法(DOM3新增)
detailInteger只读与事件相关细节信息
eventPhaseInteger只读1、捕获阶段,2、处于目标,3、冒泡阶段
preventDefaultFunction只读取消时间默认行为,cancelable为true可使用
stopImmediatePropagationFunction只读取消事件的进一步捕获或冒泡,并阻止事件处理程序调用(DOM3新增)
stopPropagationFunction只读取消事件进一步捕获或冒泡,若bubbles为true,可使用此方法
targetElement只读事件目标
trustedBoolean只读true事件由浏览器生成,false事件由js生成(DOM3新增)
typeString只读被触发事件类型
viewAbstractView只读与事件关联的抽象视图,等同于发生事件的window对象

在事件处理程序内部,this始终等于currentTarget,target只包含事件实际目标(个人理解:就是事件在哪个元素产生,就是那个元素),若直接将事件处理程序指定给目标元素,则三者相同值,例:

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

若事件在按钮父节点,则不一样:

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

在需要一个函数处理多个事件时,可以使用type属性:

//一个函数处理多个事件var btn = document.getElementById("myBtn");var handler = function(event){  switch(event.type){    case "click":      alert("click");      break;    case "mouseover":      alert("mouseover");      break;    case "mouseout":      alert("mouseout");      break;  }};btn.onclick = handler;btn.onmouseover = handler;btn.onmouseout = handler;

IE中的事件对象

与访问DOM中的event对象不同,要访问IE中的event对象有几种不同的方式,取决于事件处理程序的方法。DOM0级方法,event作为window对象的一个属性存在
var btn = document.getElementById("myBtn");btn.onclick = function(){  var event = window.event;  alert(event.type);   //"click"};

事件处理程序使用attachEvent添加,那么会有个event对象传入:

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

若通过HTML指定事件处理程序还可以通过一个名叫event的变量访问event对象,例:

<input type="button" value="Click Me" onclick="alert(event.type)">

IE的event对象一样包含与创建它的事件相关的属性和方法,基本都有对应的DOM属性和方法,也因事件类型不同而不同,但都有如下属性:
属性/方法类型读/写说明
cancelBubbleBoolean读/写默认false,设为true,取消事件冒泡
returnValueBoolean读/写默认true,设为false可以取消事件默认行为,与DOM的preventDefault方法一样
serElementElement只读事件目标,与DOM的target相同
typeString只读被触发的事件类型


因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为this会始终等于事件目标,为此,最好使用event.serElement比较保险,例:

var btn = document.getElementById("myBtn");btn.onclick = function(){  alert(window.event.srcElement === this);  //true};btn.attachEvent("onclick",function(event){  alert(event.srcElement === this);      //false});

跨浏览器事件对象:

//跨浏览器事件对象var EventUtil = {  addHandler:function(element,type,handler){    if(element.addEventListener){      element.addEventListener(type,handler,false);  //DOM2级    }else if(element.attachEvent){      element.attachEvent("on" + type,handler);    //兼容IE8及更早版本,加上“on”,IE方法    }else{      element["on" + type] = handler;         //DOM0    }  },  getEvent:function(event){                //返回对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){    if(element.removeEventListener){      element.removeEventListener(type,handler,false);//DOM2级    }else if(element.detachEvent){      element.detachEvent("on" + type,handler)    //兼容IE8及更早版本,加上“on”,IE方法    }else{      element["on" + type] = null;    }  },  stopPropagation:function(event){    if(event.stopPropagation){      event.stopPropagation();    }else{      event.cancelBubble = true;    }  }};//使用示例btn.onclick = function(event){  event = EventUtil.getEvent(event);  var target = EventUtil.getTarget(event);};var link = document.getElementById("myLink");link.onclick = function(event){             //可确保在所有浏览器中单击该链接都不会打开另一个页面  event = EventUtil.getEvent(event);  EventUtil.preventDefault(event);};//由于IE不支持事件捕获,所以只能用来阻止事件冒泡var btn = document.getElementById("myBtn");btn.onclick = function(event){  alert("Clicked");  event = EventUtil.getEvent(event);  EventUtil.stopPropagation(event);};document.body.onclick = function(event){  alert("body clicked");};

事件类型

UI事件

DOMActivate:表示元素已被激活,DOM3已废弃,不建议使用
load:页面完全加载后在window上面触发,当所有框架加载完在框架集上触发,当图像加载完在<img>元素触发,当嵌入内容加载完在<object>元素上面触发
unload:与load的加载相反
abort:在用户停止下载过程时,若嵌入内容没有加载完,在<object>元素上触发
error:js错误在window触发,无法加载图像在<img>触发,无法加载嵌入内容在<object>触发,或当一个或多个框架无法加载在框架集上触发
select:用户选择文本框一或多个字符时触发
resize:窗口或框架大小变化时在window或框架上触发
scroll:用户滚动带滚动条的元素中的内容时,在该元素触发,<body>包含所加载页面滚动条
除DOMActivate事件外,其他事件在DOM2级都为HTML事件,DOMActivate在DOM2中依旧是UI事件

//浏览器是否支持DOM3级事件定义的事件var isSupported = document.implementation.hasFeature("UIEvent","3.0");

1、load事件

当页面完全加载完后(包括图像,js,css等外部文件),就会触发window的load事件
EventUtil.addHandler(window,"load",function(event){  alert("Loaded");});

第二种指定onload事件处理程序方式是为<body>添加onload属性:

<!DOCTYPE html><html><head>  <title>Load Event Example</title></head><body onload="alert('Loaded!')">  </body></html>

建议:尽可能使用js方式
在IE9+,Firefox,Opera,Chrome,Safari3+中,<script>也会触发load事件,可以确定动态加载的js文件是否加载完毕,例:
EventUtil.addHandler(window,"load",function(){  var script = document.createElement("script");  EventUtil.addHandler(script,"load",function(event){    alert("Loaded");  });  script.src = "example.js";  document.body.appendChild(script);});

IE和Opera还支持<link>上的load事件,以便确定动态加载的样式表是否加载完毕

2、unload事件

与load相对应,使用方式一致,也有两种方式
注:无论哪种方式,都要小心编写onunload代码。unload事件是在一切都被卸载之后才触发,那么页面加载后存在的对象,此时就不一定存在。

3、resize事件


浏览器窗口被调整时在window上触发,所以可以通过js或<body>元素中的onresize特性来指定事件处理程序

4、scroll事件

虽然在window对象上发生,但它实际表示的则是页面中相应元素的变化

焦点事件

blur:元素失去焦点时触发,不会冒泡,所有浏览器支持
DOMFocusIn:元素获得焦点触发,与HTML事件focus等价,冒泡,只有Opera支持,DOM3废弃,选择focusIn
DOMFocusOut:元素失去焦点触发,是HTML事件blur的通用版本,只有Opera支持,DOM3废弃,选择focusOut
focus:元素获得焦点触发,不冒泡,所有浏览器支持
focusIn:元素获得焦点触发,与HTML事件focus等价,冒泡,支持的浏览器:IE5.5+,Safari5.1+,Opera11.5+,Chrome
focusOut:元素失去焦点触发,与HTML事件focus等价,冒泡,支持的浏览器:IE5.5+,Safari5.1+,Opera11.5+,Chrome
当焦点从页面一个元素移动到另一个元素,依次触发事件:
focusOut:失去焦点的元素触发
focusIn:获得焦点的元素触发
blur:失去焦点的元素触发
DOMFocusOut:失去焦点的元素触发
focus:获得焦点的元素触发
DOMFocusIn:获得焦点的元素触发
确定浏览器是否支持这些事件:


//确定浏览器是否支持焦点事件var isSUpported = document.implementation.hasFeature("FocusEvent","3.0");

鼠标与滚轮事件

click:单击主鼠标(一般为左键),或按下回车
dbclick:双击主鼠标
mousedown:用户按下任意鼠标按钮触发,不能通过键盘触发
mouseenter:鼠标光标从元素外部首次移动到元素范围之内触发,不冒泡,移动到后代元素不触发,IE,Firefox9+,Opera支持
mouseleave:在位于元素上方的鼠标光标移动到元素范围之外触发,不冒泡,移动到后代元素不触发,IE,Firefox9+,Opera支持
mousemove:鼠标指针在元素内部移动时重复触发,不能通过键盘触发
mouseout:从一个元素上方移动到另一个元素上方触发,不能通过键盘触发
mouseover:在鼠标指针位于一个元素外部,首次移入另一个元素边界之内触发,不能通过键盘触发
mouseup:释放鼠标按钮触发,不能通过键盘触发
mousedown + mouseout 触发 click,有一个被取消,click都不会触发,click + click 触发 dbclick
检测浏览器是否支持以上DOM2级事件(除dbclick,mouseenter,mouseleave)
//确定浏览器是否支持DOM2级鼠标事件var isSUpported = document.implementation.hasFeature("MouseEvents","2.0");//确定浏览器是否支持所有鼠标事件var isSUpported = document.implementation.hasFeature("MouseEvent","3.0");   //差了个s

滚轮事件,mousewheel事件

1、客户区坐标位置

clientX,clientY,保存鼠标事件在浏览器视口的特定位置,表示事件发生时鼠标在视口中的水平和垂直坐标
注意:并不包括页面滚动距离,因此并不表示鼠标在页面上的位置,只是视口位置

2、页面坐标位置

pageX,pageY,保存鼠标事件发生在页面的具体位置
IE8级更早版本不支持事件对象上的页面坐标,使用客户区坐标和滚动信息可以计算出来,使用document.body(混杂模式)或document.documentElement(标准模式)中的scrollLeft和scrollTop,例:
var div = document.getElementById("myDiv");EventUtil.addHandler(div,"click",function(event){  event = EventUtil.getEvent(event);  var pageX = event.pageX;    pageY = event.pageY;  if(pageX == undefined){    pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft);  }  if(pageY == undefined){    pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop);  }  alert("Page coordinates: " + pageX + "," + pageY);});

3、屏幕坐标位置

screenX,screenY,保存相对于整个电脑屏幕的位置

4、修改键

Shift,Ctrl,Alt,Meta(window键盘的window键,苹果的cmd),用来修改鼠标事件的行为,DOM规定4个属性:shiftKey,ctrlKey,altKey,metaKey,包含的都是布尔值,IE9、Firefox,Safari,Chrome,Opera都支持这四个属性,IE8及之前不支持

5、相关元素

mouseover,mouseout事件会涉及更多元素,mouseover,主要目标是获得光标的元素,相关元素是失去光标的元素,mouseout,主要目标是失去光标的元素,相关元素是获得光标的元素
DOM通过event对象的relatedTarget属性提供了相关的信息,这个属性只对mouseover,mouseout事件才包含值,其他的事件为null,IE8及之前版本不支持,提供其他属性。在mouseover触发时,IE的fromElement保存相关元素,在mouseout触发,IE的toElement保存相关元素(IE9支持所有这些属性)。把跨浏览器取得相关元素的方法加入到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;    }  },  //其他代码};

6、鼠标按钮

在mousedown,mouseup中event对象保存一个button属性,0、主按钮,1、中间按钮(滚轮),2、次按钮
IE8及更早版本也提供button属性,但与DOM的button属性值有大差异:
0:没有按下按钮
1:按下主按钮
2:按下次按钮
3:同时按下主次按钮
4:按下中间按钮
5:同时按下主和中
6:同时按下次和中
7:同时按下三键
为EventUtil对象添加getButton方法:
var EventUtil = {  //其他代码  getButton:function(event){    if(document.implementation.hasFeature("MouseEvents","2.0")){    //检测MouseEvents特性可以知道event对象存在的button属性是否包含正确的值,失败,说明是IE      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;      }    }  },  //其他代码}

7、更多的事件信息

并没啥用,一方面只有IE支持,另一方面,提供的信息要么没啥用,要么可以通过其他方式计算出来

8、鼠标滚轮事件

mousewheel,IE6首先实现,Opera,Chrome,Safari随后也支持,与mousewheel事件对应的event对象另外包含了一个特殊的wheelDelta属性,当用户向前滚动滚轮,wheelDelta为正数,向后为负数
注:Opera9.5之前是正负号是相反的
Firefox支持一个名为DOMMouseScroll的类似事件,滚轮信息保存在detail属性中,向前是负数,向后是正数
将获取鼠标滚轮增量值的方法添加到EventUtil对象中:
var EventUtil = {  //其他代码  getWheelDelta:function(event){    if(event.wheelDelta){      return (client.engine.opera && client.engine.opera < 9.5 ?        -event.wheelDelta : event.wheelDelta);    }else{      return -event.detail * 40;    }  },  //其他代码}

9、触摸设备

10、无障碍性问题

键盘与文本事件

键盘事件的支持主要遵循DOM0级

3个键盘事件:

keydown:按下键盘任意键触发,按住不放可以重复触发
keypress:按下字符键触发,按住不放可以重复触发,ESC也会触发,Safari3.1之前版本按下非字符键也会触发
keyup:释放键盘上的按键触发
只有一个文本事件:textInput,在文本插入文本框之前触发
键盘事件与鼠标事件一样,支持修改键,IE不支持metaKey

1、键码

发生keydown和keyup事件时,event对象的keyCode属性会包含一个代码,DOM和IE的event对象都支持

2、字符编码

IE9、Firefox、Chrome、Safari的event对象都支持一个charCode属性,只有发生keypress事件才包含值,代表按下的键的ASCII编码。此时keyCode为0或者等于按键键码。
跨浏览器方式取得字符编码



var EventUtil = {  //其他代码  getCharCode:function(event){    if(typeof event.charCode == "number"){      return event.charCode;    }else{      return event.keyCode;    }  },  //其他代码}

取得的字符编码可以使用String.fromCharCode()将其转换成实际字符

3、DOM3级变化

DOM3不再包含charCode属性,改为新属性:key,char
key为了取代keyCode新增,值是字符串,按下某个字符键时,是对应的文本字符,按下非字符键,是相应键值(shift等),char属性的值在按下字符键与key相同,按下非字符键为null
IE9+支持key,不支持char,Safari5和Chrome支持名为keyIdentifier的属性,按下非字符键与key同,按下字符键返回格式U+0000的字符串,表Unicode值
注:为跨浏览器,不推荐使用key,keyIdentifier,char
DOM3级事件还添加一个location属性,是一个数值
0:默认键盘
1:左侧位置(左alt)
2:右侧位置(右alt)
3:数字小键盘
4:移动设备键盘(虚拟键盘)
5:手柄(任天堂Wii控制器)
IE9支持这个属性,Safari和Chrome支持名为keyLocation属性,但有bug,值始终为0,除非按下数字键,值为3,否则不会是1245的值
注:支持location浏览器不多,不推荐使用

4、textInput事件

DOM3级事件规范引入新事件:textInput。在可编辑区域中输入字符触发。与keypress的textInput事件行为稍为不同。
区别一:
任何可以获得焦点的元素都可以触发keypress事件,但只有可编辑区域才能触发textInput事件
区别二:
textInput事件只有用户输入能够输入实际字符的键才会触发,keypress事件则再按下能够影响文本显示的键(退格键)也会触发
textInput事件主要考虑字符,因此它的event对象还包含一个data属性,保存用户输入的字符
使用textInput事件的例子:
var textbox = document.getElementById("myText");EventUtil.addHandler(textbox,"textInput",function(event){  event = EventUtil.getEvent(event);  alert(event.data);});

event对象上还有一个属性:inputMethod,表示把文本输入文本框的方式
0:不确定
1:键盘输入
2:粘贴
3:拖放
4:IME输入
5:表单选择某一项输入
6:手写输入(比如使用手写笔)
7:语音输入
8:几种方法组合输入
9:脚本输入
支持textInput属性的浏览器:IE9+,Safari和Chrome,只有IE支持inputMethod属性

5、设备中的键盘事件

复合事件

DOM3中新添加事件,用于处理IME(Input Method Editor,输入法编辑器)输入序列,可以让用户输入物理键盘上找不到的字符

变动事件

DOM2级的变动事件能在DOM中的某一部分发生变化时给出提示。是为DOMSubtreeModified:DOM结构发生任何变化触发,在其他任何事件触发后都会触发
DOMNodeInserted:在一个节点作为子节点被插入到另一个节点时触发
DOMNodeRemoved:节点移除触发
DOMNodeInsertedIntoDocument:在节点被直接插入文档或通过子树间接插入文档之后触发,在DOMNodeInserted之后触发
DOMNodeRemovedFromDocument:在节点被直接从文档或通过子树间接从文档中移除之前触发,在DOMNodeRemoved之后触发
DOMAttrModified:特性被修改触发
DOMCharacterDataModified:在文本节点值发生变化触发

//检查浏览器是否支持变动事件var isSUpported = document.implementation.hasFeature("MutationEvents","2.0");

1、删除节点

例:
<!DOCTYPE html><html><head>  <title>Node Removal Events Example</title></head><body>  <ul id="myList">    <li>Item 1</li>    <li>Item 2</li>    <li>Item 3</li>  </ul></body></html>

假设移除<ul>元素,触发事件顺序:
1、在<ul>上触发DOMNodeRemoved,relatedNode属性等于document.body
2、在<ul>触发DOMNodeRemovedFromDocument
3、在身为<ul>子节点的每个<li>及文本节点触发DOMNodeRemovedFromDocument
4、在document.body触发DOMSubtreeModified,因为<ul>是document.body的直接子元素

2、插入节点

HTML5事件

1、contexmenu事件

冒泡的事件,用于显示自定义的上下文菜单

2、beforeunload事件

3、DOMContentLoaded事件

window的load事件要在文档全部加载完毕才会触发,而DOMContentLoaded事件则在形成完成的DOM树之后就触发,而不理会js文件,图像,css等资源文件的加载
IE9+,Firefox,Chrome,Safari3.1,Opera9+都支持DOMContentLoaded,不支持的建议在页面加载期间设置一个时间为0的超时调用。

4、readystatechange事件

这个事件行为很难预料,支持的对象都有一个readyState属性,值包含:
uninitiallized(未初始化):对象存在但尚未初始化
loading(正在加载):对象正在加载
loaded(加载完毕):对象数据加载完成
interactive(交互):可以操作对象了,但还没有完全加载
complete(完成):对象已加载完毕
这个事件的event对象不提供任何信息,也没有目标对象

5、pageshow和pagehide事件

Firefox和Opera特性,往返缓存,可以在用户使用浏览器的后退前进按钮加快页面转换速度。
Firefox提供新事件:
pageshow,在页面显示时触发。注:必须将事件处理程序添加到window中
pageshow事件的event对象还包含属性:parsisted的布尔值属性,若页面保存在bfcache,值为true,否则为false
pagehide事件,在浏览器卸载页面时触发,在unload之前触发,包含这个事件的event对象也包含persisted属性,在卸载后被保存到bfcache值被设置为true
Firefox,Safari5+,Chrome,Opera都支持这两事件,IE9及之前不支持

6、hashchange事件

H5新增,在url参数列表(及URL中#号后面的所有字符串)发生变化通知开发人员,必须要把此事件处理程序添加到window对象,然后URL参数列表只要变化就会调用它。此时event对象包含两个属性:oldURL和newURL,分别保存变化前后的完整url
支持的浏览器IE8+,Firefox3.6+,Safari5+,Chrome,Opera10.6+,只有Firefox6+,Chrome,Opera支持oldURL和newURL属性,为此最好使用location对象来确定当前的参数列表

设备事件

可以让开发人员确定用户再怎样使用设备

1、orientationchange事件

苹果为移动Safari中添加了orientationchange事件,移动Safari的window.orientation属性可能包含3个值:
0:肖像模式
90:左旋转(主屏按钮在右侧)
-90:右旋转(主屏按钮在左侧)

2、MozOrientation事件

Firefox3.6为检测设备方向引入MozOrientation新事件,当设备的加速计检测到设备方向改变时,触发事件,在window对象
此时event对象包含三个属性:x,y,z
x=0,y=0,z=1,竖直状态,设备向右倾斜x减小,反之反之,设备向远离用户倾斜y减小,反之反之,z检测垂直加速度,1表示静止,设备移动时减少,失重时为0

3、deviceorientation事件

deviceorientation事件的意图是告诉开发人员设备在空间中朝向哪儿,而不是如何移动
设备在三维空间中靠x,y,z来定位,静止放在水平表面,都为0,x方向从左往右,y方向从下往上,z方向从后往前(x,y与水平面在同一个平面内,z垂直于水平面)
deviceorientation事件触发时,事件对象包含5个属性:
alpha:围绕z轴旋转(左右旋转),y轴度数差,一个介于0-360的浮点数
beta:围绕x轴旋转(前后旋转),z轴度数差,-180-180浮点数
gamma:围绕y轴旋转(扭转设备),z轴度数差,-90-90浮点数
absolute:布尔值,表示设备是否返回一个绝对值
compassCalibrated:布尔值,表示设备的指南针是否校准过

4、devicemotion事件

devicemotion事件告诉开发人员设备什么时候移动,而不仅仅设备方向如何改变。
触发devicemotion事件时,事件对象包含以下属性
acceleration:包含x,y,z属性的对象,不考虑重力情况下,告诉你每个方向的加速度
accelerationIncludingGravity:考虑z轴自然重力加速度
interval:以毫秒表示的时间值,必须在另一个devicemotion事件触发前传入。每个事件中应该是常量
rotationRate:一个包含表示方向的alpha,beta,gamma属性的对象
若读取不到acceleration,accelerationIncludingGravity,rotationRate的值,则为null

触摸与手势事件

1、触摸事件

touchstart:当手指触摸屏幕时触发;即使已经有一个手指在屏幕上也会触发
touchmove:手指在屏幕上滑动时连续触发
touchend:移开触发
touchcancel:当系统停止跟踪触摸时触发
都会冒泡,也都可以取消
touches:当前跟踪的触摸操作的Touch对象数组
targetTouchs:特定于事件目标的Touch对象数组
changeTouches:自上次触摸以来发生了什么改变的Touch对象数组
每个Touch对象包含以下属性
clientX:触摸目标视口X坐标
clientY:触摸目标视口Y坐标
identifier:标识触摸的唯一ID
pageX:触摸目标在页面中的X坐标
pageY:触摸目标在页面中的Y坐标
screenX:触摸目标在屏幕中的X坐标
screenY:触摸目标在屏幕中的Y坐标
target:触摸的DOM节点目标

2、手势事件

ios2.0的Safari引入了一组手势事件。
gesturestart:当一个手指已经按在屏幕上而另一个手指又触摸屏幕时触发
gesturechange:触摸屏幕的任一个手指位置发生变化时触发
gestureend:任何一个手机从屏幕中移开时触发

内存和性能

事件委托

事件处理程序越多,占用内存越多,性能越差,解决方案是:事件委托。利用了事件冒泡,指定一个事件处理程序就可以管理某一类型的所有事件。以下面HTML为例:
<ul id="myLinks">  <li id="goSomewhere">Go somewhere</li>  <li id="doSomething">Do something</li>  <li id="sayHi">say hi</li></ul>

包含3个单击后会执行操作的列表项,按之前的做法,需要每个都添加事件处理程序。若在复杂的web程序中如此做,将会有数不清的代码用于事件处理程序。使用事件委托,只需在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;  }});

此方法事前消耗更低,因为只取一个DOM元素,只添加了一个事件处理程序,这种技术需要内存更少。所有用到按钮的事件(多数鼠标键盘事件)都适合使用此事件委托技术。
若可行,考虑为document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型事件。优点:
1、document对象很快可以访问,且可在页面生命周期的任何时点为它添加事件处理程序。换句话,只要可单击元素呈现在页面,就可以立即具备适当的功能。
2、页面设置事件处理程序所需时间更少。
3、整个页面占用内存少,能够提升整体性能

移除事件处理程序

空事件处理程序,是造成web程序内存与性能问题的主要原因,处理方案:移除事件处理程序
注:若知道某个元素即将被移除,最好手工移除事件处理程序,例:
<div id="myDiv">  <input type="button" value="Click Me" id="myBtn"></div><script type="text/javascript">  var btn = document.getElementById("myBtn");  btn.onclick = function(){    //先执行某些操作    btn.onclick = null;     //移除事件处理程序    document.getElementById("myDiv").innerHTML = "Processing...";  };</script>

模拟事件

DOM中的事件模拟

1、模拟鼠标事件

2、模拟键盘事件

3、模拟其他事件

4、自定义DOM事件

IE中的事件模拟



第14章,表单脚本

表单基础知识

HTML中由<form>表示,js中则是对应HTMLFormElement类型。继承了HTMLElement,因而与其他的HTML元素具有相同默认值,HTMLFormElement也有自己的属性和方法:
acceptCharset:服务器能够处理的字符集
action:接受请求的URL
elements:表单中所有控件的集合
enctype:请求的编码类型
length:表单中控件的数量
method:要发送的http请求类型
name:表单名称
reset():将所有表单域重置为默认值
submit():提交表单
target():用于发送请求和接收响应的窗口名称
取得<form>元素引用方式:
1、当成普通元素,为其添加id特性,使用getElementById方法找到它
var form = document.getElementById("form1");

2、通过document.forms获取所有表单元素,通过数值索引或name值来取得特定表单

var firstForm = document.forms[0];               //取得页面中的第一个表单var myForm = document.forms["form2"];           //取得页面中name值为“form2“的表单

提交表单


<!-- 通用提交按钮 --><input type="submit" value="Submit Form"><!-- 自定义提交按钮 --><button type="submit">Submit Form</button><!-- 图像按钮 --><input type="image" src="graphic.gif">

只要表单出现上面列出的任何一种按钮,在相应表单获得焦点的情况下,按回车键都可以提交表单。若表单没有提交按钮,按回车键不会提交表单。
在js中,以编程方式调用submit方法也可以提交表单,而且无需表单包含提交按钮,任何时候都可以正常提交表单。
例:
var form = document.getElementById("myForm");//提交表单form.submit();

以此形式提交表单,不会触发submit事件,因此记得调用此方法之前先验证表单数据

重置表单


<!-- 通用重置按钮 --><input type="reset" value="Reset Form"><!-- 自定义重置按钮 --><button type="reset">Reset Form</button>

重置按钮也可以通过js,例:

var form = document.getElementById("myForm");//重置表单form.reset();

与调用submit方法不同,此方法会触发reset事件

表单字段

可以使用原生DOM方法访问表单元素,每个表单都有elements属性,该属性是表单中所有表单元素(字段)的集合。是一个有序列表,包含表单所有字段,可以按照位置和name特性来访问,若有多个控件使用同一个name,就会返回以该name命名的一个NodeList。

1、共有的表单字段属性

除<fieldset>元素外,所有表单元素都拥有相同的一组属性:
disable:布尔值,表示是否被禁用
form:指向当前字段所属表单的指针,只读
name:当前字段名称
readOnly:布尔值,是否只读
tabIndex:当前字段tab切换序号
type:当前字段类型
value:当前字段将被提交给服务器的值,对文件字段来说,是只读的,包含着文件在计算机中的路径
除form属性外,js可以动态修改其他属性

2、共有的表单字段方法

每个表单都有focus,blur方法。
focus用于将浏览器焦点设置到表单字段,即激活表单字段
blur方法,用于从元素中移走焦点。

3、共有的表单字段事件

除了鼠标,键盘,更改和HTML事件外,所有表单字段还支持下列事件:
blur:当前字段失去焦点触发
change:对于<input>,<textarea>,在失去焦点且value改变时触发,对于<select>,选项改变时触发
focus:当前字段获得焦点触发

文本框脚本

<input>表示单行文本框,<textarea>表示多行文本框
<input>,size特性,指定显示字符数,value设置初始值,maxlength文本框可以接受的最大字符数
<textarea>,rows,cols指定文本框大小,<textarea>初始值必须放在<textarea>和</textarea>之间,不能指定最大字符数
注:处理文本框的值时,最好不要使用DOM方法

选择文本

上述两种方法都支持select方法,用于选择所有文本

1、选择(select)事件

与select方法对应的是select事件,选择了文本框的文本就会触发select事件。IE9+,Opera,Firefox,Chrome,Safari,只有用户选择了文本(要释放鼠标),才会触发select事件,IE8及更早版本中,只要用户选择了一个字母(不用释放鼠标),就会触发select事件。在调用select方法时也会触发select事件
例:
var textbox = document.forms[0].elements["textbox1"];EventUtil.addHandler(textbox,"select",function(event){  var alert("Text selected" + textbox.value);});

2、取得选择文本

H5通过扩展方案可以知道用户选择了什么文本,方法是添加两个属性:selectionStart和selectionEnd,保存基于0的数值,表示所选择文本的范围。
IE9+,Opera,Firefox,Chrome,Safari都支持这两个属性,IE8及更早版本不支持,提供另一种方案:document.selection对象。要取得选择文本,必须要创建一个范围。
注:调用document.selection时,不用考虑textbox参数

3、选择部分文本

H5为选择文本框中部分内容提供了解决方案,及最早有Firefox引入的setSelectionRange方法。现在除了select方法外,所有文本框都有一个setSelectionRange方法。接收两个参数:要选择的第一个字符的索引,要选择的最后一个字符的索引。IE9+,Opera,Firefox,Chrome,Safari都支持这种方案,IE8及更早版本支持使用范围选择部分文本。

过滤输入

1、屏蔽字符

理论上只有用户按下字符键才会触发keypress事件,但Firefox和Safari3.1之前会对上键,下键,退格键,删除键触发keypress事件。

2、操作剪切板

剪切板事件:
beforecopy:发生复制操作前触发
copy:发生复制操作时触发
beforecut:剪切前触发
cut:剪切时触发
beforepaste:粘贴前触发
paste:粘贴时触发
要访问剪切板数据,使用clipboardData对象。IE中是window对象的属性,Firefox4+,Safari和Chrome中是相应event对象的属性。
clipboardData对象有三个方法:getData(),setData(),clearData()。
getData()接受一个参数:要取得的数据格式
setData()第一个参数是数据类型,第二个参数是文本
弥合差异:

var EventUtil = {  //其他代码  getCliboardText:function(event){    var clipboardData = (event.clipboardData || window.clipboardData);    return clipboardData.getData("text");  },  setCliboardText:function(event,value){    if(event.clipboardData){      return event.clipboardData.setData("text/plain",value);    }else if(window.clipboardData){      return window.clipboardData.setData("text",value);    }  },  //其他代码};

自动切换焦点

在用户填写完当前字段时,自动将焦点切换到下一个字段。例如,美国号码通常分为三部分:区号,局号,另外4位数字
<input type="text" name="tel1" id="txtTel1" maxlength="3"><input type="text" name="tel2" id="txtTel2" maxlength="3"><input type="text" name="tel3" id="txtTel3" maxlength="4">

自动切换焦点功能实现:

(function(){  function tabForward(event){    event = EventUtil.getEvent(event);    var target = EventUtil.getTarget(event);    if(target.value.length == target.maxLength){      var form = target.form;      for(var i=0,len=form.elements.length;i < len;i++){        if(form.elements[i] == target){          if(form.elements[i+1]){            form.elements[i+1].focus();          }          return ;        }      }    }  }  var textbox1 = document.getElementById("textTel1");  var textbox2 = document.getElementById("textTel2");  var textbox3 = document.getElementById("textTel3");  EventUtil.addHandler(textbox1,"keyup",tabForward);  EventUtil.addHandler(textbox2,"keyup",tabForward);  EventUtil.addHandler(textbox3,"keyup",tabForward);})();

HTML5约束验证API

支持的浏览器:Firefox4+,Safari5+,Chrome,Opera10+

1、必填字段

第一种情况,指定required属性
<input type="text" name="username" required>

适用于<input>,<textarea>,<select>字段

2、其他输入类型

H5为<input>的type增加了几个值,“email”,“url“,得到最多支持的类型。

3、数值范围

注:浏览器支持并不好

4、输入模式

H5为文本字段新增pattern属性。这是属性值是一个正则表达式

5、检测有效性

使用checkValidity()方法可以检测表单中的某个字段是否有效
validity属性提供为什么字段有效或无效的信息

6、禁用验证

设置novalidate属性,可以告诉表单不进行验证

选择框脚本

选择框是<select>,<option>创建的。为方便交互,除表单字段共有属性和方法外,HTMLSelectElement类型还提供如下属性和方法:
add(newOption,relOption):向控件插入新<option>元素,其位置在相关项relOption之前
multiple:布尔值,是否允许多项选择
options:控件中所有<option>元素的HTMLCollection。
remove(index):移除给定位置的项
selectedIndex:基于0的选中项的索引,没有选中项,则为-1,对于多选控件,只保存选中项的第一项的索引
size:选择框中可见行数
选择框的type属性不是select-one就是select-multiple,取决于HTML代码中有没有multiple特性,选择框的value属性由当前选中项决定。
注:不推荐使用DOM技术修改<option>的文本或值
注:选择框的change事件与其他表单字段的change事件触发条件不一样,触发条件:只要选中了选项就触发,不需要焦点离开

选择选项

添加选项

1、使用DOM方法
2、使用Option构造函数
3、使用选择框的add()函数

移除选项

1、使用DOM的removeChild()方法
2、使用选择框的remove()方法
3、将相应选项置为null

移动和重排选项

推荐使用DOM方法

表单序列化

js中可以利用表单字段的type属性,连同name和value属性一起实现对表单的序列化

富文本编辑

本质:在页面中嵌入一个包含空HTML页面的iframe

使用contenteditable属性

操作富文本

使用document.execCommand(),3个参数:要执行的命令名称,表示浏览器是否应该为当前命令提供用户界面的一个布尔值和执行命令必须的一个值(不需要就传null),为确保跨浏览器兼容,第二个参数应始终设为false。Firefox会在true抛出错误
Opera没有实现任何剪贴板命令,Firefox默认禁用。Safari和Chrome实现了cut和copy,没实现paste。

富文本选区

使用框架(iframe)的getSelection方法,可以确定实际选择文本,这个方法是window对象和document对象的属性,调用则返回当前选择文本的Selection对象