事件流
事件流描述了页面接收事件的顺序。
graph LR A(IE) --从内而外--> B(事件冒泡流) C(Netscape Communicator) --从外而内--> D(事件捕获流) D --> 得到所有浏览器的支持DOM2 Event规范规定事件流分为3个阶段:事件捕获、到达目标和事件冒泡
事件捕获和事件冒泡本质上是差不多的,我本身没有事件,我可以向内(子级元素)或向外(父级元素)寻找事件作为触发的事件。
事件处理程序
指定事件处理程序的方式如下
HTML事件处理程序
使用事件处理程序的名字以HTML属性的形式指定
<input type="button" value="Click Me" onclick="console.log(event.type)">
DOM0事件处理程序
把一个函数赋值给DOM元素的一个事件处理程序
let btn=document.getElementById('mybtn');
btn.onclick() = () => {
...
};
移除事件
btn.onclick=null;
DOM2事件处理程序
addEventListener(eventType,handler,flag)
graph LR flag --> true:在捕获阶段调用handler flag --> false:默认,在冒泡阶段调用handlerremoveEventListener(eventType,handler,flag)
IE事件处理程序
attachEvent(eventType,handler)和detachEvent(eventType,handler)
使用attachEvent和使用DOM0方式的主要区别:attachEvent里的this值是window,而使用DOM0方式的事件处理程序里的this值是目标元素
跨浏览器事件处理程序
自行编写跨浏览器兼容的事件处理程序:addHandler(element,eventType,handler)和
removeHandler(element,eventType,handler)
const EventUtil={
addHandler(element,eventType,handler){
if(element.addEventListener){
element.addEventListener(type,handler,false);
}else if(element.attachEvent){
element.attachEvent('on'+type,handler);
}else{
element['on'+type]=handler;
}
},
removeHandler(element,eventType,handler){
if(element.removeEventListener){
element.removeEventListener(type,handler,false);
}else if(element.detachEvent){
element.detachEvent('on'+type,handler);
}else{
element['on'+type]=null;
}
}
}
事件对象
事件的所有信息被存储在一个名为 event 的对象中
DOM事件对象
不管以哪种方式指定事件处理程序,event对象是传给事件处理程序的唯一参数(如果要传参的话)
event属性:
属性/方法 | 类型 | 读/写 | 说明 |
---|---|---|---|
type | 字符串 | 只读 | 被触发的事件类型 |
currentTarget | 元素 | 只读 | 当前事件处理程序所在元素 |
target | 元素 | 只读 | 事件目标 |
在事件处理程序内部,this等于currentTarget,而target是事件的实际目标
IE事件对象
IE事件对象基于事件处理程序被指定的方式以不同方式来访问
- 使用DOM0方式指定,则 event 是window对象的一个属性
- 使用attachEvent()指定,则event作为唯一参数传给事件处理程序
跨浏览器事件对象
事件类型
DOM3 Event定义了如下事件类型:
- 用户界面事件:涉及与BOM交互的通用浏览器事件
- 焦点事件
- 鼠标事件
- 滚轮事件
- 输入事件
- 键盘事件
- 合成事件:在使用某种IME(Input Method Editor,输入法编辑器)输入字符时触发
用户界面事件
焦点事件
鼠标和滚轮事件
键盘与输入事件
合成事件
变化事件
HTML5事件
设备事件
触摸及手势事件
内存与性能
创建GUI的语言如C#给每一个按钮都设置了onclick事件处理程序,这样不会有什么性能损耗
但是JS不同:
- 每一个函数都是对象,都占用内存空间
- 为指定事件处理程序而访问DOM次数过多会造成整个页面交互延迟
事件委托
"过多事件处理程序"的解决办法就是事件委托
事件委托利用事件冒泡,只使用一个事件处理程序来处理一个类型的事件,这一个事件处理程序应位于祖先节点上,
删除事件处理程序
导致事件处理程序常驻内存的两个场景:
- 删除了带有事件处理程序的元素,而事件处理程序还在内存。最好在删除元素前,将其带有的事件处理程序一并删除( 置null );
- 第二场景就是页面卸载(前进、后退和刷新)。最好在onload事件处理程序中趁页面未卸载先删除其他所有事件处理程序
模拟事件
可以通过JS在任何时候触发任意事件,这些事件被当作浏览器创建的事件
DOM模拟事件
document.createEvent(eventType):创建一个event对象
在DOM2里,eventType都是英文复数形式;在DOM3里又改为了单数形式
eventType可取值:
- "UIEvents":通用用户事件(鼠标事件和键盘事件都继承自这个事件)
- "MouseEvents":通用鼠标事件
- "HTMLEvents":通用HTML事件
事件模拟的最后一步是触发事件,为此调用dispatchEvent(event)方法,触发模拟事件