第13章 事件
1. 事件流
- 事件流:描述页面接受事件的顺序
- 事件冒泡流
- 事件由目标元素接受,向上传播至document(部分浏览器会到window)
- 事件捕获流
- 事件由document对象(规范要求,但浏览器还是从window对象开始捕获)接收,向下传播搭配目标
- DOM事件流的三个阶段
- 捕获阶段不接受事件(规范要求)
- 实际上主流浏览器都会在捕获阶段触发事件对象上的事件
2. 事件处理程序(事件侦听器)
-
概念
- 响应事件的函数就是事件处理程序
- 事件处理程序名字以on开头
- 为事件指定处理程序的方式:HTML事件、DOM0级事件、DOM2级事件
- 事件处理程序代码执行时有权访问全局作用域中的任何代码
-
HTML事件处理程序
-
使用HTML特性指定事件处理程序
-
<div onclick="alert('莫挨老子!')">点我</div>
-
也可调用脚本函数
-
可通过event变量直接访问事件对象
-
可通过this访问目标元素(若使用箭头函数,则this指向window)
function handler(e){ console.log(this == e.currentTarget); // true }
-
缺点
- 时差问题:执行函数未解析前触发事件,会报错。解决方案是使用try-catch封装事件处理程序
- 事件处理程序的作用域链在不同浏览器中可能导致不同结果
- HTML与JS紧密耦合,更换事件处理程序HTML与JS均需要改动
-
-
DOM0级事件处理程序
- 将函数赋值给事件处理程序属性
- 优势:简单,可跨浏览器
- 被认为是元素的方法,可通过this引用当前元素,并访问元素属性和方法
- 删除事件处理程序:属性值设为null即可
- 设置多次事件属性值,旧值会被新值覆盖
let d = document.getElementById("ddd"); d.onclick = function(){ console.log(this.id); // "ddd" }; d.onclick = null; 删除
-
DOM2级事件处理程序
- 处理指定和删除事件处理程序的操作addEventListener()和removeEventListener()
- 可用于所有DOM节点
- 接受3个参数:
- 事件名(不以“on”开头)
- 处理函数(不要是匿名函数)
- 是否捕获事件(true/false):默认是冒泡事件(考虑兼容性,不建议设置为true)
- addEventListener()
- 可以添加多个事件处理程序,按添加顺序依次触发执行
- removeEventListener()
- addEventListener()添加的事件只能用removeEventListener()移除
- 传入参数必须一致,因此,匿名函数无法移除
- 不建议捕获阶段注册事件
-
IE事件处理程序
- attachEvent()和detachEvent()
- 两个参数:事件名(on开头)、处理函数
- 作用域区别:this指向window
- 多个事件触发顺序与添加顺序相反
- attachEvent()和detachEvent()
-
跨浏览器的事件处理程序
- 默认使用DOM0级事件
- 默认使用DOM0级事件
-
小结
- HTML事件会被DOM0级事件覆盖
- DOM0级事件触发优先于DOM2级事件
- DOM2级事件可以添加多次事件处理程序
3. 事件对象
-
DOM中的事件对象
- DOM0与DOM2事件处理程序都会传入event对象(HTML事件也可传入event)
- 所有事件都有的属性与方法
- 事件处理程序内部this指向currentTarget(绑定事件的元素)的值
- target为触发事件的元素
- type属性可以用于一个函数处理多个事件(结合switch语句)
- eventPhase属性确定事件流处于哪个阶段,1表示捕获、2表示处于目标、3表示冒泡
- 只有cancelable为true时,才可取消默认行为
- preventDefault():阻止事件默认行为(如链接的跳转)
- stopPropagation():立即停止事件传播(冒泡或捕获)
-
IE中的事件对象
-
DOM0级事件的event对象作为window的属性存在
-
HTML特性指定事件处理程序则可通过event变量访问事件对象
-
使用attachEvent()时,event对象会作为参数传入事件处理函数
-
属性与方法
-
4. 事件类型
-
DOM3级事件分类
- UI事件:load、unload、resize、scroll等
- 焦点事件:blur、focus等
- 鼠标与滚轮事件
- 文本事件
- 键盘事件
- 合成事件
- 变动事件
-
UI事件
- load事件
- 可以用在window、image、或iframe
- 可以使用HTML特性、DOM0、DOM2三种方法指定事件处理程序
- unload事件
- 文档完全卸载后触发(前进或后退页面)
- 可用于清除引用,以避免内存泄漏
- resize事件
- 窗口最大化最小化也会触发
- 持续触发
- scroll事件
- 滚动时会重复触发
- load事件
-
焦点事件
- focus与blur事件不冒泡
-
鼠标事件
-
click:相继触发mousedown与mouseup才触发
-
dbclick:触发两次click
-
mousedown、mouseup
-
mouseenter/mouseleave
- 不冒泡
- 移到后代元素不触发
-
mouseout/mouseover:
- 指针由一个元素上方移出/入另一个元素(可以是外部元素也可以是子元素)
- 会冒泡
-
mousemove:移动会重复触发
-
鼠标事件包含以下属性
- clientX/clientY:鼠标指针在视口的坐标
- pageX/pageY:页面中的坐标(页面不滚动时与视口坐标相等)
- screenX/screenY:屏幕坐标
- shiftKey、ctrlKey、altKey、metaKey(win或cmd):布尔值,是否按下对应键
- relatedTarget:相关元素(IE中使用的是fromElement/toElelment)对应mouseover和mouseout
- button:鼠标按钮
-
-
滚轮事件
- onscroll:处理的是对象内容(页面)滚动事件
- onwheel:处理的是鼠标滑轮滚动事件,只要滚动滑轮就会触发,event对象有以下属性
- deltaX/Y/Z属性:3的倍数,表示滚动方向与距离
-
键盘与文本事件
-
keydown:按下任意键盘触发,按住不放持续触发
-
keyup:释放键盘时触发
- keyCode:键码
- charCode:始终为0
-
keypress:按下字符键盘触发,按住不放持续触发
- charCode:字符编码(同keycode)
- keycode:始终为0
- 按shift键会改变charcode值
- 会区分大小写
-
其它共有属性
- key:键盘字符串
- location:键位置(0默认键盘,1左侧,2右侧,3小键盘,4移动设备键盘或虚拟键盘,5手柄)
- 按下左右侧的
alt
、crtl
、shift
分别为1、2
- 按下左右侧的
-
按
shift
++
触发键盘事件顺序如下:
-
textInput:对keypress的补充,在可编辑区域输入字符时触发
-
-
复合事件
-
变动事件
- 删除节点(removeChild()、replaceChild())
- DOMNodeRemoved:冒泡,事件目标为被删除节点
- DOMNodeRemovedFromDocument:不冒泡,事件目标为被删除节点及其子节点(event不包含其它信息)
- DomSubtreeModified::冒泡,事件目标为被删除节点的父节点
- 插入节点(appendChild()、replaceChild()、insertBefore())
- DOMNodeInserted:冒泡,事件目标为被插入节点
- DOMNodeInsertedInDocument:不冒泡,事件目标为新插入的节点(event不包含其它信息)
- DomSubtreeModified::冒泡,事件目标为新插入节点的父节点
- 删除节点(removeChild()、replaceChild())
-
HTML5事件
-
contextmenu事件
- 鼠标右键调出上下文菜单
- 先使用event.preventDefault()方法阻止默认右键菜单栏,然后自定义右键菜单
let menu = document.getElementById("menu"); document.oncontextmenu= ()=>{ event.preventDefault(); menu.style.visibility = "visible"; } document.onmousemove=()=>{ menu.style.top = event.clientY + "px"; menu.style.left = event.clientX + "px"; } document.onclick=()=>{ menu.style.visibility = "hidden"; }
-
beforeunload事件(window对象)
- 浏览器卸载页面前触发,弹出提示框,控制权交给用户
- 无法彻底取消该事件(相当于无法离开页面)
-
-
DOMContentLoaded事件
- load事件:一切加载完毕时触发,可能由于资源过多而耗时
- DOMContentLoaded事件是形成完整的DOM树就会触发
- 不管image、js、css等文件及其他资源是否加载完毕
- 可以更早的与页面交互
- 目标(target)对象为document
-
hashchange事件(window对象)
- event额外包含两个属性:oldURL和newURL(完整的URL)
- AJAX应用中,利用hash值来保存状态或导航信息
-
设备事件
-
触摸与手势事件
5. 内存与性能
- 事件委托
- 事件处理程序过多时,利用事件冒泡来管理某一类型的所有事件
- 适合委托的事件:click、mousedown、mouseup、keyup、keydown、keypress
- 移除事件处理程序
- 产生不用的事件处理程序的情况:
- 从文档移除的带有事件处理程序的元素(使用innerHTML替换元素时)
- 卸载页面(刷新、关闭)
- 移除方法:将null赋值给事件处理程序
- 产生不用的事件处理程序的情况:
6. 模拟事件(DOM 3级事件)
- 模拟DOM事件
- 创建Event对象:
e = document.createEvent(eventType)
- 参数可选值:UIEvent | MouseEvent | KeyboardEvent |MutationEvent
- 初始化:
e.initxxx(options)
- 触发事件:
selectedElement.dispatchEvent(e)
- 创建Event对象:
- 模拟IE中的事件:createEventObject()