13.1 事件流
13.1.1 冒泡事件(现在浏览器都是默认为冒泡事件)
事件由文档中嵌层次最深的那个节点接收,然后逐级向上传播,一直到 document/window 节点
13.1.2 事件捕获
事件由最外层节点(document/window)接收一直往内传播,直到最具体的节点
13.1.
2 DOM 事件流
“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段
13.2 事件处理程序
事件就是用户或浏览器自身执行的某种动作,如 click、mouseover、load,都是事件的名字。
而响应某个事件的函数就叫事件处理程序(或叫事件侦听器)。事件处理程序的名字以“on”开头。
13.2.1 HTML 事件处理程序
<script>
function showMessage() {
alert('Hello World!')
}
</script>
<input type="button" value="Click Me" onclick="showMessage()">
这样指定的事件处理会创建一个封装着元素属性值的函数,这个函数中有一个局部变量 event,也就是事件对象
通过 event 变量,可以直接访问事件对象。
并且在这个函数中,this 等于事件的目标元素(绑定此事件的元素)【是在element的事件处理程序中访问的this,可以把this传递给对应函数】
<script>
function showMessage(cur) {
console.log(cur) // 就是那个button
alert('Hello World!')
}
</script>
<input type="button" value="Click Me" onclick="showMessage(this)">
在这个函数内部会扩展它的作用域,可以像局部变量一样访问 document 及元素本身的成员
<input type="button" value="Click Me" onclick="alert(value, getElementById('myDiv'))">
这个函数使用 with 就像下面这样扩展作用域
function () {
with (document) {
witdh (this) {
// 元素值
}
}
}
如果当前元素是一个表单输入元素,则作用域还会包含表单元素的入口
function () {
with (document) {
witdh (this.form) {
with (this)
// 元素值
}
}
}
<form action="">
<input type="button" value="Click Me" onclick="showMessage(userName.value)">
<input type="text" name="userName">
</form>
13.2.2 DOM0 级事件处理程序
const btn = document.getElementById('btn')
// 绑定事件
btn.onclick = function () {
console.log(666);
}
// 解绑事件
btn.onclick = null
13.2.3 DOM2 级事件处理程序
通过 addEventListener 绑定事件 和 removeEventListenter 解绑事件。
第一个参数为事件名
第二个参数为函数
第三个参数为是否在捕获阶段调用此函数,默认值为 false
如果想事件 removeEventListenter 解绑事件。则不能事件匿名函数绑定事件
const btn = document.getElementById('btn')
// 绑定事件
btn.addEventListener('click', function () {
console.log(666);
}, false)
// 解绑事件
btn.removeEventListener()
13.3 事件对象
13.3.1 DOM 中的事件对象
event 对象包含与创建它的特定事件有关,不过所有事件都会有以下成员


其中 currentTarget 始终等于 this
13.4 事件类型
13.4.1 UI 事件
-
onload:当页面完全加载时触发(包括图片,css文件等)执行的函数
以下 HTML 标签支持 onload :
<body>, <frame>, <frameset>, <iframe>, <img>, <input type="image">, <link>, <script>, <style>
-
unload:当页面完全卸载后在window上触发
-
error:当发送 JavaScript 错误的时候在 window上触发,无法加载图片时在 img 上触发,无法加载嵌入内容时在<object>上触发。
-
select:当用户选择文本框(input 或 textarea)中的一个或多个字符触发。
-
resize:当窗口大小发生改变时在 window上触发。
-
scroll:当用户滚动带滚动条元素中的内容的时候,在该元素上触发
13.4.2 焦点事件
-
blur:在元素失去焦点时触发,不会冒泡,全浏览器支持。
-
focusout:与 blur 等价,但是它会冒泡。
-
focus:在元素获得焦点的时候触发,不会冒泡,全浏览器支持。
-
focusin:与 focus 等价,但是它会冒泡。
13.4.3 鼠标与滚轮事件
-
click:单击
-
dbclick:双击
-
mousedown:按下鼠标任意键触发
-
mouseup:在用户释放鼠标时触发
-
mouseenter:鼠标 首次移动到元素范围内触发,不冒泡
-
mouseleave:鼠标 首次移出到元素范围外触发,不冒泡
-
mousemove:鼠标指针在元素内部移动 重复触发
-
mouseout:鼠标移除元素或移到元素的子元素上触发(在子元素上会连续触发)
-
mouseover:鼠标位于元素外部, 首次移入元素内触发
以下是 mouseenter 和 mouseover 区别(主要是是否支持冒泡决定了触发时机),mouseout 和 mouse leave 同理

1、客户端坐标位置:clientX 和 clientY
可以使用鼠标事件来获取坐标信息
2、页面坐标位置:pageX 和 pageY
表示鼠标光标在页面中的位置。在页面没有滚动条的情况下,pageX 和 pageY 与 clientX 和 clientY 相等
3、屏幕坐标位置:screenX 和 screenY
鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。
4、修改键
当鼠标按下时同时按下以下修改键(Shift、Ctrl、Alt、Meta)时,事件对象中的 shiftKey、ctrlKeys、altKeys、metaKeys会变为true,否则为 false(这些属性值为布尔值)。可以通过这几个属性确定用户是否同时按下了以这几个键:
const box = document.getElementById('box')
box.addEventListener('click', function (ev) {
let keys = []
if (ev.shiftKey)
keys.push('shift')
if (ev.ctrlKey)
keys.push('ctrl')
if (ev.altKey)
keys.push('alt')
if (ev.metaKey)
keys.push('meta')
console.log(keys);
})
5、相关元素
6、鼠标按钮
当鼠标被按下时(mousedown事件)可以通过 button 属性来判断按了哪个键
-
0:鼠标左键
-
1:滚轮键
-
2:鼠标右键
7、更多事件
event 对象中还提供了 detail 属性。表示在同一个像素上发生了多少次点击。及同一个像素相继触发 mousedown 和 mouseup 事件算一次单击。detail 属性从 1 开始计算,如果移动了像素,则会被重置为 0.
8、鼠标滚轮事件
mousewheel事件通过 event.wheelDelta 判断向上或向下滚动
-
向上滚动(正数)
-
向下滚动(负数),
-
支持浏览器:IE、Chrome、Opera、Safari
-
其中 Opera 9.5 之前的版本,正负号是颠倒的
DOMMouseScroll 事件 通过 event.detail 判断向上或向下滚动
-
向上滚动(负数)
-
向下滚动(正数)
-
支持浏览器: Firefox
13.4.4 键盘与文本事件
-
keydown:当用户按下任意键触发,如果按下不放,会重复触发此事件。
-
keypress:当用户按下字符键,如果按下不放,会重复触发此事件。
-
keyup:当用户释放键盘上的字符键的时候触发。
13.4.5 复合事件
-
keydown:当用
13.4.6 变动事件
-
DOMSubtreeModifed:在 DOM 结构中发生任何变化时触发。这个事件在其他任何操作 DOM 元素事件后都会触发。
-
DOMNodeRemoved:在节点从父节点中被移除时触发。(冒泡)
-
DOMNodeRemovedFromDocument:在一个节点被直接从文档移除或通过子树间接从文档移除之前触发。这个事件在 DOMNodeRemoved 之后触发。(不冒泡)
-
DOMNodeInserted:在一个节点作为子节点被插入到另一个节点中时触发。
-
DOMNodeInsertedFromDocument:在一个节点被直接从插入文档或通过子树间接插入文档之前触发。这个事件在 DOMNodeInserted 之后触发。(不冒泡)
1.删除节点
-
在使用 removeChild() 或 replaceChild() 从 DOM 中删除节点时,首先会触发 DOMNodeRemoved 事件。
-
这个事件的目标(event.target)是被删除的节点,而 event.relatedNode 属性中包含着对目标节点父节点的引用。
-
在这个事件触发时,节点尚未从其父节点中移除,因此其 parentNode 仍然指向父节点(与 event.relatedNode)相同。
-
这个事件会冒泡,所以可以在 DOM中的任何层次上处理它。
-
如果插入的节点包含子节点,那么在其所有子节点以及这个被插入的节点上会相继触发 DOMNodeRemovedFromDocument 事件。这个事件不会冒泡,所以只有直接指定给其中一个子节点的事件处理程序才能被调用。
-
然后会触发 DOMSubTreeModified 事件。这个事件目标是被移除节点的父节点。
2.插入节点
-
在使用 appendChild()、replaceChild 或 insertBefore() 从 DOM 中插入节点时,首先会触发 DOMNodeInsert 事件。
-
这个事件的目标(event.target)是被插入的节点,而 event.relatedNode 属性中包含着对目标节点父节点的引用。
-
在这个事件触发时,节点已经被插入到新的父节点中。
-
这个事件会冒泡,所以可以在 DOM中的任何层次上处理它。
-
紧接着会在新插入的节点上触发 DOMNodeInsertedFromDocument 事件。这个事件不会冒泡,所以只有在插入节点之前为它添加这个事件才能被触发。
-
然后会触发 DOMSubTreeModified 事件。这个事件目标是插入节点的新父节点。
13.4.7 HTML5事件
-
contextmenu 事件:右键出现上下文菜单
-
beforeupload 事件:在页面卸载之前触发。可以通过它来取消卸载页面并继续使用原有页面
-
DOMContentLoaded 事件:在形成完整DOM树之后就会触发,不理会图像、JS文件、CSS文件和其他资源是否已经下载完毕。
-
readystatechange 事件:每个 readystatechange 事件都有一个 document.readyState 属性,可能包含下列 5 个值的中的一个。
-
uninitialized(未初始化):对象存在但为初始化
-
loading(正在加载):对象正在加载数据
-
loaded(加载完毕):DOM对象加载数据完成
-
interactive(交互):DOM对象已经加载解析完毕,可以触发事件,但是图片、CSS、JS 未加载完。等于 DOMCOntentLoaded 事件
-
complete(完成):对象已经完全加载完毕。等于 load 事件
-
PS:交互阶段不能保证在完成阶段之前触发,为了尽早的触发,有必要同时检测交互和完成阶段
-
document.addEventListener('readystatechange', function() {
if (document.readyState === 'interactive' || document.readyState === 'complete') {
document.removeEventListener('readystatechange', arguments.callee)
console.log('Content loaded');
}
})
-
pageshow 事件:有些浏览器有“往返缓存”的概念,即back-forward cache,或叫 bfcache,会保存浏览器“后退”和“前进”的页面在内存中。如果开启了这个功能那么用户在使用“前进”和“后退”进入页面时不会触发 onload 事件,只会触发 pageshow 事件。
-
可以通过 pageshow 的 event.persisted 来判断当前页面是否是从bfcache中加载的,是的话为 true,否则为 false
-
-
pagehide:pagehide 会在 unload 事件之前触发。
-
pagehide 事件 也有一个 event.persisted 属性来判断页面卸载之后是否会保存在 bfcache 之中。
-
hashChange 事件:当 hash 值发生改变的时候触发。有 event.oldURL 和 event.newURL 两个参数存储着旧的和新的 hash 值,但是兼容性不好,建议直接使用 location.hash。
-
13.4.9 触摸与手势事件
1.触摸事件
-
touchstart:当手指触摸屏幕时触发。
-
touchmove:当手指在屏幕滑动时连续触发。
-
touchend:当手指在屏幕上移开时触发。
-
touchancel:当系统停止跟踪触摸时触发。
-
PS:上面这几个事件都会冒泡,也都可以取消、都提供了常见的鼠标属性
除了常见的 DOM 属性之外。触摸事件还包含下列三个用于跟踪触摸的属性
-
touches:表示当前跟踪的触摸操作 Touch 对象的数组
-
targetTouches:特定于事件目标的 Touch 对象数组
-
changeTouchs:表示自上次触摸依赖发生了什么改变的 Touch 对象的数组
-
clientX:触摸目标在视口中的 X 坐标
-
clientY:触摸目标在视口中的 Y 坐标
-
identifier:标识触摸的唯一 ID
-
pageX/Y:在页面的 X/Y 坐标
-
screenX/Y:在屏幕的 X/Y 坐标
-
target:目标 DOM 节点
在触摸屏幕上的元素时,这些事件(包括鼠标事件)发生的顺序如下:
-
touchstart
-
mouseover
-
mousemove
-
mousedown
-
mouseup
-
click
-
touchend
2.手势事件(IOS支持)
-
gesturestart
-
gesturechange
-
gestureend
13.5 内存和性能
在 JS 中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能
13.5.1 事件委托
13.5.2 移除事件处理程序
-
如果知道某个元素即将被移除,那么最好手工移除事件处理程序
-
在页面卸载前,把所有事件卸载
13.6 模拟事件
13.6.1 DOM 中的事件模拟
可以在 document 对象上使用 createEvenet() 方法创建 event 对象。这个方法接收一个参数,即表示要创建的事件类型的字符串。
-
UIEvents:一般化的 UI 事件。鼠标事件和键盘事件都是继承自 UI 事件。DOM3 级中是 UIEvent。
-
MouseEvents:一般化的鼠标事件。DOM3级中是 MouseEvent。
-
MutationEvents:一般化的 DOM 变动事件。DOM3级中是 MutationEvent。
-
HTMLEvents:一般化的 HTML 事件。没有对应的 DOM3 级事件(HTML事件被分散到其他类别中)
1.模拟鼠标事件
通过 event.createEvent('MouseEvents'),返回的对象有一个名为 initMouseEvent() 方法。用于指定与该鼠标事件相关的信息。有15个参数如下:
-
type(字符串):要出发的事件类型,如'click'
-
bubbles(布尔值):表示事件是否应该支持冒泡。
-
cancelable(布尔值):表示事件是否可以取消
-
view:与事件关联的视图。这个参数几乎总是要设置为 document.defaultView
-
detail(整数):
-
.... P406
let btn = document.getElementById('myBtn')
let event = document.createEvent('MouseEvents')
btn.addEventListener('click', function () {
console.log('你点击了按钮');
})
event.initMouseEvent('click', true, true, document.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
btn.dispatchEvent(event)
2.模拟键盘事件
3.模拟其他事件
4.自定义DOM事件