《JavaScript高级程序设计》学习笔记——事件

时间:2022-12-26 11:00:44

Author:chemandy

第12章 事件

1.事件流

1.1事件冒泡(IE事件流)

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

□所有浏览器均支持事件冒泡。Firefox、chrome、safari将事件一直冒泡到window对象。

1.2事件捕获(Netscape事件流)

□不太具体的节点更早收到事件,而具体的节点最后收到节点。

□Safari、chrome、Opera、firefox支持,但从window对象开始捕获(DOM2级规范是从document开始)。

1.3DOM事件流

□“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。

□DOM2:捕获阶段不含实际目标,不涉及事件目标,只为截获事件提供机会。

□Safari、chrome、Firefox和Opera9.5以上都会在捕获阶段触发事件对象上的事件。结果有两个机会在目标上操作事件。

□IE不支持DOM事件流。其他支持。

2.事件处理程序(或事件侦听器)

定义:响应某个事件的函数。

2.1 HTML事件处理程序

□某个元素支持的每种事件,都可以使用一个相应事件处理程序同名的HTML特性来指定。

□其中的函数代码字符需经过HTML转义。

□存在问题:

◇时差问题:当触发事件时,事件处理程序有可能尚不具执行条件。使用try-catch块封装,以便错误不会浮出水面。

<input type="button" value="click me" onclick="try{show();}catch(ex){}"/>

◇HTML与JavaScript代码紧密耦合。

2.2 DOM0级事件处理程序

□DOM0级事件处理程序:将一个函数赋值给一个事件处理程序属性。优点:简单、跨浏览器。

□这种方式的事件处理程序在代码运行以前不会绑定事件。

□使用DOM0级方法指定的事件处理程序被认为是元素的方法。事件处理程序在元素作用域中运行;程序中this引用当前元素。

□删除DOM0级事:btn.onclick = null;  //删除事件处理程序

2.3 DOM2级事件处理程序

□addEventListener()和removeEventListener()。接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。

◇如果最后这个布尔值是true,表示捕获阶段调用事件处理程序;如果是false表示冒泡阶段调用事件处理程序。

Var btn = document.getElementById("myBtn");

Btn.addEventListener("click",function(){alert(this.id);},false);

□使用DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。按添加顺序触发。

□通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。即匿名函数无法移除。

□将事件处理程序添加到冒泡阶段,得到最大限度兼容。

□Firefox、Safari、Chrome和Opera支持DOM2级事件处理程序。IE不支持,有独有的事件处理程序。

2.4 IE事件处理程序。

□IE中与DOM2类似方法:attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。

□IE仅支持冒泡,通过attachEvent()添加的事件处理程序都被添加到冒泡阶段。

□attachEvent()中事件处理函数会再全局作用域中运行,因此this等于window。

□attachEvent()也可以为元素添加多个处理程序,以相反的书序触发。

□attachEvent()事件可通过detachEvent()移除,匿名函数无法移除。

2.5 跨浏览器的事件处理程序

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{

elmenet["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;

}

}

};

□此兼容函数没有考虑到浏览器的所有问题,如IE中作用域问题。

3.事件对象

定义:在触发DOM的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。

3.1DOM中的事件对象

①兼容DOM的浏览器会将一个event对象传入到事件处理程序中(DOM0级或DOM2级)。通过HTML特性指定时,event变量保存event对象。

②所有event对象均包含以下属性/方法。P291

□bubbles:表明事件是否冒泡,bool。

□cancelable:表明是否可以取消事件的默认行为,bool。

□currentTarget:其事件处理程序当前正在处理事件的那个元素。

□detail:与事件相关的细节信息。

□eventPhase:调用事件处理程序的阶段:1.捕获 2.处于目标 3.冒泡。

□preventDefault():取消事件默认行为。

□stopPropagation():取消事件的进一步捕获或冒泡。

□target:事件的目标。

□type:被触发的事件类型。

□view:与事件关联的抽象视图。

③在事件处理程序内部:

□对象this始终等于currentTarget的值,即this和currentTarget均指向绑定该事件程序的元素。

□target则只包含事件的实际目标,即触发事件的元素。

④event.preventDefault():可取消特定事件的默认行为。

⑤event.stopPropagation():立即停止事件在DOM中的传播。

⑥通过eventPhase属性确定事件当前正位于事件流哪个阶段。

3.2 IE中的事件对象

①访问IE中的event对象,取决于指定的事件处理程序方法。

□使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。

□使用attachEvent()添加,则有一个event对象作为参数传入。同时也可以通过window.event访问。

□通过HTML特性指定事件处理程序时,可以通过一个名叫event的变量访问event对象。

②IE中所有的event对象包含以下属性/方法:

□cancelBubble:默认为false,设为true可取消事件冒泡(类似DOM中的stopPropagation()方法)

□returnValue:默认为true,设为false可取消事件默认行为。(类似DOM2中的preventDefault()方法)

□srcElement:事件的目标(与DOM2中target属性相同)

□type:被触发的事件类型。

3.3 跨浏览器的事件对象

var eventUtil = {

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;

}

},

stopPropagation : function(event){

if(event.stopPropagation){

event.stopPropagation();

}else{

event.cancelBubble = true;

}

}

};

4.事件类型

□鼠标事件,当用户通过鼠标在页面上执行操作时触发;

□键盘事件,当用户通过键盘在页面上执行操作时触发;

□HTML事件,当浏览器窗口发生变化或发生特定的客户端/服务器交互时触发。

□变动(mutation)事件,当底层DOM结构发生变化时触发。

4.1 UI事件

UI事件主要与元素焦点相关,仅在兼容DOM的浏览器中受到支持:

□DOMActive:表示元素已经被用户操作(通过鼠标或键盘)激活;

□DOMFocusIN:表示元素已经获得焦点;为HTML中focus事件的普通版;

□DOMFocusOut:表示元素已经失去焦点;为HTML中blur事件的普通版;

△由于支持的浏览器很少,不推荐使用。

4.2 鼠标事件

①DOM中定义了7个鼠标事件,页面上所有元素都支持鼠标事件:

□click:在用户单击主鼠标按钮(一般式左边的按钮)或者按下回车键时触发。

□dblclick:在用户双击主鼠标按钮(一般是左键)时触发。

□mousedown:在用户按下了任意鼠标按钮时触发。不能通过键盘触发这个事件。

□mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。新移入的元素可能在旧元素外部,也可以是其子元素。不能通过键盘触发。

□mouseover:在鼠标指标为于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。不通过键盘触发。

□mouseup:在用户释放鼠标按钮时触发。不通过键盘触发。

□mousemove:当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发。

②注意事项:

□同一个元素上相继触发mousedown和mouseup事件才会触发click事件。如果mousedown或mouseup其中之一取消,就不会触发click事件。

□同一元素连续两次触发click事件,才会触发dblclick事件。

4.2.1 客户区坐标位置

□鼠标事件都是在浏览器视口中的特定位置发生的。这个位置信息保存在事件对象(event)中的clientX和clientY属性中。

4.2.2 屏幕坐标位置

□鼠标事件发生时,还有一个相对于整个电脑屏幕的位置。保存在事件对象(event)中的screenX和screenY属性中。

4.2.3 修改键

□虽然鼠标事件主要是使用鼠标来触发,但按下鼠标时键盘上的某些键也可以影响到所需操作。

□修改键为:Shift、Ctrl、Alt和Meta(苹果中cmd键、windows中windows键)。

□DOM中表示这4个键的布尔值属性:(在鼠标事件的event中)shiftKey、ctrlKey、altKey和metaKey(IE不支持metaKey)。

4.2.4 相关元素

发生mouseover和mouseout事件时,会涉及更多元素。两事件都会涉及把鼠标指针从一个元素的边界之内移动到另一个元素的边界之内。

□DOM通过evnet对象的relatedTarget属性提供了相关元素信息。

□IE不支持relatedTarget属性。

◇在mouserover事件触发时,IE的fromElement属性中保存了相关元素。

◇在mouseout事件触发时,IE的toElement属性保存了相关元素。

□兼容方式:

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;

}

}

};

4.2.5 鼠标按钮

□click事件:单击鼠标主键触发,无必要检测按键信息。

□mousedown和mouseup事件,在其event对象中有一button属性表示按下或释放的按钮。

□DOM中的button属性取值:

◇0表示主鼠标按键

◇1鼠标中键(滚轮按钮)

◇2鼠标次键

□IE提供的button属性

◇0没按键◇1按主键◇2按次键◇3同时主+次◇4中键◇5主+中◇6次+中◇7主+次+中

□兼容代码

getButton : function(event){

if(document.implementation.hasFeature("MouseEvents","2.0")){

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;

}

}

}

};

□如果不是按下或释放主键,opera不会触发mouseup或mousedown

4.2.6 更多的事件信息

□DOM2中detail属性:元素单击次数,离离开元素后清0.

□IE为鼠标增加更多信息。(仅IE支持,并无实用价值)。

4.2.7移动Safari

面向iPhone和Ipod中Safari开发时:

□不支持dblclick事件。双击Safari窗口会放大画面,无法改变该行为。

□轻击可单击元素先触发mousemove事件,若导致内容变化则无其他事件发生。若无内容变化,一次发生mousedown、mouseup和click。

□mousemove事件也会触发mouseover和mouseout事件。

4.2.7 易访问性问题

屏幕阅读器只可通过click来触发事件。使用鼠标事件时应该注意:

□使用click事件执行代码

□不要使用onmouseover向用户显示新选项。屏幕阅读器无法触发。

□不要使用dblclick执行重要操作。键盘无法触发。

4.3 键盘事件

①对键盘事件的支持主要遵循DOM0级。“DOM3级”为键盘事件制定了规范。

②3个键盘事件:

□keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。

□keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。

□keyup:当用户释放键盘上的键时触发。

□与鼠标事件一样,支持相同的修改键。而且键盘事件对象中也有shifKey、ctrlKey、altKey、metaKey属性。

4.3.1 键码

①发生keydown和keyup事件时,event对象的keyCode属性会包含一个代码与键盘上一个特定的键对应。

②keyCode属性值与ASCII码中对应小写字母或数字的编码相同。查表P304

③DOM和IE的event对象都支持keyCode属性。

④以下是无论keydown或keyup事件都会存在的一些特殊情况:

□Firefox和Opera中,按分号键时keyCode值为59,即ASCII中值,但IE和Safari返回186,即键盘键码

□Safari3之前版本中,上下左右,上下翻页返回大于6300的值。

□在Opera9.5之前版本中,会将非数字字母键的keyCode设为ASCII编码。

□在Safari3之前版本,不会因为按下了制表、上档、控制或替代键而触发keydown和keyup事件。

4.3.2 字符编码

□Firefox、Chrome和Safari的event对象支持一个charCode属性,这个属性只有在发生keypress时才有值,为字符的ASCII编码。此时的keyCode值有可能为0,也可能为所按键码。

□IE和Opera则是在keyCode中保存ASCII编码。

□跨浏览器取字符编码

var EventUtil = {

//省略的代码

getCharCode : function(event){

if(typeof event.charCode == "number"){

return event.charCode;

}else{

return event.keyCode;

}

},

};

4.3.4 textInput事件

□当用户在可编辑区域中输入字符时,会触发textInput事件。

◇只有编辑区域才能触发textInput事件。

◇事件只会在用户按下能够输入实际字符的键时才会被触发。

◇事件event对象中包含一个data属性,保存用户输入的字符。

□2008年上半年,仅Safari3和Chrome支持。

4.3.4 设备中的键盘事件。(略)

4.4 HTML事件

①定义:HTML事件指的是那些不一定与用户操作有关的事件。

□load事件:

◇当页面完全加载后window上面触发。

◇当所有框架都加载完毕时在框架集上面触发

◇当图像加载完毕时在<img>元素上面触发

◇当嵌入的内容加载完毕时在<object>元素上面触发

□unload事件:

◇当页面完全卸载后在window上面触发

◇当所有框架都卸载后在框架集上面触发

◇嵌入的内容卸载完毕后在<object>元素上面触发

□abort事件:在用户停止下载过程时,如果嵌入的内容没有加载完,则在<object>元素上面触发。

□error事件:

◇当发生JavaScript错误时在window上面触发

◇当无法加载图像时在<img>元素上面触发

◇当无法加载嵌入内容时在<object>元素上面触发

◇当有一或多个框架无法加载时在框架集上触发

□select事件:当用户选择文本框(<input>或<textarea>)中的一个或多个字符时触发。

□change事件:当文本框(<input>或<textarea>)失去焦点或者取得焦点后其值被改变时触发。

□submit事件:当用户单击提交按钮时在<form>元素上面触发。

□reset事件:当用户单击重置按钮时在<form>元素上触发。

□resize事件:当窗口或框架的大小变化时在window或框架上触发。

□scroll事件:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。<body>元素中包含所加载页面的滚动条。

□focus事件:当页面或元素获得焦点(用户单击、按制表键进入或以其他方式激活元素时)在window及相应元素上面触发。

□blur事件:当页面或元素失去焦点时在window及相应元素上面触发。

②多数HTML事件都与window对象或表单控件相关。要确定浏览器是否支持DOM规定的HTML事件代码如下:

Var isSupport = document.implementation.hasFeature("HTMLEvents","2.0");

4.4.1 加载(load)事件

①有两种定义onload事件处理程序的方式

□EventUtil.addHandler(window,"load",function(event){alert("loaded");});

□为<body>元素添加一个onload特性(不建议)

②图像上面也可以触发load事件,无论是DOM中图像还是HTML中的图像元素。

□可以像使用<img>元素一样使用image对象,不过无法添加到DOM树中。

例子:使用Image对象在客户端预加载图像

EventUtil.addHandler(window,"load",function(){

var image = new Image();

EventUtil.addHandler(image,"load",function(event){

alert("Image loaded!");

});

image.src = "smile.gif";

});

③Firefox、Opera、Chrome和Safari3以上,<script>元素也会触发load事件。与图像不同,只有在设置了src并添加到文档中才会开始下载JS文件。(IE不支持)

④IE和Opera还支持<link>元素上load事件。与<script>节点类似,在未指定href属性并将<link>元素添加到文档之前也不会开始下载样式表。

4.4.2 卸载事件(unload)

□与load事件对应的是unload事件,这个事件在文档完成卸载后触发。

□利用这个事件最多的情况是消除引用,以免内存泄露。

□使用方式一:EventUtil.addHandler(window,"unload",function(event){alert("unload");});

□使用方式二:为<body>元素添加一个特性。

4.4.3 调整大小(resize)事件

①当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件。

②事件在window(窗口)上触发,因此可以通过JavaScript或<body>元素中的onresize特性来指定事件处理程序。

③IE、Safari、Chrome和Opera会再浏览器窗口变化了1像素时触发resize事件,然后随着变化不断重复触发。因此不要加入大量计算代码。

4.4.4 滚动(scroll)事件(略)

4.5 变动事件

①定义:DOM2级的变动(mutation)事件能在DOM中的某一部分发生变化是给出提示。

②DOM2级定义了如下变动事件:

□DOMSubtreeModified:在DOM结构中发生任何变化时触发。

□DOMNodeInserted:在一个节点作为子节点被插入到另一个节点中时触发。

□DOMNodeRemoved:在节点从其父节点中移除时触发。

□DOMNodeInsertIntoDocument:在一个节点被直接插入文档或通过子树间接插入文档之后触发。在DOMNodeInsert之后触发。

□DOMNodeRemovedFromDocument:在一个节点被直接从文档中移除或通过子树间接从文档中移除之前触发。这个事件在DOMNodeRemove之后触发。

□DOMAttriModified:在特性被修改之后触发。

□DOMCharacterDataModified:在文本节点的值发生变化时触发。

③检测浏览器是否支持变动事件:

Var isSupport = document.implementation.hasFeature("MutationEvents","2.0");

□实际上,即使返回true也不代表浏览器支持。P312

4.6 专有事件

4.6.1 上下文菜单(contextmenu事件)

①contextmenu事件:用以表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。

②contextmenu事件是冒泡的,因此可以为document指定一个事件处理程序,用以处理页面中发生的所有类似事件。

③事件的目标是发生用户操作的元素,在所有浏览器中都可以取消这个事件:兼容的DOM的:event.preventDefault(),在IE中,event.returnValue设为false。

④contextmenu事件属于鼠标事件,其对象中包含与光标位置有关的所有属性。

⑤通常使用contextmenu事件来显示自定义的上下文菜单,而使用onclick事件处理程序隐藏该菜单。

EventUtil.addHandler(window, "load", function(event){

var div = document.getElementById("myDiv");

EventUtil.addHandler(div, "contextmenu", function(event){

event = EventUtil.getEvent(event);

EventUtil.preventDefault(event);

var menu = document.getElementById("myMenu");

menu.Style.left = event.clientX + "px";

menu.style.top = event.clientY + "px";

menu.style.visibility = "visible";

});

EventUtil.addHandler(document, "click", fucntion(event){

document.getElementById("myMenu").style.visibility = "hidden";

});

});

4.6.2 卸载前(beforeunload)事件

①beforeunload事件:会在浏览器卸载页面之前触发,可以通过它来取消卸载并继续使用原有页面。

②IE和Firefox支持,会弹出询问用户是否离开对话框。Safari和Chrome支持,但不妨碍事件继续,没有显示对话框。Opera9.5之前不支持。

EventUtil.addHandler(window, "beforeunload", function(event){

event = EventUtil.getEvent(event);

event.returnValue = "I'm really miss you";

});

4.6.3 鼠标滚轮(mousewheel)和DOMMouseScroll事件

①在鼠标滚轮滚动时触发

②mousewheel事件为IE、Opera、Chrome、Safari支持,事件对应event中包含鼠标事件的所有标准信息外,还包含一个特殊的wheelEdlta属性。是120的倍数,向后滚-120倍数。

③9.5之前的版本中,wheelDelta值正负号颠倒。

④Firefox支持DOMMouseScroll事件,滚轮信息存在detail属性中,前滚为-3倍数,后滚为3倍数。

兼容代码:

var EventUtil = {

getWheelDlta : function(event){

if(event.wheelDelta){

Return (client.egine.opera && client.egine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta);

}else{

return -event.detail * 40;

}

},

};

4.6.4 DOMContentLoaded事件

①DOMContentLoaded事件在形成完整的DOM树之后就触发,不会理图像、JavaScript文件、CSS文件或其他资源是否已经下载完毕。

EventUtil.addHandler(document,"DOMContentLoaded", function(event){

alert("Content loaded");

});

②对于不支持DOMContentLoad的浏览器,建议在页面加载期间设置一时间为0毫秒的超时调用。

setTimeout(function(){

//在此添加事件处理程序

}, 0);

4.6.5 就绪状态变化(readystatechange)事件

①IE为DOM文档中某些部分提供了readystatechange事件:提供与文档或元素的加载状态有关的信息。

②支持readystatechange事件的每个对象都有一个readyState属性

□uninitialized(未初始化):对象存在但尚未初始化;

□loading(正在加载):对象正在加载数据;

□loaded(加载完毕):对象加载数据完成;

□interactive(交互):可以操作对象了,但还没完全加载

□complete(完成):对象已经加载完毕。

③在IE中使用readystatechange近似模拟DOMContentLoaded事件。

EventUtil.addHandler(document, "readystatechange", function(event){

if(document.readyState == "interactive" || document.readyState == "complete"){

eventUtil.removeHandler(document, "readystatechange", arguments.callee);

alert("Content load");

}

});

4.6.6 页面显示(pageshow)和页面隐藏(pagehide)事件

①Firefox和Opera有一个特性,名叫“往返缓存”(back-forward cache,或bfcache)。可以在用户使用浏览器的“后退”和“前景”按钮时加快页面的转换速度。

②pageshow事件:在页面显示时触发,无论页面是否来自bfcache。在重新加载的页面中,pageshow会在load事件触发后触发,而对于bfcache中的页面,pageshow会在页面状态完全恢复的那一刻触发。

□pageshow事件的event对象包含一个名为persisted的布尔值属性。如果页面保存在bfcache中,则只为true,反之则反。

③pagehide事件:事件会在浏览器卸载页面的时候触发,而且是在unload事件之前触发。

□第一次触发pagehide时,persisted就会变成true(除非页面不存在bfcache中)

④指定了onunload事件处理程序的页面自动排除在bfcache之外。

4.6.7 移动Safari支持的事件

①方向变化(ovientationchange)事件

②触摸事件

③手势事件

5.内存和性能

①在JavaScript中,添加到页面上的事件处理程序数量直接关系到页面整体运行性能。

□每个函数是对象,都会占用内存;内存中的对象越多,性能越差。

□必须事先指定所有事件处理程序而导致的DOM访问次数,会延迟整个页面的交互就绪时间。

5.1 事件委托

①事件委托:解决“事件处理程序过多”问题。

原理:利用事件冒泡,只指定一个事件处理程序,来管理某一类型的所有事件。

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 title";

break;

case "goSomewhere" : location.href = "http://www.wrox.com";

break;

case "sayHi" : alert("hi");

break;

}

},false);

②如果可行的话,也可以考虑为document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型事件。

优点:

□document对象很快就可以访问,而且可以在页面生命周期的任何时间点上为它添加事件处理程序(无需等待DOMContentLoad或Load事件)。即只要可单击的元素呈现在页面上,就可以立即具备适当的功能。

□在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少。

□整个页面占用的内存空间更少,能提升整体性能。

③使用范围

□适合采用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress。

□虽然mouseover和mouseout事件也冒泡,但要适当处理他们并不容易,而且经常需要计算元素的位置(因当鼠标从一个元素移动到其子节点时或者鼠标移出该元素时,就会触发mouseout事件)

5.2 移除事件处理程序

①浏览器页面代码与JavaScript代码之间建立的链接越多,执行越慢。可用事件委托技术,限制建立的链接数量。在不需要的时候移除事件处理程序。

②内存中留有过时不用的“空事件处理程序”(dangling event handler)也是造成web应用程序内存与性能问题的主要原因。

1)第一种情况:从文档中移除带有事件处理程序的元素时。如removeChild()和replaceChild()方法,更多是发生在使用innerHTML替换页面中某一部分的时候。

□手动解除这些“空事件处理程序”,释放内存。

var btn = document.getElementById("myBtn");

btn.onclick = function(){

//先执行某些操作

btn.onclick = null; //移除事件处理程序

document.getElementById("myDiv").innerHTML = "prossing";

};

2)另一种情况:卸载页面的时候。若没有清理干净会滞留内存。

□在卸载之前,先通过onunload事件处理程序移除所有事件处理程序。

6.模拟事件

可以使用JavaScript在任意时刻来触发特定的事件,而此时的事件就如同浏览器创建的事件一样。即该冒泡会冒泡,且照样导致浏览器执行已经制定处理它们的事件处理程序。

6.1 DOM中的事件模拟

■可以在docuemnt对象上使用createEvent()方法创建对象。这个方法接受一个参数,即表示创建事件类型的字符串,如下:

□UIEvents:一般化的UI事件。鼠标事件和键盘事件都继承自UI事件。

□MouseEvents:一般化的鼠标事件。

□MutationEvents:一般化的DOM变动事件。

□HTMLEvents:一般化的HTML事件。

■各种类型创建的event对象,都有一个特殊的方法,为它传入适当的数据可以初始化该event对象。

■触发事件:调用dispatchEvent()方法,传入要触发事件的event对象。

6.1.1 模拟鼠标事件

①创建新的鼠标事件对象并为其指定必要的信息,就可以模拟鼠标事件。创建鼠标事件的对象方法是createEvent(),传入字符串"MouseEvents"。返回的对象有一个名为initMouseEvent()方法,用于指定与该鼠标事件有关的信息。

initMouseEvent()方法15个参数:

□type(字符串):表示要触发的事件类型名,例如“click”。

□bubbles(布尔值):表示事件是否应该冒泡。

□cancelable(布尔值):表示事件是否可以取消。

□view(AbstractView):与事件关联的视图。几乎总设为document.defaultView

□detail(整数):与事件有关的详细信息。通常设为0

□screenX(整数):事件相对于屏幕的X坐标。

□screenY

□clientX(整数):事件相对于视口的X坐标。

□clientY

□ctrlKey(布尔值):表示是否按下了Ctrl键。默认为false

□altKeyu

□shiftKey

□metaKey

□button(整数):表示按下了哪一个鼠标键。默认0

□relatedTarget(对象):表示与事件相关的对象。这参数只在模拟mouseover或mouseout时使用。

var btn = document.getElementById("myBtn");

//创建事件对象

var event = document.creeateEvent("MouseEvents");

//初始化事件对象

event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);

//触发事件

btn.dispatchEvent(event);

6.1.2 模拟键盘事件

①Firefox中调用createEvent()并传入"keyEvents"就可以创建一个键盘事件。返回的事件对象会包含一个initKeyEvent()方法。

②在其他浏览器中,则需要创建一个通用的事件,然后再向事件对象添加键盘事件特有的信息。

var textbox = document.getElementById("myTextbox");

//创建事件对象

var event = document.createEvent("Events");

//初始化事件对象

event.initEvent(type,bubbles,cancelable);

event.view = document.defaultView;

event.altKey = false;

event.ctrlKey = false;

event.shiftKey = false;

event.metaKey = false;

event.keyCode = false;

event.charCode = false;

//触发事件

textbox.dispatchEvent(event);

6.1.3 模拟其他事件(变动事件和HTML事件)

6.2 IE中的事件模拟

IE中思路与DOM相同,其中的区别:

□IE使用document.createEventObject()方法创建对象。此法不接受参数,仅返回一个通用event对象。必须手工为此对象添加属性。

□触发事件在目标上调用fireEvent()方法。两个参数:事件处理程序名和event对象。在调用fireEvent()方法时,会自动为event对象添加srcElement和type属性。

第十三章 表单脚本

1.表单

①JavaScript中表单<form>对应HTMLFormElement类型。HTMLFormElement继承了HTMLElement,因而与其它HTML元素具有相同的默认属性。也有其独有属性和方法:

□acceptCharset:服务器能够处理的字符集;等价HTML中的accept-charset特性。

□action:接受请求的URL;等于HTML中的action特性。

□elements:表单中所有控件的集合(HTMLCollection)。

□enctype:请求的编码类型;等价于HTML中的enctype特性。

□length:表单中控件的数量。

□method:要发送的HTTP请求类型,通常是“get”或“post”;等价于HTML的method特性。

□name:表单名称;等价于HTML的name特性。

□reset():将所有表单域重置为默认值。

□submit():提交表单。

□target:用于发送请求和接收响应的窗口名称;等价于HTML的target特性。

②获取表单

var form = document.getElementById("form1");

var firstForm = document.forms[0];

var myForm = document.forms["form2"];

1.1 提交表单

①提交表单的三种方法:

□通用按钮:<input type="submit" value="submit Form"/>

□自定义按钮:<button type="submit">Submit Form</button>

□图像按钮:<input type="image" src="graphic.gif"/>

②在提交表单时,浏览器会将请求发送给服务器之前触发submit事件。这样就有机会验证表单数据,并据以决定是否允许表单提交。

var form = document.getElementById("myForm");

EventUtil.addHandler(form,"submit",function(event){

//取得事件对象

event = EventUtil.getEvent(event);

//阻止默认事件

EventUtil.preventDefault(event);

});

③提交表单的最大问题是重复提交表单。解决办法:在第一次提交表单后就禁用提交按钮。

④在JavaScript中,可以以编程方式提交表单。

var form = document.getElementById("myForm");

//提交表单

form.submit();  //此方法提交表单不会触发submit事件

1.2 重置表单

①重置表单方式

□通用按钮:<input type="reset" value="Reset Form"/>

□自定义按钮:<button type="reset">Rest Form</button>

②用户单击重置按钮重置表单是,会触发reset事件,利用这个机会可在必要时取消重置操作。

③可用JavaScript重置表单:form.reset();与调用submit()方法不同,调用reset()方法会想单击重置按钮一样触发reset事件。

1.3表单字段

①访问表单字段方法:

□使用原生DOM方法。

□通过表单的elements属性,该属性是表单中所有元素的集合。

◇表单字段按标记顺序保存在elements中。

◇可以按照位置和name特性访问它们。

◇如果多个表单控件都使用一个name特性,则返回一个NodeList。

var form = document.getElementById("form1");

//取得表单中的第一个字段

var field1 = form.elements[0];

//取得名为textbox1的字段

var field2 = form.elements["textbox1"];

//取得表单中包含的字段的数量

var fieldCount = form.elements.length;

1.3.1 共有的表单字段属性

①表单字段共有的属性和方法如下:

□disabled:布尔值,表示当前字段是否被禁用。

□form:指向当前字段所属表单的指针,只读。

□name:当前字段的名称。

□readOnly:布尔值,表示当前字段是否只读。

□tabIndex:表示当前字段的切换(tab)序号。

□type:当前字段的类型,如:“checkbox”、“radio”等等。

□value:当前字段将被提交给服务器的值。

②不能用click事件处理submit事件,因不知道在submit前触发click还是submit后触发click。

1.3.2 共有的表单字段方法

□focus()方法:将浏览器的焦点设置到表单字段。

□blur()方法:从元素中移走焦点。

1.3.3 共有的表单字段事件

除了支持鼠标事件、键盘、变动和HTML事件外,所以表单字段支持下列3个事件:

□blur:当前字段失去焦点时触发。

□change:对于<input>和<textarea>元素,他们失去焦点切value值改变时触发;对于<select>元素,在其选项改变时触发。

□focus:当前字段获得焦点时触发。

2.文本框脚本

①在HTML中两种方法表现文本框

□使用<input>元素的单行文本框

◇显示25个字符,输入不超过50个字符。size="25" maxlength="50"

□<textarea>元素,多行文本框

◇25行,5列。rows="25" cols="5"

②设置文本框的值

□使用value属性读取或设置文本框的值,不建议使用DOM方法。

□使用DOM方法:setAttribute()设置value特性或修改<textarea>元素第一个子节点这样对value值的修改不一定反映到DOM中。

2.1 选择文本

①<input>单行文本和<textarea>多行文本都支持select()方法,这个方法用于选择文本框中所有文本。

EventUtil.addHandler(textbox, "focus", function(){

event = EventUtil.getEvent(event);

var target = EventUtil.getTarget(event);

target.select();

});

2.1.1 选择(select)事件

select事件触发的情况复杂,要想编写跨浏览器代码,必须手工取得对事件目标的引用:

var textbox = document.forms[0].elements["textbox1"];

EventUtil.addHandler(textbox,"select",function(event){

var target = document.forms[0].elements["textbox1"];

alert("text selected");

});

2.1.2 取得选择的文本

□文本框的两个属性:selectionStart和selectionEnd。基于0的数值,表示所选文本范围。FF、Safari、Chrome、Opera支持。

□IE有一个document.select对象,其中保存用户整个文档范围内选择的文本信息。与select事件一起,可假定为文本内容。取值前须创建一个范围。

function getSelectedText(textbox){

if(document.selection){

return document.selection.createRange().text;

}else{

return textbox.value.substring(textbox.selectionStart,textbox.selectionEnd);

}

}

2.1.3 选择部分文本

□setSelectionRange()方法。接受两个参数:要选择的第一个字符的索引和要选择的最后一个字符之后的字符索引。FF、Chrome、Safari、Opera支持。

□IE为文本框提供了createTextRange()方法,要选择文本框中的部分文本,必须首先使用这个方法创建一个范围,并将其放在恰当位置上。再使用moveStart()和moveEnd()这两个方法将范围移动位置。

□兼容代码

function selectText(textbox,strtIndex,stopIndex){

if(textbox.setSelectionRange){

textbox.setSelectionRange(startIndex,stopIndex);

}else if(textbox.createTextRange){

var range = textbox.createTextRange();

range.collapse(true);

range.moveStart("character",startIndex);

range.moveEnd("charater",stopIndex-startIndex);

range.select();

}

Textbox.focus();

}

2.2.1 过滤输入

例为屏蔽非数字值,而不屏蔽ctrl组合键

EventUtil.addHandler(textbox,"keypress",function(event){

event = EventUtil.getEvent(event);

var target = EventUtil.getTarget(event);

var charCode = EventUtil.getCharcode(event);

if(!/\d/.test(String.fromCharCode(charCode))&&charCode>9&&!event.ctrlkey){

EventUtil.preventDefault(event);

}

});

2.2.2 操作剪贴板

①6个剪贴板事件:

□beforecopy:在发生复制操作前触发;

□copy:在发生复制操作时触发

□beforecut:在发生剪切操作时触发。

□cut:在发生剪切操作时触发

□beforepaste:在发生粘贴操作前触发

□paste:在发生粘贴操作时触发

②访问剪贴板中数据,可使用clipboaredData对象,有3个方法:

getDate()、setDate()和clearData()

兼容方法:

var EventUtil = {

getClipboardText : function(event){

var clipboardData = (event.clipboardData || window.clipboardData);

return clipboardData.getData("text");

},

setClipboardText : function(event, value){

if(event.clipboardData){

return event.clipboardData.setData("text/pain",value);

}else if(window.clipboardData){

return window.clipboardData.setData("text",value);

}

},

};

3.选择框脚本

①选择框是通过<select>和<option>元素创建的。HTMLSelectElement类型还提供了下列属性和方法:

□add(newOption, relOption):向空间按中插入新元素,其位置在指定项relOption之前

□multiple:布尔值,表示是否允许多项选择;等价于HTML中的multiple特性。

□options:控件中所有<option>元素的HTMLCOllection。

□remove(index):移除给定位置选项。

□selectedIndex:基于0的选中项的索引,如果没有选中项,则值为-1.对于支持多选的控件,只保存选项中第一项的索引。

□size:选择框中可见的行数;等价于HTML中的size特性

□type:"select-one"或"select-multiple",取决于HTML代码中有没multiple特性。

□value:取决于当前选中项。

②DOM中,每个<option>元素都有一个HTMLOptionElement对象表示。其对象添加以下属性:

□index:当前选项在options集合中的索引

□label:当前选项的标签;等价于HTML中的label特性

□selected:布尔值,表示当前选项是否被选中。设true可选中。

□text:选项的文本

□value:选项的值(等价于HTML中的value特性)

3.1 选择选项

□对于只允许一项的选择框,使用选择框的selectedIndex属性。

var selectedOption = selectbox.options[selectbox.selectedIndex];

□对于多选的选择框,遍历检查其selected属性。

3.2 添加选项

①DOM方法:

var newOption = document.createElement("option");

newOption.appendChild(document.createTextNode("Option text");

newOption.setAttribute("value","Option value");

selectbox.appendChild(newOption);

②Option构造函数,接受两个参数:文本(text)和值(value);第二个可选。(IE不可用)

Var newOption = new Option("Option text","Option value");

Selectbox.appendChild(newOption);  //在IE中有问题

③使用选择框的add()方法,接受两个参数:要添加的新选项和将位于新选项之后的选项。

Var newOption = new Option("Option text", "Option value");

Selectbox.add(newOption, undefined);  //最佳方案

3.3 移除选项

①可使用DOM的removeChild()方法,为其传入要移除的项。

selectbox.removeChild(selectbox.option[0]);

②使用选择框的remove()方法。接受一个参数,即要移除项的索引。

selectbox.remove(0);  //移除第一个选项

③将相应项设为null

3.4 移动和重排选项

①使用appendChild()方法,讲一个选择框中选项移到另一个选择框。

②使用insertBefore()实现重排

4.表单序列化

①浏览器发数据至服务器方式:

□对表单字段的名称和值进行URL编码,使用和号(&)分隔。

□不发送禁用的表单字段

□只发送勾选的复选框和单选按钮

□不发送type为“reset”和“button”的按钮。

□多选选择框中的每个选中值单独一个条目。

□在单击提交按钮表单的情况下,也会发送提交按钮;否则,不发送提交按钮。也包括type为"image"的<input>元素。

□<select>元素的值,就是选中<option>元素的value特性的值。如果<option>元素没有value特性,则是<option> 元素的文本值。

②表单序列化代码:

function serialize(form){

var parts = new Array();

var field = null;

for(var i = 0, len = form.elements.length; i < len; i++){

field = form.elements[i];

switch(field.type){

case "select-one" :

case "select-multiple" :

for(var j=0, optLen = field.options.length; j<optLen; j++){

var option = field.options[j];

if(option.selected){

var optValue=" ";

if(potion.hasAttribue){

optValue = (option.hasAttribute("value") ? Option.value : option.text);

}else{

optValue = (option.attributes["value"].specified ? Option.value : option.text);

}

parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(optValue));

}

case undefined :  //字段集

case "file" :  //文件输入

case "submit" :  //提交按钮

case "reset" : //自定义按钮

Break;

case "radio" :  //单选按钮

case "checkbox" : //复选框

If(!field.checked){break;}

default : 

parts.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(field.name));

return parts.join("&");

}

}

}

5.富文本编辑(略)