文章目录
1 事件流
事件流描述的是从页面中接受事件的顺序。IE和NetScape采用了相反的实现机制,IE中采用的是事件冒泡,NetScape采用的是事件捕获。
事件冒泡:
事件从最具体的元素,逐级传递到最不具体的元素。
事件捕获:
事件捕获的目的是在事件到达预定目标之前捕获它。事件捕获的思想是越不具体的节点更早接受到事件,而最具体的节点最后接受到事件。
DOM事件流:
DOM事件流定义了三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。事件捕获是为截获事件提供机会。
2 事件处理程序
事件是用户或浏览器自身执行的动作。而事件处理程序是对事件的响应的一个函数。
2.1 HTML事件处理程序
HTML中每个元素支持的事件都可以使用一个与相应事件处理程序同名的特性指定。
- 事件处理程序中的代码在执行时有权访问全局作用域中的任何代码。
- 事件处理程序中包含了一个事件对象event。
- 在函数内部,this就代表事件的目标元素。
- 当然我们可以用with拓展作用域链。
然而在HTML中指定事件处理程序有两个缺点:
- 存在一个时差问题。当触发事件是,相应事件处理程序还不具备执行条件。可以像下面这样解决。加try-catch
- 扩展作用域链在不同浏览器上会有不同的结果。
- 把HTML代码和JavaScript代码耦合起来了。
2.2 DOM0 级事件处理程序(JavaScript事件处理程序)
需要获取被操作对象的引用。如下:
也可以删除事件处理程序:
2.3 DOM2级事件处理程序
DOM2级事件处理程序提供了两个方法addEventListener()、removeEventLister()。
两个方法的参数一样,都是三个,分别为:要处理的事件名、事件处理程序、布尔值(true表示是在事件捕获阶段调用事件处理程序;false表示是在事件冒泡阶段调用事件处理程序)
通过addEventListener()添加的事件处理程序只能使用removeEventListener来删除。且传入的参数必须一样(意味着传入匿名函数不能被删除)。
大多数情况下都是将事件处理程序添加到事件冒泡阶段。除非想要截获事件时才将事件处理程序添加到事件捕获阶段。
2.4 IE事件处理程序
IE也提供了attachEvent(),detachEvent() 两个方法来添加事件处理程序。例子:
参数区别
需要注意的是attachEvent的第一个参数直接是onclick,而不是Dom2级事件处理程序的click.
作用域区别
另外,通过attachEvent与dom0或dom2事件处理程序的区别在于事件处理程序的作用域。dom0或dom2事件处理程序是在其所属的元素的作用域内运行的,而IE事件处理程序是在全局作用域中运行的。下面例子就说明了这个问题:
执行顺序的区别
通过attachEvent添加的事件处理程序的执行顺序不是按添加顺序执行的,而是反过来执行的。
通过detachEvent移除事件处理程序时,参数要和用attachEvent添加事件处理程序一样(所以不能删除匿名函数)。
2.5 跨浏览器的事件处理程序
如何使用?
当然这个跨浏览器的事件处理程序没有考虑所以的浏览器问题,比如Ie中的作用域问题。
3 事件对象
事件对象(event),包含着所以与事件有关的信息。如导致事件的元素,事件的类型等。
3.1 DOM中事件对象
(1)兼容DOM的浏览器会将event传入到事件处理程序中。下面是事件对象的属性及方法:
(2) 在事件处理程序内部,this始终等于currentTarget的值,而target只包含事件的事件目标。比如事件处理程序存在于按钮的父节点中时:
(3) 在需要一个函数中处理多个事件是可以使用type属性。
(4) 要阻止事件的默认行为可以使用preventDefault()。例如阻止链接导航:
(5) 使用stopPropagtion()方法来立即阻止事件在dom层次的传递。
(6) 使用事件对象的eventPhase属性来确定事件当前正处于事件流的那个阶段。eventPhase为1时表示处于事件捕获阶段,eventPhase为2时表示处于目标对象,为3时表示处于冒泡阶段。
3.2 IE中事件对象
event对象的获取
- 使用DOM0级方法添加事件处理程序时,event对象作为window对象的一个属性。
- 使用ie的attachEvent添加事件处理程序时,event对象会作为一个参数传入。当然也可以通过window对象获取event。
- 使用HTML特性添加事件处理程序时,可以通过变量event访问event对象。
ie中event对象共有属性:
(1)不要认为this始终等于事件目标(因为事件处理程序的作用域是根据指定它的方式确定的)。最好用event.srcElement保险一些。比如下面:
(2) ie中event的returnValue属性相当于Dom中event的preventDefault()方法,用来阻止执行默认行为。
(3)cancelBubble相当于Dom中event的stopPropagtion()方法,用来阻止事件在dom层级的传递。然而,ie中不支持事件捕获,只支持事件冒泡。
3.3 跨浏览器的事件对象
IE中event对象的全部信息和方法dom中的event对象都有,只不过实现方式不一样。
实现跨浏览器的事件对象如下:
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 {
element["on" + type] = handler;
}
},
getEvent: function(event){
return event ? event : window.event;
},
getTarget: function(event){
return event.target || event.srcElement;//应该可以改为event.target ? 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);
} else if (element.detachEvent){
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
stopPropagation: function(event){
if (event.stopPropagation){
event.stopPropagation();
} else {
event.cancelBubble = true;
}
}
};
4 事件类型
事件类型有:
HTML5定义了一组事件,而dom,bom还会实现其他专有事件。
4.1 UI事件
UI事件指的是那些不一定和用户操作有关的事件。现有的UI事件如下:
事件 | 描述 |
---|---|
DOMActive | 表示元素已经被用户操作(通过鼠标或键盘)激活。这个事件在Dom3级事件中已经被废除 |
load | 当页面加载完毕后再window上面触发, |
unload | 事件在文档完全被卸载后触发 |
abort | 当用户停止下载过程时,如果嵌入的内容没有被下载完就会触发这个事件 |
error | 当发生错误是在相应元素触发 |
select | 当用户选择文本框中的一个或多个字符时触发 |
resize | 当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件 |
scroll | 当用户滚动带有滚动条元素时在该元素触发 |
4.2 焦点事件
利用焦点事件和document.hasFocus()方法及document.activeElement属性配合,可以知晓用户在页面上的行踪。6个焦点如下:
- blur
- DOMFocusIn
- DOMFocusOut
- focus
- focusin
- focusout
当焦点从页面中的一个元素移到另一个元素,会依次触发下列事件:
4.3 鼠标与滚轮事件
(1)DOM3级定义了9个鼠标事件:
鼠标事件 | 说明 |
---|---|
click | 用户单击主鼠标(一般是左键)或者按enter键触发 |
dbclick | 用户双击主鼠标触发 |
mousedown | 用户按下任意鼠标时触发。不能通过键盘触发 |
mouseup | 用户释放任意鼠标时触发。不能通过键盘触发 |
mouseenter | 当鼠标光标首次从元素外部移动到元素内部时触发。这个事件不冒泡 |
mouseleave | 当位于元素上方的鼠标光标移动到元素外部时触发。这个事件不冒泡 |
mouseover | 在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时触发。不能通过键盘触发这个事件 |
mouseout | 在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时触发。 |
mousemove | 当鼠标指针在元素内部移动时重复地触发。不能通过键盘触发这个事件。 |
下面是双击时,事件的触发顺序:
- mousedown
- mouseup
- click
- mousedown
- mouseup
- click
- dblclick
(2)9个小点
- 获取可获取坐标位置。这个信息保存在事件对象的clientX与clientY中。需要注意的是这个值并不表示鼠标在页面上的位置。
- 页面坐标位置。这个信息保存在pageX与pageY中,这个信息可以告诉你当前鼠标位于页面的哪个位置 。
- 屏幕坐标位置。这个信息保存在screenX与screenY中,这个信息可以告诉你当前鼠标位于整个电脑屏幕的哪个位置。
- 虽然鼠标事件主要是使用鼠标来触发的,但在按下鼠标时键盘上的某些键的状态也可以影响到所要采取的操作。比如ctrl、alt、shift、meta(在window里是Windows键,在macos里面是Cmd键)键。这些信息保存在ctrlKey、altKey、shiftKey、metaKey里面,这4个key是布尔形的。
- 相关元素relatedTarget。mouseover、mouseout这两个事件都会涉及把鼠标指针从一个元素的边界之内移动到另一个元素的边界之内。对 mouseover 事件而言,事件的主目标是获得光标的素,而相关元素就是那个失去光标的元素。类似地,对 mouseout 事件而言,事件的主目标是失去光标的元素,而相关元素则是获得光标的元素。
- 鼠标按钮,用于表示按钮的状态。DOM 的 button 属性可能有如下 3 个值: 0 表示主鼠标按钮, 1 表示中间的鼠标按钮(鼠标滚轮按钮), 2 表示次鼠标按钮。
- 更多事件信息detail。对于鼠标事件来说, detail 中包含了一个数值,表示在给定位置上发生了多少次单击。
- 鼠标滚轮事件mouseWheel。当用户通过鼠标滚轮与页面交互、在垂直方向上滚动页面时(无论向上还是向下),就会触发 mousewheel事件。
- 触摸设备。 不支持 dblclick 事件。轻击可单击元素会触发 mousemove 事件。mousemove 事件也会触发 mouseover 和 mouseout 事件。两个手指放在屏幕上且页面随手指移动而滚动时会触发 mousewheel 和 scroll 事件。
4.4 键盘与文本事件
三个键盘事件一个文本事件:
键盘事件/文本事件 | 说明 |
---|---|
keydown | 当用户按下键盘任意键时触发,而且按住不放的话,会重复触发该事件 |
keypress | 当用户按下键盘字符键时触发,而且如果按住不放的话,会重复触发此事件 |
keyup | 当用户释放键盘上的键时触发 |
textInput | 这是一个文本事件,在文本插入文本框之前触发 |
键码:
在发送keyup和keydown事件是,event对象的keyCode会有一个值,为相应键的ASCII码。
字符编码:
IE9、 Firefox、 Chrome 和 Safari 的 event 对象都支持一个 charCode 属性,这个属性只有在发生keypress 事件时才包含值,而且这个值是按下的那个键所代表字符的 ASCII 编码。
DOM3级变化:
DOM3级事件发生了一些变化。如DOM3级事件中的键盘事件,不再包含 charCode 属性,而是包含两个新属性: key 和 char。由于存在跨浏览器问题,因此不推荐使用 key、 keyIdentifier 或 char。
textInput事件:
这个用于替代 keypress 的 textInput 事件的行为稍有不同。区别如下:
- 就是任何可以获得焦点的元素都可以触发 keypress 事件,但只有可编辑区域才能触发 textInput事件。
- 是 textInput 事件只会在用户按下能够输入实际字符的键时才会被触发,而 keypress事件则在按下那些能够影响文本显示的键时也会触发(例如退格键)。
由于 textInput 事件主要考虑的是字符,因此它的 event 对象中还包含一个 data 属性,这个属性的值就是用户输入的字符(而非字符编码)。另外, event 对象上还有一个属性,叫 inputMethod,表示把文本输入到文本框中的方式
设备中键盘事件:
任天堂 Wii 会在用户按下 Wii 遥控器上的按键时触发键盘事件。
4.5 复合事件
用于处理 IME 的输入序列。IME(Input Method Editor,输入法编辑器)可以让用户输入在物理键盘上找不到的字符。有以下三个事件:
- compositionstart:在 IME 的文本复合系统打开时触发,表示要开始输入了。
- compositionupdate:在向输入字段中插入新字符时触发。
- compositionend:在 IME 的文本复合系统关闭时触发,表示返回正常键盘输入状态。
复合事件有一个data属性,用于获取用户的输入信息。
经过我在chrome,ie,edge三款浏览器上运行,发现只有edge对复合事件的支持下好一些,其他都不怎么样。所以复合事件用处不大
4.6 变动事件
变动事件,就是在dom发生变化时,给出提示。在插入节点、删除节点都会触发相应的事件。
4.8 HTML5事件
HTML5详尽的列出了浏览器应该支持的事件。下面仅仅讨论被浏览器完美支持的事件。
contextMenu事件
Windows 95 在 PC 中引入了上下文菜单的概念,即通过单击鼠标右键可以调出上下文菜单。不久,这个概念也被引入了 Web 领域。
beforeUnload事件
之所以有发生在 window 对象上的 beforeunload 事件,是为了让开发人员有可能在页面卸载前阻止这一操作。不能完全取消该事件。
DOMContentLoaded事件
而 DOMContentLoaded 事件则在形成完整的 DOM 树之后就会触发,不理会图像、 JavaScript 文件、 CSS 文件或其他资源是否已经下载完毕。
readyChangeState事件
这个事件的目的是提供与文档或元素的加载状态有关的信息,但这个事件的行为有时候也很难预料。
pageShow和pageHide事件
Firefox 和 Opera 有一个特性,名叫“往返缓存”(back-forward cache,或 bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。内存中包含了整个页面。
第一个事件就是 pageshow,这个事件在页面显示时触发,无论该页面是否来自 bfcache。在重新加载的页面中, pageshow 会在 load 事件触发后触发;而对于 bfcache 中的页面, pageshow 会在页面状态完全恢复的那一刻触发。
与 pageshow 事件对应的是 pagehide 事件,该事件会在浏览器卸载页面的时候触发,而且是在unload 事件之前触发。
hashchange事件
HTML5 新增了 hashchange 事件,以便在 URL 的参数列表(及 URL 中“#”号后面的所有字符串)发生变化时通知开发人员。
4.9 设备事件
智能手机和平板电脑的普及,为用户与浏览器交互引入了一种新的方式,而一类新事件也应运而生。设备事件(device event)可以让开发人员确定用户在怎样使用设备。
4.10 触摸与手势事件
因为 iOS 设备既没有鼠标也没有键盘,所以在为移动 Safari 开发交互性网页时,常规的鼠标和键盘事件根本不够用触。摸事件会在用户手指放在屏幕上面时、在屏幕上滑动时或从屏幕上移开时触发。
iOS 2.0 中的 Safari 还引入了一组手势事件。当两个手指触摸屏幕时就会产生手势,手势通常会改变显示项的大小,或者旋转显示项。
5. 内存和性能
在javaScript中,添加到页面的事件处理程序的数量将直接影响页面运行的整体性能。具体有两点:
- 每个函数都是对,要占有内存,内存中对象越多,性能越差。
- 在设置事件处理程序时,需要获取元素,将导致dom访问。dom访问次数越多,性能越差。
5.1 事件委派
事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。例如, click 事件会一直冒泡到document 层次。也就是说,我们可以为整个页面指定一个 onclick 事件处理程序,而不必给每个可单击的元素分别添加事
件处理程序。
5.2 移除事件处理程序
有两种情况可以导致“空事件处理程序”问题。
- 第一种情况就是从文档中移除带有事件处理程序的元素时。对策是在删除元素之前,先删除元素的事件处理程序。
- 第二种情况就是卸载页面的时候。如果在页面被卸载之前没
有清理干净事件处理程序,那它们就会滞留在内存中。对策就是通过 onunload 事件处理程序移除所有事件处理程序。
6 模拟事件
模拟事件就是用javascript在任意时刻触发事件。就像用户触发事件一样。在测试 Web 应用程序,模拟触发事件是一种极其有用的技术。 DOM2 级规范为此规定了模拟特定事件的方式, IE9、 Opera、 Firefox、 Chrome 和 Safari 都支持这种方式。 IE 有它自己模拟事件的方式。
6.1 DOM2中事件模拟
可以在 document 对象上使用 createEvent()方法创建 event 对象。模拟事件的最后一步就是触发事件。这一步需要使用 dispatchEvent()方法,所有支持事件的DOM 节点都支持这个方法。例子:
var btn = document.getElementById("myBtn");
//创建事件对象
var event = document.createEvent("MouseEvents");
//初始化事件对象
event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0,
false, false, false, false, 0, null);
//触发事件
btn.dispatchEvent(event);
6.2 IE中事件模拟
调用 document.createEventObject()方法可以在 IE 中创建 event 对象。最后一步就是在目标上调用 fireEvent()方法,这个方法接受两个参数:事件处理程序的名称和 event 对象。例子:
var btn = document.getElementById("myBtn");
//创建事件对象
var event = document.createEventObject();
//初始化事件对象
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY = 0;
event.ctrlKey = false;
event.altKey = false;
event.shiftKey = false;
event.button = 0;
//触发事件
btn.fireEvent("onclick", event);