事件
Javascript与HTML之间的交互是通过事件来实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。
事件流
事件流描述的是从页面中接收事件的顺序。IE8及之前版本的事件流仅是事件冒泡流。
事件冒泡
IE的事件流叫做事件冒泡,即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。
打个比方说:你在地方法院要上诉一件案子,如果地方没有处理此类案件的法院,地方相关部门会帮你继续往上级法院上诉,比如从市级到省级,
直至到中央法院,最终使你的案件得以处理。
最常应用于事件委托技术中。
事件捕获
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于事件到达预定目标之前捕获它。
DOM事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。
事件处理程序
事件是指用户或浏览器自身执行的某种动作,如click;
事件处理程序是指响应某个事件的函数,也叫做事件侦听器,如onclick()。
事件处理程序有以下五类:
1)HTML事件处理程序
直接在HTML中调用javascript代码或函数;
有点:简单,方便;
缺点:javascript和html紧密耦合,不方便维护和修改。
2)DOM0级事件处理程序
通过javascript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。
3)DOM2级事件处理程序
“DOM2级事件”定义了两个方法,用于处理和删除事件处理程序的操作:addEventListener()和removeEventListener()
所有DOM节点都包含这两个方法,它们都接收3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。
最后这个布尔值参数如果是true,表示在捕获阶段调用事件处理程序;如果是false,表示在冒泡阶段调用事件处理程序。
使用DOM2级方法主要好处是可以添加多个事件处理程序。
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。
这也意味着通过addEventListener()添加的匿名函数将无法移除。
4)IE事件处理程序
IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。
这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数。
在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的作用域。
在使用DOM0级方法的情况下,事件处理程序会在其所属元素的作用域内运行;
在使用attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此this等于window。
使用attachEvent()添加的事件可以通过detachEvent()来移除,但必须提供相同的参数,同样地,无法将匿名函数移除。
支持IE事件处理程序的浏览器有IE和Opera。
5)跨浏览器的事件处理程序
自定义合适的事件处理的方法,使其能够在大多数浏览器下一致地运行。
创建addHandler()方法,它的职责是视情况分别使用DOM0级方法、DOM2级方法或IE方法来添加事件。
addHandler()方法接收3个参数:要操作的元素、事件名称和事件处理程序函数。
事件对象
在触发DOM上的某个事件时,会产生一个事件对象event。所有浏览器都支持event对象。
DOM中的事件对象
兼容DOM的浏览器会将一个event对象传入到事件处理程序中。
在事件处理程序内部,对象this始终等于currentTarget的值,而target则包含事件的实际目标。
IE中的事件对象
要访问IE中的event对象有几种不同的方式,取决于指定事件处理程序的方法。
在使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性存在。
因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为this会始终等于事件目标。
所以,最好还是使用event.srcElement来确定事件目标。
跨浏览器的事件对象
对前面的EventUtil对象加以增强:
事件类型
“DOM3级事件”规定了以下几类事件:
UI事件:当用户与页面上的元素进行交互时触发;
UI事件(在DOM2级中归为HTML事件)有:load、unload、abort、error、select、resize、scroll
焦点事件:当元素获得或是去焦点时触发;
blur、focusin、focusout、focus、DOMFocusIn、DOMFocusOut;
利用这些事件并与document.hasFocus()方法及document.activeElement属性配合,可以知晓用户在页面上的行踪。
当焦点从页面中的一个元素移动到另一个元素,会依次触发下列事件:
1)focusout在失去焦点的元素上触发;
2)focusin在获得焦点的元素上触发;
3)blur在失去焦点的元素上触发;
4)DOMFocusOut在失去焦点的元素上触发;
5)focus在获得焦点的元素上触发;
6)DOMFocusIn在获得焦点的元素上触发。
鼠标事件:当用户通过鼠标在页面上执行操作时触发;
click、dbclick、mousedown、mouseenter、mouseleave、
mousemove、mouseout、mouseover、mouseup
相关元素:event对象的relatedTarget属性提供了相关元素的信息。
例如mouserover,事件的主目标是获得光标的元素,而相关元素就是那个失去光标的元素。
滚轮事件:当使用鼠标滚轮(或类似设备)时触发;
mousewheel;
与mousewheel事件对应的event对象除包含鼠标事件的所有标准信息外,还包括一个特殊的wheelDelta属性。
当用户向前滚动鼠标滚轮时,wheelDelta是120的倍数;当用户向后滚动鼠标滚轮时,wheelDelta是-120的倍数。
而FireFox支持名为DOMMouseScroll的类似事件,有关鼠标滚轮的信息则保存在detail属性中,向前滚动鼠标滚轮时,属性值为-3的倍数,向后滚动鼠标滚轮时,属性值为3的倍数。
文本事件:当在文档中输入文本时触发;
键盘事件:当用户通过键盘在页面上执行操作时触发;
keydown(任意键)、keypress(仅字符键)、keyup;
在发生keydown和keyup事件时,event对象的keyCode属性会包含一个代码,与键盘上一个特定的键对应。
要想以跨浏览器的方式取得字符编码,必须首先检测charCode属性是否可用,如果不可用则使用keyCode.
合成事件(DOM3级):当为IME(输入法编辑器)输入字符时触发;
变动事件:当底层DOM结构发生变化时触发;
变动名称事件:当元素或属性名变动时触发。(已被废弃)
HTML5事件
1.contextmenu事件
通常使用contextmenu事件来显示自定义的上下文菜单。
2.beforeunload事件
该事件是为了让开发人员有可能在页面卸载前阻止这一操作。
3.DOMContentLoaded事件
该事件在形成完整的DOM树之后触发,不理会图像、javascript文件、css文件或其他资源是否已经下载完毕。
4.readystatechange事件
该事件的目的是提供与文档或元素的加载状态有关的信息。
支持readystatechange事件的每个对象都有一个readyState属性,可能包含下列值:
uninitialized(未初始化):对象存在但尚未初始化;
loading:对象正在加载数据;
loaded:对象加载数据完成;
interactive(交互):可以操作对象了,但还没有完全加载;
complete:对象加载完毕。
5.pageshow和pagehide事件
6.hashchange事件
将hashchange事件处理程序添加给window对象,然后URL参数列表只要变化就会调用它。
此时event对象应该额外包含两个属性:oldURL和newURL。这两个属性分别保存着参数列表变化前后的完整URL。
设备事件
1.orientationchange事件
苹果公司为移动Safari中添加了orientationchange事件,以便开发人员能够确定用户何时将设备由横向查看模式切换为纵向查看模式。
window.orientation属性可能包含3个值:
0表示肖像模式,90表示向左旋转的横向模式,-90表示向右旋转的横向模式。
2.MozOrientation事件
Firefox3.6为检测设备的方向引入了一个名为MozOrientation的新事件。
3.deviceorientation事件
4.devicemotion事件
触摸与手势事件
1.触摸事件
touchstart:当手指触摸屏幕时触发;即使已经有一个手指放在了屏幕上也会触发。
touchmove:当手指在屏幕上滑动时连续地触发。这个事件发生期间,调用preventDefault()可以阻止滚动
touchend:当手指从屏幕移开时触发;
touchcancel:当系统停止跟踪触摸时触发;
三个用于跟踪触摸的属性:
touches:表示当前跟踪的触摸操作的Touch对象的数组;
targetTouches:特定于事件目标的Touch对象的数组;
changeTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组。
每个Touch对象包含下列属性:
clientX:触摸目标在视口中的x坐标;
clientY:触摸目标在视口中的y坐标
identifier:标识触摸的唯一ID;
pageX:触摸目标在页面中的x坐标;
pageY:触摸目标在页面中的y坐标;
screenX:触摸目标在屏幕中的x坐标;
screenY:触摸目标在屏幕中的y坐标;
target:触摸的DOM节点目标。
2.手势事件
gesturestart:当一个手指已经按在屏幕上而另一个手指又触摸屏幕时触发。
gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。
gestureend:当任何一个手指从屏幕上面移开时触发。
内存和性能
事件委托(事件代理)
事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
优点:
- document对象很快就可以访问,而且可以在页面生命周期的任何时点上为它添加事件处理程序
(无需等待DOMContentLoaded或load事件)。- 在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的DOM引用更少,所花时间更少。
- 整个页面占用的内存空间更少,能够提升整体性能。
移除事件处理程序
每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的Javascript代码之间就会建立一个连接。连接越多,页面执行起来就慢。所以,需要将那些过时不用的“空事件处理程序”移除。
当从文档中移除带有事件处理程序的元素时,原来添加到元素中的事件处理程序极有可能无法被当作垃圾回收。
所以最好手工移除事件处理程序,eg : btn.onclick = null;
导致“空事件处理程序”的另外一种情况,就是卸载页面的时候。如果在页面被卸载之前没有清理干净事件处理程序,那它们会滞留在内存中。
所以,最好的做法是在页面卸载之前,先通过onunload事件处理程序移除所有的事件处理程序。
模拟事件
DOM中的事件模拟
通过document.createEvent()方法创建event对象,这个方法接收一个参数,
即表示要创建的事件类型的字符串。
字符串如下:
UIEvents :一般化的UI事件。
MouseEvents : 一般化的鼠标事件。
MutationEvents : 一般化的DOM变动事件。
HTMLEvents : 一般化的HTML事件。
模拟事件的触发需要使用dispatchEvent()方法,所有支持事件的DOM节点都支持这个方法。
1.模拟鼠标事件(MouseEvent)
2.模拟键盘事件(keyboardEvent)
IE中的事件模拟
通过document.createEventObject()在IE中创建event对象,这个方法不接受参数,返回通用的event对象。然后,你必须手工为这个对象添加所有必要的信息,最后在目标上调用fireEvent()方法。
fireEvent()接受两个参数:事件处理程序名称和event对象。
表单脚本
表单的基础知识
在Javascript中,表单对应的是HTMLFormElement类型,继承于HTMLElement。
有如下属性和方法:
acceptCharset:服务器能够处理的字符集;
action:接受请求的URL;
elements:表单中所有控件的集合;
enctype:请求的编码类型;
length:表单中控件的数量;
method:要发送的HTTP请求类型;
name:表单的名称;
reset():将所有表单域重置为默认值;
submit():提交表单;
target:用于发送请求和接收响应的窗口名称。
取得表单主要是通过为其添加id特性,然后使用document.getElementById()方法取得;
此外还可以通过document.forms可以取得页面中所有的表单。
提交表单
用户点击提交按钮或图像按钮时,就会提交表单。
图像按钮:
浏览器会在将请求发送给服务器之前触发submit事件,这样,我们就有机会验证表单数据,并据以决定是否允许表单提交。阻止这个事件的默认行为就可以取消表单那提交。
表单字段
如果有多个表单控件都在使用一个name(如单选按钮),name就会返回以该name命名的一个NodeList。
共有的表单字段属性
disabled,form,name,readonly,tabIndex,type,value。
为防止用户重复点击表单的提交按钮,需要在第一次单击后就禁用提交按钮。
共有的表单字段方法
focus()和blur()。
在HTML5中,为表单字段新增了一个autofocus属性,在支持这个属性的浏览器中,只要设置了,不用Javascript就能自动把焦点移动到相应字段。
共有的表单字段事件
blur、change、focus。
文本框脚本
<input>和<textarea>
要实现文本框,必须将<input>元素的type特性设置为”text”。而通过设置size特性,可以指定文本框中能够显示的字符数。而maxlength特性则用于指定文本框可以接受的最大字符数。
<textarea>则始终会呈现一个多行文本框。通过使用rows(行)和cols(列)特性来指定文本框大小
选择文本
上述两种文本框都支持select()方法,这个方法用于选择文本框中的所有文本,该方法不接受参数。
1.选择(select)事件
在大多数浏览器中,只有用户选择了文本(而且释放鼠标),才会触发select事件。而在IE8及更早版本中,只要用户选择了一个字母(不必释放鼠标),就会触发select事件。
2.取得选择的文本
在HTML5中,添加了两个属性:selectionStart和selectionEnd。这两个属性中保存的是基于0的数值,表示所选择的文本的范围。
因此,要取得用户在文本框中选择的文本,可以如下编码:
而IE8及其更早版本要实现取得用户选择的文本,需要通过document.selection对象,其中保存着用户在整个文档范围内选择的文本信息。
3.取得部分文本
setSelectionRange()方法,所有文本框都支持,该方法接受两个参数:要选择的第一个字符的索引和要选择的最后一个字符之后的字符的索引。
要看到选择的文本,必须在调用setSelectionRange()之前或之后立即将焦点设置到文本框。
IE9、FireFox、Safari、Chrome和Opera支持这种方案。
而IE8及其更早版本支持使用范围选择部分文本。
过滤输入
1.屏蔽字符
检测keypress事件对应的字符编码,然后再决定如何响应。
EventUtil.addHandler(textbox, “keypress”, function(event){
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
var charCode = EventUtil.getCharCode(event);
//屏蔽非数值,并避免屏蔽一些常用和必要的键,,如向上键、向下键、ctrl+C等
if(!/\d/.test(String.fromCharCode(charCode))&&charCode>9&&!event.ctrlKey){
EventUtil.preventDefault(event);
}
});
2.操作剪贴板
六个剪贴板事件:
beforecopy:在发生复制操作前触发。
copy:在发生复制操作时触发。
beforecut:在发生剪切操作前触发。
cut:在发生剪切操作时触发。
beforepaste:在发生粘贴操作前触发。
paste:在发生粘贴操作时触发。
要访问剪切板中的数据,可以使用clipboardData对象:在IE中,这个对象是window对象的属性;而在其他主流浏览器中,这个对象是相应的event对象的属性。
clipboardData对象有三个方法:
getData()、setData()和clearData()。
这三个方法的接受参数在IE和其他主流浏览器中有所区别。
在需要确保粘贴到文本框中的文本包含某些字符,或者符合某种格式要求时,能够访问剪贴板是非常有用的。
自动切换焦点
为增强表单字段的易用性,通常在用户填写完当前字段时,自动将焦点切换到下一个字段。
当用户在前一个文本框中输入的字符达到最大数量后,自动将焦点切换到下一个文本框。
HTML5约束验证API
当用户禁用javascript或浏览器不支持时,使用HTML5提供的一些功能,仍能够实现基本的验证。
1.必填字段
required属性
2.其他输入类型
HTML5为<input>元素的type属性又增加了几个值:email和url等。
3.数值范围
除了”email”和“url”,HTML5还定义了另外几个输入元素:”number”、”range”、”datetime”、”datetime-local”、”date”、”month”、”week”以及”time”.
对这些数值类型的输入元素,可以指定min属性、max属性和step属性。此外,还有两个方法:stepUp()和stepDown(),
都可以接收一个可选的参数:要在当前值基础上加上减去的数值。(默认是加或减1)
这两个方法都还没得到任何浏览器的支持。
4.输入模式
HTML5为文本字段新增了pattern属性,这个属性的值是一个正则表达式,用于匹配文本框中的值。
只允许输入数值:
注意,模式的开头和结尾不用加^和$符号。这两个符号表示输入的值必须从头到尾都与模式匹配。
5.检测有效性
使用checkValidity()方法可以检测表单中的某个字段是否有效。如果字段有效,返回true,否则返回false。字段的值是否有效的判断依据是根据前面介绍的各种约束,如必填字段如果没有值就是无效,而字段中的值与pattern属性不匹配也是无效的。
validity属性会告知什么字段有效或无效。这个对象有一系列属性,每个属性返回布尔值。
customError、patternMismatch、rangeOverflow、rangeUnderflow、stepMismatch、tooLong、
typeMismatch、valid、valueMissing。
6.禁用验证
通过设置novalidate属性,可以告诉表单不进行验证。
选择框脚本
选择框是通过<select>和<option>元素创建的。
有如下的属性和方法:
add(newOption,relOption):向控件中插入新<option>元素,其位置在相关项(relOption)之前。
multiple:布尔值,表示是否允许多选。
options:控件中所有<option>元素的HTMLCollection。
remove(index):移除给定位置的选项。
selectIndex:基于0的选中项的索引,如果没有选中项,则值为-1; 对于支持多选的控件,只保存选中项中第一项的索引。
size:选择框中可见的行数。
在DOM中,每个<option>元素都有一个HTMLOptionElement对象表示,并拥有如下的属性:
index:当前选项在options 集合中的索引。
label:当前选项的标签;
selected:布尔值,表示当前选项是否被选中。将这个属性设置为true可以选中当前选项。
text:选项的文本;
value:选项的值。
选择选项
对于单选的选择框,访问选中项的最简单方式,就是使用选择框的selectedIndex 属性。
另一种选择选项的方式,就是取得对某一项的引用,然后将其属性设置为true。
selected属性的作用主要是确定用户选择了选择框中的哪一项。
添加选项
有多种添加选项的方式,如下:
第二种方式是使用Option构造函数来创建新选项。Option构造函数接收两个参数:文本(text)和值(value)。
第三种添加新选项的方式是使用选择框的add()方法。该方法接受两个参数:要添加的新选项和将位于新选项之后的选项。
要想将新选项插入到其他位置,应该使用标准的DOM技术和insertBefore()方法。
移除选项
使用DOM的removeChild()方法或者使用选择框的remove()方法,还有最后一种就是将相应选项设置为null。
移动和重排选项
使用DOM技术能够很简单的实现移动和重排选项。
表单序列化
浏览器将数据发送给服务器的过程:
对表单字段的名称和值进行URL编码,使用和号(&)分隔;
不发送禁用的表单字段;
只发送勾选的复选框和单选按钮;
不发送type为“reset”和“button”的按钮;
多选选择框中的每个选中的值单独一个条目;
在单击提交按钮提交表单的情况下,也会发送提交按钮;否则不发送提交按钮。
<select>元素的值,就是选中的<option>元素的value特性的值。如果<option>元素没有value特性,则是<option>元素的文本值。
表单序列化的实现:
富文本选区
在富文本编辑器中,使用框架的getSelection()方法,可以确定实际选择的文本。这个方法是window对象和document对象的属性,调用它会返回一个表示当前选择文本的Selection对象。每个Selection对象都有以下属性:
anchorNode:选区起点所在的节点。
anchorOffset:在到达选区起点位置之前跳过anchorNode中的字符数量。
focusNode:选区终点所在的节点。
focusOffset:focusNode中包含在选区之内的字符数量。
isCollapsed:布尔值,表示选区的起点和终点是否重合。
rangeCount:选区中包含的DOM范围的数量。
上述属性并没有多少用处,但以下的方法提供了更多的信息,并支持对选区的操作。
addRange(range):将指定的DOM范围添加到选区中。
collapse(nde,offset):将选区折叠到指定节点中的相应的文本偏移位置。
collapseToEnd():将选区折叠到终点位置。
collapseToStart():将选区折叠到起点位置。
containsNode(node):确定指定的节点是否包含在选区中。
deleteFromDocument():从文档中删除选区中的文本,与document。execCommand(“delete”,false,null)命令的结果相同。
extend(node,offset):通过将focusNode和focusOffset移动到指定的值来扩展选区。
getRangeAt(index):返回索引对应选区中的DOM范围。
removeAllRanges():从选区中移除所有DOM范围。实际上,这样会移除选区,因为选区中至少要有一个范围。
removeRange(range):从选区中移除指定的DOM范围。
selectAllChildren(node):清除选区并选择指定节点的所有子节点。
toString():返回选区所包含的文本内容。
下面来看一个例子:
表单与富文本
富文本编辑器是使用iframe而非表单控件实现的,富文本编辑器中的HTML不会被自动提交到服务器,而需要我们手工来提取并提交HTML。
通过文档主体的innerHTML属性获取了iframe中的HTML,然后将其插入到了名为”comments”的表单字段中。