第13章 事件
1、事件流
事件流描述的是从页面中接收事件的顺序。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。
(1)事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。以下面为例,如果你单击了页面中的<div>元素,那么这个click事件会按照如下顺序传播:
(2)事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预定目标之前捕获它。以下面为例,如果你单击了页面中的<div>元素,那么这个click事件会按照如下顺序传播:
(3)DOM事件流
“DOM级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
首先发生的是事件捕获,为截取事件提供了机会。然后是实际的目标接收到事件。最后一阶段是冒泡阶段,在这个阶段对事件作出响应。
2、事件处理程序
事件就是用户或浏览器自身执行的某种操作,而响应某个事件的函数就叫做事件处理程序(或事件监听器)。事件处理程序的名字以“on”开头。
(1)HTML事件处理程序
某个元素支持的每种事件,都可以使用一个与相应事件处理程序同名的HTML特性来指定。如向 button 元素分配 onclick 事件:
<button onclick="displayDate()">点击这里</button>
缺点:存在一个时差问题; HTML和JavaScript代码紧密耦合。
(2)DOM0级事件处理程序
通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。如向 button 元素分配 onclick 事件:
<script>
document.getElementById("myBtn").onclick=function(){displayDate()};
</script>
删除通过DOM0级方法指定的事件处理程序,只要像下面这样将事件处理程序属性的值设置为null即可:
btn.onclick = null;
优点:简单;跨浏览器
(3)DOM2级事件处理程序
“DOM2级事件“定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()和removeEventListener()。
所有DOM节点中都包含这两个方法,并且它们都接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值(如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序)。
要在按钮上为click事件添加事件处理程序,可以使用下列代码:
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 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); //有效
大多数情况下,都是将事件处理程序添加到事件流的冒泡阶段,这样可以最大限度地兼容各种浏览器。
(4)IE事件处理程序
IE实现了与DOM中类似的方法:attachEvent()和detachEvent()。这两个方法接受相同的两个参数:事件处理程序名称与实践处理程序函数。IE只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。
attachEvent()方法也可以用来为一个元素添加多个事件处理程序,如:
不过,与DOM方法不同的是,这些事件处理程序不是以添加它们的顺序执行,而是以相反的顺序被触发。
(5)跨浏览器的事件处理程序
视情况分别使用DOM0级方法、DOM2级方法或IE方法来添加事件。
然后像下面这样使用EvenUtil对象:
3、事件对象
在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型,以及其他与特定时间相关的信息。
(1)DOM中的事件对象
无论指定时间处理程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。如:
var btn=document.getElementById("myBtn");
btn.onclick=function(event){
alert(event.type); //"click"
};
btn.addEventListener("click",function(event){
alert(event.type); //"click"
},false);
所有事件都会有下表列出的成员:
要阻止特定事件的默认行为,可以使用preventDefault()方法。例如,链接的默认行为就是在被单击时会导航到其href特性指定的URL。想阻止这一默认行为,那么通过链接的onclick事件处理程序可以取消它,如:
var link=document.getElementById("myLink");
link.onclick=function(event){
event.preventDefault();
}
只有cancelable属性设置为true的事件,才可以使用preventDefault()来取消其默认行为。
另外stopPropagation()方法用于立即停止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。
(2)IE中的事件对象
与访问DOM中的event对象不同,要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。
在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。例:
如果事件处理程序是使用attachEvent()添加的,那么就会有一个event对象作为参数被传入事件处理程序函数中,例:
如果是通过HTML特性指定的事件处理程序,那么还可以通过一个名叫event的变量来访问event对象,例:
4、事件类型
(1)UI事件
load:当页面完全加载后在window上面触发,当所有框架都加载完毕时在框架集上面触发。
unload:当页面完全卸载后在window上面触发,当所有框架都卸载后在框架集三面触发,或者当嵌入的内容卸载完毕后在<object>元素上面触发。
abort:在用户停止下载过程时,如果嵌入的内容没有加载完,这在<object>元素上面触发。
error:
select:当用户选择文本框(<input>或<texterea>)中的一或多个字符时触发。
resize:当窗口或框架的大小变化时再window或框架上面触发。
scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。
(2)焦点事件
blur:在元素失去焦点时触发,所有浏览器都支持它。
focus:在元素获得焦点时触发,所有浏览器都支持它。
focusin:在元素获得焦点时触发。
focusout:在元素失去焦点时触发。
(3)鼠标与滚轮事件
鼠标事件:click、dbclick、mousedown、mouseenter、mouseleave、mousemove、mouseout、mouseover、mouseup。
l 客户区坐标位置:
鼠标事件都是在浏览器视口中的特定位置上发生的。这个位置信息保存在事件对象的clientX和clientY属性中。
l 页面坐标位置
页面坐标通过事件对象的pageX和pageY属性,能告诉你事件是在页面中的什么位置发生
l 屏幕坐标位置
通过screenX和screenY属性句可以确定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。
l 修改键
DOM规定了4个属性表示修改键的状态:shiftKey、ctrlKey、altKey和metaKey。这些属性中包含的都是布尔值,如果相应的键被按下了,则值为true,否则值为false。
l 鼠标按钮
对于mousedown和mouseup事件来说,在其event对象存在一个button属性,表示按下或释放的按钮。
DOM的button属性可能有如下3个值:0表示主鼠标按钮,1表示中间的鼠标按钮(鼠标滚轮按钮),2表示次鼠标按钮。在常规的设置中,主鼠标按钮据说鼠标左键,而次鼠标按钮就是鼠标右键。
l 更多的事件信息
“DOM2级事件”规范在event对象中还提供了detail属性,用于给出有关事件的更多信息。
l 鼠标滚轮事件
当用户通过鼠标滚轮与页面交互、在垂直方向上滚动页面时,就会触发mousewheel事件。
(4)键盘与文本事件
有3个键盘事件:keydown、keypress、keyup。
当用户在可编辑区域中输入字符时,就会触发事件textInput。
(5)复合事件
是DOM3级事件中新添加的一类事件,用于处理IME(输入法编辑器)的输入序列。
(6)变动事件
DOM2级的变动事件能在DOM中的某一部分发生变化时给出提示。如,DOM结构发生变化、插入节点、移除节点、特性修改、文本节点的值发生变化等。
(7)专有事件
contextmenu事件,用以表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单。
beforeunload(卸载前)事件,是为了让开发人员有可能在页面卸载前阻止这一操作。
DOMContentLoaded事件在行程完整的DOM树之后就会触发,不理会图像、JavaScript文件、CSS文件或者其他资源是否已经下载完毕。
IE为DOM文档中的某些部分提供了readystatechange(就绪状态变化)事件。这个事件的目的是提供与文档或元素的加载状态有关的信息。
HTML5新增了haschange事件,以便在URL的参数列表(及URL中“#”号后面的所有字符串)发生变化时通知开发人员。
(8)设备事件
可以让开发人员确定用户在怎么使用设备。
(9)触摸与手势事件
具体来说,有以下几个触摸事件:touchstart、touchmove、touchend、touchcancel。
当两个手指触摸屏幕时就会产生手势,手势通常会改变显示项的大小,或者旋转显示项。有三个手势事件:gesturestart、gesturechange、gestureend。
5、内存和性能
(1)事件委托
对“事件处理程序过多”的问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
使用事件委托,只需在DOM树中尽量最高的层次上添加一个事件处理程序。如:
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say Hi </li>
</ul>
包含3个被单击后会执行操作的列表项,可以这样处理
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.tile="i changed the document's title";break;
case "goSomewhere":
location.href="http://www.wrox.com";break;
case "sayHi":
alert("hi");break;
}
}
6、模拟事件
(1)DOM中的事件模拟
可以在document对象上使用createEvent()方法创建event对象。这个方法接受一个参数,即表示要创建的事件类型的字符串。可以是下列几个字符串之一:
UIEvents:一般化的UI事件;
MouseEvents:一般化的鼠标事件;
MutationEvents:一般化的DOM变动事件;
HTMLEvents:一般化的HTML事件。
在Firefox中,调用createEvent()并传入“KeyEvents”就可以创建一个键盘事件。
DOM3级还定义了“自定义事件”。可以调用createEvent(“CustomEvents”)创建。
(2)IE中的事件模拟
在IE8及之前版本中模拟事件与在DOM中模拟事件的思路相似:先创建event对象,然后为其指定相应的信息,然后再使用该对象来触发事件。
调用document.createEventObject()方法可以在IE中创建event对象。这个方法不接受参数,结果返回一个通用的event对象。如:
第14章 表单脚本
1、表单
在JavaScript中,表单对应的是HTMLFormElement类型。
取得<form>元素应用的方式有好几种:
将它看成与其他元素一样,使用getElementById()方法找到它;
其次,通过document.forms可以取得页面中所有的表单;
(1)提交表单
使用<input>和<button>都可以定义提交按钮,只要将其type特性的值设置为"submit"即可,而图像按钮则是通过将<input>的type特性设置为"image"来定义。
<input type="submit" vlaue="Submit Form"> --通用提交按钮
<button type="submit">Submit Form</button> --自定义提交按键
<input type="image" src="graphic.gif"/> --图像按钮
只要表单中存在上面的任何一种按钮,按回车就可以提交该表单。以这种方式提交表单时,浏览器会在将请求发送给服务器之前触发submit事件。
在JavaScript中,以编程方式调用submit()方法也可以提交表单。而且,这种方式无需表单包含提交按钮,任何时候都可以正常提交表单。
var from=document.getElementById("myForm");
//提交表单
form.submit();
在以调用submit()方法的形式提交表单时,不会觖submit事件,因此要记得在调用此方法之前先验证表单数据。
(2)重置表单
使用type特性值为"reset"的<input>或<button>都可以创建重置按钮。如下面所示:
<input type="reset" vlaue="Reset Form"> --通用提交按钮
<button type="reset">Reset Form</button> --自定义提交按键
用户单击重置按钮重置表单时,会触发reset事件。
也可以通过JavaScript来重置表单,如:
var from=document.getElementById("myForm");
//重置表单
form.reset();
(3)表单字段
可以像访问页面中的其他元素一样,使用原生DOM方法访问表单元素。此外,每个表单元素都有element属性,该属性是表单中所有元素的集合。
这个elements集合是一个有序列表,其中包含着表单中的所有字段,例如<input>、<textarea>、<button>和<fieldset>。每个表单字段在elements集合中的顺序,与它们出现在标记中的顺序,可以按照位置和name特性来访问它们。如:
var form=document.getElementById("form1");
//取得表单中的第g个字段
var field1=from.elements[0];
//取得名为"textbox1"的字段
var field2=form.elements["textbox1"];
//取得表单中包含的字段的数量
var fieldCount=form.elements.length;
共有的表单字段属性如下:
disabled:布尔值,表示当前字段是否被禁用.
form:指向当前字段所属表单的指针;只读
name:当前字段的名称
readOnly:布尔值,表示当前字段是否可读
tabIndex:表示当前字段的切换(tab)序号.
type:当前字段的类型,如"checkbox"
value:提交给服务器的值.
每个表单字段都有两个方法:focus()和blur()。其中,focus()方法用于将浏览器的焦点设置到表单字段,即激活表单字段,使其可以响应键盘事件。blur()方法,它的作用是从元素中移走焦点。
除了支持鼠标、键盘、更改和HTML事件外,所有表单字段都支持以下3个事件:
blur:当前字段失去焦点时触发.
change:对于<input>和<textarea>元素,在它们失去焦点且value值改变时触发;对于<select>元素,在其选项改变时触发.
focus:当前字段获得焦点时触发
2、文本框脚本
在HTML中,有两种方式来表现文本框,一种是使用<input>元素的单行文本框,一种是使用<textarea>的多行文本框。
上述两种文本框都支持select()方法,这个方法用于选择文本框中的所有文本。
与select()方法对应的,是一个select事件.在选择了文本框中的文本时,就会触发select事件。
由于文本框在默认情况下没有提供多少验证数据的手段,因此必须使用JavaScript来完成此类过滤输入的操作。
除了opera之外的所有浏览器都支持剪贴板事件,包括copy、cut和paste。
自动切换焦点:用户填写完当前字段后,自动将焦点切换到下一个字段。“自动切换焦点”的功能,可以通过下来代码实现:
( function(){
function tabForward(event){
event=EventUtil.getEvent(event);
var target=EventUtil.getTarge(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("txtTel1");
var textbox2=document.getElementById("txtTel2");
var textbox3=document.getElementById("txtTel3");
EventUtil.addHandler(textbox1,"keyup",tabForward);
EventUtil.addHandler(textbox2,"keyup",tabForward);
EventUtil.addHandler(textbox3,"keyup",tabForward);
}
)();
3、选择框脚本
选择框是通过<select>和<option>元素创建的,HTMLSelectElement类型提供了额外的属性和方法:
add(newOption, relOption):向控件中插入新<option>元素,其位置在相关项(relOption)之前.
multiple:布尔值,表示是否允许多项选择,等价于HTML中的multiple特性.
options:控件中所有<option>元素的HTMLCollection
remove(index):移除给定位置的选项.
selectedIndex:基于0的选中项的索引,如果没有选中项,则值为-1.对于多选的空间,只保存选中项中第一项的索引.
size:选择框中可见的行数;等价于HTML中的size特性.
在DOM中,每个<option>元素都有一个HTMLOptionElement对象表示.为了便于访问数据,HTMLOptionElement对象添加了下列属性:
index:当前选项在options集合中的索引
label:当前选项的标签;等价于HTML中的label特性.
selected:布尔值,表示当前选项是否被选中.
text:选项的文本
value:选项的值
(1)可以使用JavaScript动态创建选项,并为它们添加到选择框中。添加选项的方式有很多‘
第一种方式是使用DOM方法,如:
var newOption = document.createElement("option");
newOption.appendChild(document.createTextNode(textTextbox.value));
newOption.setAttribute("value", valueTextbox.value);
selectbox.appendChild(newOption);
第二种方式是使用Option构造函数来创建新选项,如:
var newOption = new Option("Option
text", "Option value");
selectbox.appendChild(newOption); //在IE8及之前版本有bug
第三种添加新选项的方式是使用选择框的add()方法,如:
var newOption = new Option("Option
text", "Option value");
selectbox.add(newOption, undefined);
(2)移除选项的方式也有很多种
首先,可以使用DOM的removeChild()方法,如:
selectbox.removeChild(selectbox.option[0]);
其次,可以使用选择框的remove()方法,如:
selectbox.remove(0);
最后,就是将相应选项设为null
selectbox.option[0] = null;
移除一个选项后后续选项会自动向上移一个位置。
(3)移动和重排选项
如果为appendChild()方法传入一个文档中已有的元素,那么就会先从该元素的父元素节点中移除它,再把它添加到指定的位置。
要将选择框中的某一项移动到特定位置,最合适的DOM方法是insertBefore()。
4、富文本编辑
富文本编辑WYSIWYG(What
You See Is What You Get):在网页中编辑富文本内容。本质技术在页面中嵌入一个包含空HTML页面的iframe,通过设置designMode属性,这个空白的HTML页面可以被编辑,而编辑的对象则是该页面<body>元素的HTML代码。designMode属性有两个可能的值:"off"(默认值)和"on",设置"on"时候可以进行编辑。