JavaScript
十三、事件
13.1 事件流
- 某个按钮被点击,点击事件不仅发生在按钮上,也发生在按钮的容器元素甚至整个页面
- 事件流描述的是从页面中接收事件的顺序。
13.1.1 事件冒泡
事件开始时由最具体的元素接收,然后逐级向上传播。
13.1.2 事件捕获
不太具体的节点更早接收到事件。较少使用
13.1.3 DOM事件流
包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
- 处于目标阶段中的事件也会被看成冒泡阶段的一部分
- DOM规定,在事件捕获阶段目标元素不会接收事件
- 多数浏览器在捕获阶段会触发事件对象,所以有两个机会在目标对象上操作时间
13.2 事件处理程序
响应某个事件的函数即为事件处理程序或事件侦听器。为事件指定处理程序的方式有多种。
13.2.1 HTML事件处理程序
使用HTML特性来指定,特性值可以是javascript代码或者在页面其他地方定义的脚本。
- 特点
- 通过HTML特性指定事件处理程序时,首先会根据特性值自动创建一个函数,且有一个局部变量event(事件对象),不需要定义或从函数的参数列表中读取就能直接使用
- 在函数内部,会自动拓展作用域
- 使用with拓展this,this指向事件的目标元素
- 如果当前元素是表单输入元素,使用with拓展form
- 最后拓展document
//自动根据特性值创建的函数大体结构,自动拓展作用域
function(){
with(document){
with(this.form){
with(this){
//执行特性值中的代码
}
}
}
}
<html>
<head>
<script>
var age=18;
</script>
</head>
<body>
<form>
<input type='text' value='Nicholas' name='PersonName'/>
<input type='text' onclick='alert(value);alert(PersonName.value);alert(age);' value='Software Engineer'/>
<!-- 依次输出18 Nicholas Software Engineer -->
</form>
</body>
</html>
- 缺点
- 当<script>元素放置在<body>元素内部的最末尾,当事件触发时,脚本块还未解析,访问其中的代码会报错
- 作用域链在不同浏览器中会导致不同结果
- HTML与javascript代码紧密耦合
13.2.2 DOM0 级事件处理程序
- 将函数赋给元素的事件处理程序属性
- 事件处理程序属性名
- 小写
- on + 事件名称
- 元素属性和HTML特性基本属于一个维度,给元素属性赋值会覆盖HTML事件特性
- 事件处理程序属性名
var button = document.getElementById("myBtn");
button.onclick=function(){
alert(this.id);
};
//将函数作用域绑定到button对象上
- 事件处理程序在冒泡阶段被处理
13.2.3 DOM2 级事件处理程序
- addEventListener()
- 传入参数
- 要处理的事件名,click
- 作为事件处理程序的函数
- bool
- true 在捕获阶段调用事件处理程序
- false 在冒泡阶段调用事件处理程序
- 针对同一个元素,可添加多个事件处理函数,按顺序执行
- 与元素事件属性互不干扰
- 传入参数
- removeEventListener()
- 传入参数和添加时相同
13.2.4 IE事件处理程序
- attachEvent()
- 冒泡阶段
- 在全局作用域中运行,this指向window
- 可为同一个事件添加多个处理程序,逆序触发。
- 接收参数
- 事件处理程序名称 onclick
- 事件处理程序函数
- detachEvent()
- 接收参数和添加时相同
13.2.5 跨浏览器的事件处理程序
- 考虑浏览器差异,选择合适的添加事件处理程序的方式
- 优先使用DOM2级事件处理程序
13.3 事件对象
event,包括导致事件的元素、事件类型以及其他与特定事件相关的信息,例如,鼠标事件对象包含鼠标位置等
13.3.1 DOM中的事件对象
事件是元素所特有的,而事件处理程序是人为附加给指定的元素,事件可以通过冒泡或捕获的方式传递到外层元素并触发其上的事件处理程序。
- event对象
- 可以直接在事件处理程序中使用,不需要读取函数参数列表
- 属性
- event.type 事件类型,click、mouseover等
- event.currentTarget
事件处理程序注册在什么元素上,即事件处理函数中的this - event.target
事件的实际目标,例如单击事件的目标是按钮 - event.eventPhase
事件当前位于事件流的哪个阶段- 捕获阶段——1
- 目标对象——2
- 冒泡阶段——3
- 方法
- event.preventDefault()
阻止特定事件的默认行为,例如链接的跳转等,当event对象的cancelable属性为true - event.stopPropagation()
阻止事件继续传递,可同时取消事件捕获和冒泡
- event.preventDefault()
13.3.2 IE中的事件对象
- 获取event对象
取决于指定事件处理程序的方式- DOM0 级
- 作为window的属性,即window.event
- DOM2 级和HTML
- 作为事件处理程序中的函数参数传入
- 也可通过window.event
- DOM0 级
- event对象属性
属性 | 类型 | 读写 | 说明 |
---|---|---|---|
cancelBubble | boolean | 读写 | 默认为false,为true时取消事件冒泡 |
returnValue | boolean | 读写 | 默认为true,为false可取消事件默认行为 |
srcElement | element | 只读 | 事件的目标,target |
type | string | 只读 | 事件类型,click、mouseover等 |
13.3.3 跨浏览器的事件对象
- 兼容IE的解决方案,先检查正常的DOM方式,若无则按IE处理
13.4 事件类型
13.4.1 UI事件
DOM规范出现之前存在的。
13.4.1.1 load事件
加载结束后触发
- 页面加载完毕
- 为window添加load事件处理程序
- 为body指定onload事件特性
- 元素加载完毕
- <img> 是设置src属性后开始下载的,与添加进DOM树的先后没有关系
- <script> 和 <link> 在加进DOM树之后才开始下载
13.4.1.2 unload事件
文档完全卸载后触发,例如从一个页面切换到另一个页面,可在事件中清除引用
13.4.1.3 resize事件
- window对象上发生
- 在页面大小改变时触发,可能会持续触发,所以在此事件处理程序中执行代码尽量简单
13.4.1.4 scroll事件
- window对象上发生
- 在文档滚动时触发,可能会持续触发,所以在此事件处理程序中执行代码尽量简单
13.4.2 焦点事件
- blur 失去焦点,不冒泡
- DOMFocusIn 获取焦点,冒泡,只有Opera支持
- DOMFocusOut 失去焦点,冒泡,只有Opera支持
- focus 获取焦点,不冒泡
- focusin 获取焦点,冒泡
- focusout 失去焦点,冒泡
- 焦点从一个元素移动到另一个元素,会依次触发下列事件
- focusout
- focusin
- blur
- DomFocusOut
- focus
- DOMFocusIn
13.4.3 鼠标与滚轮事件
- click
- 回车键和鼠标主键(左键)都可以触发
- 只有在同一个元素上相继触发mousedown和mouseup事件才会触发
- dbclick
- 双击主鼠标按钮
- 只有在同一个元素上连续触发两次click事件才会触发
- mousedown
- mouseenter 移入,不冒泡
- mouseleave 移出,不冒泡
- mousemove
- mouseout 移出
- mouseover 移入
- mouseup
13.4.3.1 客户区坐标位置
鼠标指针在视窗中的位置
- event.clientX
- event.clientY
13.4.3.2 页面坐标位置
- event.pageX
- event.pageY
13.4.3.3 屏幕坐标位置
鼠标指针在电脑屏幕中的位置
- event.screenX
- event.screenY
13.4.3.4 修改键
shift、ctrl、alt、meta键是否按下,返回bool值
- event.shiftKey
- event.ctrlKey
- event.altKey
- event.metaKey windows键
13.4.3.5 相关元素
- event.relatedTarget
- 对于mouseout事件,相关元素为获得光标的元素
- 对于mouseover事件,相关元素为失去光标的元素
13.4.3.6 鼠标按钮
- event.button
- mousedown、mouseup事件支持且有意义
- 主鼠标按钮——0
- 中间按钮——1
- 次鼠标按钮——2
13.4.3.7 更多的事件信息
- event.detail
- 在给定位置上发生多少次单击
- 移动后会重新计数
13.4.3.8 鼠标滚轮事件
- mousewheel 可以在任意元素上触发
- event.wheelDelta
- 向上滚动为正
- 数值为120的倍数
13.4.3.9 触摸设备
- 不支持dblclick事件
- 轻击可单击元素会触发mousemove事件
- 可单击元素指单击可产生默认操作或已指定onclick事件处理程序的元素
- 如果单击导致内容变化,将不再有其他时事件发生
- 如果没有变化,则会触发mousedown、mouseup和click事件
- mousemove会触发mouseover和mouseout事件
- 两个手指在屏幕上且页面随手指移动会触发mousewheel和scroll事件
13.4.3.10 无障碍性问题
- 残疾人主要通过键盘输入,只能触发click事件
13.4.4 键盘与文本事件
- keydown 任意键触发,且在文本框发生变化之前
- keypress 字符键触发,包括退格键,且在文本框发生变化之前
- keyup
13.4.4.1 键码
- event.keyCode
- keydown和keyup事件中存在
- 数字、字母
- 对应的小写字母或数字的ascii码
- 非字符
- 查表
13.4.4.2 字符编码
- event.charCode
- keypress事件中存在
13.4.4.3 DOM3级变化
- 不再包含charCode
- event.key
- 字符键
- 文本字符
- 非字符键
- 按键名称,如"Shift"等
- 字符键
- event.char
- 字符键
- 文本字符
- 非字符键
- null
- 字符键
- event.location 按下了什么位置的键
- 默认键盘——0
- 左侧位置——1
- 右侧位置——2
- 数字小键盘——3
- 移动设备键盘(虚拟键盘)——4
- 手柄——5
- event.getModifierState(keyName)
- 判断指定的修改键是否被按下
13.4.4.4 textInput事件
- 与keypress的区别
- keypress
- 任何获得焦点的元素都可以触发
- 退格键也会触发
- textInput
- 可编辑区域才能触发
- 实际字符键才能触发
- keypress
- event.data
- 输入实际字符,能反映大小写的区别
- event.inputMethod
- 把文本输入到文本框的方式
- 键盘输入——1
- 粘贴——2
- …
13.4.4.5 设备中的键盘事件
13.4.5 复合事件
- 虚拟键盘输入特殊字符
- compositionstart 开启虚拟键盘时触发
- compositionupdate 通过虚拟键盘输入字符时触发
- compositionend 关闭虚拟键盘时触发
- event.data
- compositionstart
- 该事件触发时访问data属性返回正在编辑的文本,例如已经选中的需要马上替换的文本
- compostionupdate
- 返回正插入的新字符
- compositionend
- 返回此次会话中插入的所有字符
- compositionstart
13.4.6 变动事件
- DOMSubtreeModified
- DOM树发生任何变化时触发
- DOMNodeInserted 冒泡
- DOMNodeRemoved
- DOMNodeInsertedIntoDocument
- 文档有节点插入时触发,在DOMNodeInserted之后
- DOMNodeRemovedFromDocument
- DOMAttrModified
- 特性被修改时触发
- DOMCharacterDataModified
- 文本节点的值发生变化时触发
13.4.6.1 删除节点
- DOMNodeRemoved触发时节点还未被删除
- event.relatedNode指向被删除节点的父节点
- 子节点会触发DOMNodeRemovedFromDocument事件,但该事件不会冒泡
- 父节点会触发DOMSubtreeModified
13.4.6.2 插入节点
- DOMNodeInserted触发时节点已经被插入
- 其他与删除节点类似
13.4.7 HTML5事件
13.4.7.1 contextmenu
- 右键菜单事件
- 作用
- 如何确定应该显示上下文菜单
- 如何屏蔽与该操作关联的默认上下文菜单
- 冒泡
- 作用
<html>
<head>
<script>
window.onload=function(){
var menu = document.getElementsByTagName("ul")[0];
document.oncontextmenu=function(){
event.preventDefault();
menu.style.left=event.clientX+"px";
menu.style.top=event.clientY+"px";
menu.style.visibility="visible";
};
document.onclick=function(){
menu.style.visibility="hidden";
};
};
</script>
</head>
<body>
<ul style='visibility:hidden;position:absolute;'>
<li>eee</li>
<li>fff</li>
<li>ddd</li>
</ul>
</body>
</html>
13.4.7.2 beforeunload事件
- 页面卸载之前弹出提示框
13.4.7.3 DOMContentLoaded事件
- 在形成完整的DOM树之后触发,不必所有资源全部下载完后触发
- 可以用0毫秒超时调用起到相似的作用,但不能保证
- 添加到任务队列中
- 浏览器执行html解析
- 解析完从任务队列中读取执行
13.4.7.4 readystatechange事件
- 支持该事件的元素具有readyState属性,属性值为
- uninitialized
- loading
- loaded
- interactive
- complete
- 状态的顺序不一定保证,且不一定每个状态都存在
13.4.7.5 pageshow和pagehide事件
- 往返缓存
- 前进后退按钮不再重新请求页面,而是从往返缓存中读取
- 不会触发onload事件
- pageshow事件在页面显示时触发,无论是否是从往返缓存中读取
- event.persisted
- 表示是否是从往返缓存中加载页面
- event.persisted
- 指定了onunload事件处理程序的页面不会进入往返缓存中
- onunload事件一般是撤销操作,而往返缓存保存和调用的是所有页面数据
- 执行完onunload事件后,在通过回退按钮返回,部分对象可能不存在了
13.4.7.6 hashchange事件
- 在URL参数列表发生变化时触发
- event.oldURL
- event.newURL
13.4.8 设备事件
13.4.8.1 orientationchange事件
- IOS设备调整方向时触发
- window.orientation属性记录设备方向
- 0——肖像模式
- 90——按钮在右侧
- -90——按钮在左侧
- 180——设备头朝下
13.4.8.2 MozOrientation事件
- FireFox设备调整方向时触发
- event.x
- event.y
- event.z
13.4.8.3 deviceorientation事件
- event
- alpha
- beta
- gamma
- absolute
- compassCalibrated
13.4.8.4 devicemotion事件
- 设备运动信息
- event
- acceleration
- accelerationIncludingGravity
- interval
- rotationRate
13.4.9 触摸与手势事件
13.4.9.1 触摸事件
- touchstart 手指触摸屏幕时触发,即使已经有一个手指放在屏幕上
- touchmove 手指在屏幕上移动
- touchend 手指离开屏幕
- touchcancel
13.4.9.2 手势事件
- gesturestart 两值触摸时触发
- gesturechange 两指任意一个移动时触发
- gestureend 两指任意一个离开时触发
- event
- rotation 旋转
- scale 放大缩小
13.5 内存和性能
- 事件处理程序越多,函数越多,占用的内存越多
- 指定事件处理程序时需要访问DOM,延迟页面交互就绪时间
13.5.1 事件委托
- 利用事件冒泡的特点,在父元素上添加事件处理程序
- 通过识别元素ID等方式进行针对性的处理
- 例如在document上添加事件处理程序
13.5.2 移除事件处理程序
- 从文档中移除带有事件处理程序的元素前删除事件处理程序
- 在卸载页面前移除事件处理程序
13.6 模拟事件
13.6.1 DOM中的事件模拟
- 创建event对象
- document.createEvent()
- 传入参数
- UIEvents
- MouseEvents
- MutationEvents
- HTMLEvents
- 传入参数
- document.createEvent()
- 初始化event对象
- 每种类型的event都有一个特殊的方法进行初始化
- 触发事件
- element.dispatchEvent(event)
13.6.1.1 模拟鼠标事件
- 初始化方法
- event.initMouseEvent()
- 接收参数依次为
- type 字符串,例如"click"
- bubbles 布尔值
- cancelable 布尔值
- view document.defaultView
- 接收参数依次为
- event.initMouseEvent()
- 在对象上调用dispatchEvent()方法后会自动为event对象设置target属性
13.6.1.2 模拟键盘事件
- DOM3级KeyboardEvent参数创建event对象
- 初始化方法
- event.initKeyEvent()
13.6.1.3 模拟其他事件
- 变动事件
- MutationEvents
- initMutationEvent()
- HTML事件
- HTMLEvents
- initEvent()
13.6.1.4 自定义DOM事件
- CustomEvent参数创建event对象
- initCustomEvent()
- type
- 可随意指定,不需要是常规事件
- bubbles
- cancelable
- detail
- type
13.6.2 IE中的事件模拟
- docuemnt.createEventObject()
- 不接受参数
- 初始化event对象
- 为event指定属性
- element.fireEvent()
- 接收参数
- 事件处理程序的名称
- event对象
- 接收参数