事件
JS 与 HTML 之间交互便是通过事件实现的。事件就是文档或者浏览器窗口中发生的一些特定的交互瞬间。可以使用监听器来预定事件,以便事件发生的时候执行相应的代码。
1. 事件流
事件流描述的是从页面中接收时间的顺序,IE的为冒泡流,而Netscape为事件捕获流。
1.1 事件冒泡
那么事件流的顺序按照冒泡为
div -> body -> html -> Document
需要注意的是 只有 IE9, FF, Chrome 和 Safari 会一直冒泡到 window
1.2 事件捕获
事件捕获则是一个相反的流程
Document ->html -> body -> div
依次从上到下传递事件流。、
1.3 DOM 事件流
DOM2级事件 中规定事件流包括3各阶段:事件捕获,处理目标和事件冒泡。
对于IE,直到IE9才支持该DOM2级事件。
2. 事件处理程序
事件就是用户或者浏览器自身执行某种动作,响应某个事件的函数就称之为事件处理程序。
2.1 HTML事件处理程序
该操作通过指定 onclick 特性并将一些 JS 代码作为它的值来进行定义。
需要注意的地方就是 HTML事件中 this 的指向问题
如上的 input 代码 当 onclick="alert(this.value)",没有问题会弹出 "Click Me" 的结果。
但是一旦 onclick="showValue()"再定义
function showValue(){
alert(this.value);
}
结果为 undefined。 因为这里的this 已经指向到 window,如果需要将函数定义在外部全局中,并且需要使用到所点击的节点DOM,那么就在HTML的JS代码中传入 this 即可,onclick="showValue(this)",在处理函数。
2.2 DOM 0级事件处理程序
通过JS指定事件处理程序传统方式就是将函数赋给一个事件处理程序属性。简单又有跨浏览器优势。
函数内部的 this 会指向当前元素
删除事件只需要将事件属性置为 null 即可。
2.3 DOM 2级事件处理程序
DOM 2级事件 定义了2个方法, addEventListener 和 removeEventListener 。参数有三个, 处理的事件名,事件函数以及是否是捕获阶段的布尔值。
在此的事件函数是使用匿名函数的方式传入,所以就不能在使用 removeEventListener 来解除绑定了,因为就算传入了一样的匿名函数,也是指向不同的函数,所以希望使用 remove 的,必须在add事件的时候使用变量引用传入。
还有就是对于一个元素的一个属性上可以绑定多个事件,并且是按照绑定的顺序执行的。addEventListener添加的事件只能使用 removeEventListener 来解绑,不能使用 0级事件处理程序的方式来解除事件绑定。
(IE9, FF,Safari, Chrome, Opera 支持 DOM2级)
2.4 IE事件处理程序
IE实现的与DOM中类似的两个方法 attachEvent 和 detachEvent 接受两个参数 事件处理程序名称 和 事件处理程序函数。 其第一个参数为 on 开头,如 onclick,onsubmit等。
在IE中使用attachEvent与使用DOM0级方法最大的区别是事件处理程序的作用域。DOM0级中事件处理程序会在所属元素的作用域内运行,但是在 attachEvent 方法下,事件处理程序会在全局作用于下运行,this会指向于window。
在对于编写跨浏览器的代码时,记住这点很重要。
还有一个与标准的DOM2级不同的是,在一个元素添加多个事件处理程序的时候,addEventListener是按照绑定的顺序先绑定的先执行原则触发事件处理程序的,但是 attachEvent 是先执行最近绑定的事件的,即执行顺序上与 addEventListener 相反。
在点击后,先弹出 “Helloworld!”,而后 “Clicked”。
2.5 跨浏览器的事件处理程序
这边只是简单的提供了一个兼容方案,当然没有很细致的考虑其他的比如IE中事件处理程序的作用域问题,以及绑定多个事件的执行顺序问题。
3. 事件对象
在触发DOM上的某个事件的时候,会产生一个事件对象event,该对象中包含了所有与事件有关的信息。例如鼠标点击时刻鼠标的位置,键盘按下有关于键的信息等。
3.1 DOM中的事件对象
兼容DOM的浏览器不管使用 DOM0级还是DOM2级都会将 event 事件对象传入。
event 对象包含与创建他的特定时间有关的属性和方法,触发的事件类型不一样可用的属性和方法也不一样,不过所有的又会有以下属性:
在事件处理程序内部,this 始终指向 currentTarget的值, 对于 target 只包含事件的实际目标,当直接将事件处理程序指定给目标元素,那么三个值就是相等的。
如下,将事件处理程序绑定在 body上
也可以看做提高效率的 事件委托 的雏形。
event 事件对象中 preventDefault() 来取消默认行为,比如 a 标签的跳转行为等,stopPropagation() 方法使得事件停止在DOM中的传播,取消进一步的事件捕获或者冒泡。
注意,只有在事件处理程序执行期间event事件对象才会存在,一旦事件结束,event对象就会销毁。
3.2 IE中的事件对象
之前所说的均是标准DOM下的方式, IE对于事件对象有一定的不同,但是总体上是一致的。在DOM 0级下,event对象作为window的一个属性存在
event并不是直接存在于事件处理程序之中的,而需要获取到 window.event 再进行使用。在attenchEvent中,event对象就会被作为参数传入事件处理程序之中。
IE的event同样也会包含一些属性和方法
因为事件处理程序的作用域是更具指定他的方式来确定的,所有不能认为this会始终等于时间目标,使用 event.srcElement 比较保险。
3.3 跨浏览器的事件对象
虽然 DOM 与 IE 下event对象有所不同,但是基于相似性还是可以实现跨浏览器的解决方案来的。
4. 事件类型
DOM3级规定了以下
UI事件,当用户与页面上的元素交互的时候触发。
焦点事件,当元素获得或者失去焦点的时候触发。
鼠标事件,当用户通过鼠标在页面上执行操作的时候触发。
滚轮事件,当用户鼠标滚轮时触发。
文本事件,当文档中输入文本的时候触发。
键盘事件。
合成事件,当为IME 输入法编辑器 输入字符的时候触发。
变动事件,当低层的DOM结构发生变化的时候触发。
DOM3级事件模块在DOM2级事件模块的基础上重新定义了这些事件,也添加了新的事件,IE9在内主流浏览器均已支持DOM2级事件,IE9也支持部分DOM3事件。
4.1 UI事件
DOMActivate:表示元素已经被用户操作过(DOM3中已经废弃)。
load:当页面完全加载后在window上面触发,当所有的框架都加载完毕的时候在框架集上触发,当图像加载完毕的时候在上触发,以及嵌入的
unload:页面完全卸载的时候在window上面触发。。。
abort:用户停止下载过程,内容还没有加载完毕的时候。
error:当JS在window上发生错误的时候,图像无法加载的时候等。
select:当用户选着文本框中的一个或者多姿字符时候触发。
resize:窗口或者框架大小变化的时候触发。
scroll:用户滚动带滚动条的内容的时候触发。
需要注意的是,resize和scroll事件会在我们调整窗口大小或者滚动的时候重复被触发,所以应该尽量保持事件处理程序的代码简单,或者做好逻辑处理。
4.2 焦点事件
blur:当元素失去焦点的时候触发,事件不冒泡,各个浏览器兼容。
focus:当元素获得焦点的时候触发,事件不冒泡,各个浏览器兼容。
focusin:当元素获得焦点的时候触发,事件冒泡,各个浏览器兼容(IE5.5+ SF5.1+ Op 11.5+ chrome)。
focusout:当元素失去焦点的时候触发,事件冒泡,各个浏览器兼容(IE5.5+ SF5.1+ Op 11.5+ chrome)。
那么当焦点从一个元素移动到另外一个元素的时候会依次触发
1. focusout 在失去焦点的元素上触发
2. focusin 在获得焦点的元素上触发
3. blue 在失去焦点的元素上触发
4. focusout 在获得焦点的元素上触发
( Opera 上 还有DOMFocusIn 和 DOMFocusOut 事件 )
4.3 鼠标与滚轮事件
click: 用户单击鼠标按钮时,或者按下回车键的时候。
dblclick: 用户双击鼠标时候触发。知道DOM3才将其纳入标准。
mousedown:用户按下任意鼠标按钮,不可以通过键盘触发。
mouseup:用户释放任意鼠标按钮,不可以通过键盘触发。
mouseenter:在鼠标光标从元素外部移到元素范围内的时候触发,事件不冒泡,而且在移动到后代元素上的时候不触发,DOM未定义该事件,DOM3纳入规范(IE,FF 9+,Opera等均支持)。
mouseleave:在鼠标光标从元素内部移到元素范围外的时候触发,事件不冒泡,而且在移动到后代元素上的时候不触发,DOM未定义该事件,DOM3纳入规范(IE,FF 9+,Opera等均支持)。
mousemove:当鼠标在元素内部移动的时候重复的触发。不能通过键盘触发。
mouseout:鼠标在一个元素上方,然后首次移入到另外一个元素的时候触发。
mouseover:鼠标在一个元素外部,然后首次移入到该元素上方的时候触发。
在双击鼠标的时候
mousedown -> mouseup -> click -> mousedown -> mouseup -> click ->dblclick
在对于IE8之前的版本来说会有一个小BUG, 双击时候会跳过第二个 mousedown 和 click。
检测是否支持DOM2 DOM3级
document.implementation.hasFeature("MouseEvents", "2.0");
document.implementation.hasFeature("MouseEvent", "3.0");
还有一个跟踪鼠标滚轮的事件 mousewheel 。
1. 客户区坐标位置
鼠标事件都是在浏览器视口中特定位子发生的,位子信息保存在事件对象的 clientX 与 clientY 属性中。
可以使用下面代码获取
该位置为鼠标相对于客户端视口的位子,不包括页面滚动的距离,并不表示鼠标在页面上的位置。
2. 页面坐标位置
页面位置通过事件对象的 pageX 和 pageY 属性来获取,此坐标为页面本身而非视口的左边与顶边计算的。
因此在页面没有滚动的情况下,clientX,clientY 与 pageX,pageY的值是相等的。
IE8以及早期的版本不支持事件对象的页面坐标,不过可以通过客户区坐标和滚动信息计算出来,需要使用 scrollLeft和scrollTop。
3. 屏幕坐标位置
还有一个相对于屏幕的位子坐标信息 screenX,screenY。
代码如下:
4. 修改键
鼠标事件主要是由鼠标来触发的,但是也有可能按下了其他键。 状态为 shiftKey,ctrlKey,altKey和metaKey。
IE8及之前版本不支持。
5. 相关元素
相关元素指的是鼠标在发生 mouseover 和 mouseout 的时候还会涉及到其他元素。
DOM通过event 的 relatedTarget 属性提供相关元素的信息,只对 mouseover 和 mouseout 事件才包含,对于其他事件均为 null。
IE8之前版本没有relatedTarget 属性,可以使用fromElement 和 toElement 来获取相等意义的元素。
6. 鼠标按钮
DOM的button 属性
0:主鼠标键; 1:鼠标中间键或者滚轮键; 2:次鼠标键。
IE8及以前浏览器
7. 更多的事件信息
DOM2级事件规范还在event对象中提供了 detail 属性,用于给出更多的事件信息。
对于鼠标事件来说,detail中包含了一个数值表示在给定的位子上发生了多少次的点击。
8. 鼠标滚轮事件
从IE6起,就支持 mousewheel 事件,当用户通过鼠标滚轮与页面交互,在垂直方向上滚动页面的时候都会触发 mousewheel 事件。在滚动的时候还包含一个特殊的 wheelDelta 属性,记录的滚动的量值,为120的倍数向前为正,向后为负。Firefox 支持一个名为 DOMMouseScroll的类似事件,信息保存在detail属性中,量值为3的倍数。向前为负,向后为正。
9. 触摸设备
由于iOS, 安卓等设备较为特殊,没有鼠标所以
1. 不支持 dbclick,双击放大画面,无法改变该行为。
2. 轻击可单击元素会触发 mouseover 事件,如果此操作会导致内容变化将不再有其他事件发生。如果没有变化 一次会触发 mousedown mouseup 和 click。
3. mousemove 也会触发 mouseover 和 mouseout 事件。
4. 两个手指在屏幕上且页面随手指滚动的时候也会触发 mousewheel 和 scroll 事件。
10. 无障碍性问题
4.4 键盘与文本事件
DOM3级事件 为键盘事件制订了规范
只有一个文本事件 textInput,是对keypress 的补充,使得将文本展现给用户之前可以拦截文本,在文本插入文本框之前就会触发 textInput。
1. 键码
keydown 和 keyup 的事件 event 对象的 keyCode 属性中包含一个代码,来显示所按下的。
2. 字符编码
IE9 以及其他主流浏览器的 event 对象都支持一个 charCode,代表按下键的 ASCII编码。
3. DOM3级变化
DOM3的键盘事件不再包含 charCode 属性, 而是分开为 key 和 char 的两个新属性。
4. textInput 事件
当用户在可编辑区域中输入字符的时候就会触发该事件,任何可获得焦点的元素都可以触发 keypress,而是由可编辑区域才可以触发 textInput。
4.5 复合事件
是DOM3中的新添加的一类事件用于处理 IME 输入序列。可以让用户输入键盘上没有的符号。
4.6 变动事件
DOM2级变动事件能在DOM的某一部分发生变化的时候来给出提示。
对于浏览器支持情况来说
1. 删除节点
在使用removeChild() 或者 replaceChild() 从DOM中删除节点的时候首先会触发 DOMNodeRemoved事件。该事件的 event.target 就是被删除的节点。
而 event.relatedNode则是对目标节点父节点的引用。
在上面HTML中,我们删除 ul 元素,就会依次:
1. 在 ul 上触发 DOMNodeRemoved 事件,relatedNode 属性等于 document.body。
2. 在 ul 上触发 DOMNodeRemovedFromDocument 事件。
3. 在 ul 下的 每一个 li 上 触发DOMNodeRemovedFromDocument 事件。
4. 在 document.body 上触发 DOMSubtreeModified 事件。
验证代码:
2. 插入节点
在使用appendChild,replaceChild 和 insertBefore 向DOM中插入节点的时候首先会触发 DOMNodeInserted 事件。
首先触发的是 DOMNodeInserted 事件,该事件的目标是被插入的节点,相同的 event.relatedNode 属性中包含的是对父节点的引用。并且该事件是冒泡的。
其次为在新插入的节点上触发 DOMNodeInsertedIntoDocument 事件,该事件不冒泡。事件的目标为被插入的节点,所以需要在该节点插入之给节点添加好事件处理程序。
4.7 HTML 5 事件
DOM规范没有涵盖所有的浏览器支持的事件许多浏览器推出了自己的特殊事件。HTML5详尽的列出了浏览器应该支持的所有事件。
1. contextmenu 事件
为了实现上下文菜单,windows下是鼠标右键单击的菜单,如何操作该事件,于是就有了contextmenu 事件,用以表示何时该显示上下文菜单,以便开发人员自定义上下文菜单。
JS 实现自定义上下文菜单
基本浏览器均已支持。
2. beforeunload 事件
window对象上的事件处理函数, 是为了让开发人员有可能在页面卸载之前来阻止该操作。
除了 opera11之前版本,其余均以支持。
3. DOMContentLoaded 事件
window的load事件会在页面中的一切都加载完毕之后触发,而DOMContentLoaded 事件则在完整的DOM数之后就触发,不会受到图片,JS文件,css文件等影响,意味着客户可以更早的与页面交互。
支持的浏览器有 IE9+ FF Chrome SF Op9+ 。
对于不支持的浏览器建议页面加载期间设置一个事件为 0ms 的setTimeout 来进行模拟处理。即在当前JS处理完毕后立即运行该函数。
4. readystatechange 事件
IE 为 DOM 文档中的某些部分提供了 readystatechange 事件,目的是为了提供与文档或者元素的加载状态相关的信息,支持readystatechange 的每个对象还有一个 readyState 属性:
1. uninitialized 未初始化 :对象存在但是尚未初始化
2. loading 正在加载 : 对象正在加载数据