一、什么是事件
1. 定义
在js中事件通常是指发生某种于Dom元素、document对象、window对象 有关的预定义或自定义的时刻或契机,这些契机通常是预定义的并且程序上依赖于它们,以关联这些契机发生时候的的功能发生代码。
2. 相关基本概念
(1)事件类型
字符串,用来表示事件类型
1 | <div onclick="">来点我</div> |
(2)事件目标
指事件发生在哪个对象上(dom元素、document对象、window对象)。
(3)事件处理程序
当事件发生时,执行的函数
1 | <div onclick="console.log('来点我')">来点我</div> |
(4)事件对象
事件发生时,事件本身包含的信息的对象(event对象)
如:位置信息,具体操作,事件对象等
1 | <body> |
(5)事件流
浏览器中事件传播的机制:
[1]事件冒泡:
从具体节点到不具体节点,接受事件
[2]事件捕获:
从不具体节点到具体节点,接受事件
在事件达到节点之前,捕获事件,性能更好,移动设备上touch事件用捕获实现
ps: 规范中定义事件从document开始传播,但实际浏览器是从windows开始捕获(为了性能)
[3]IE事件流 (基于事件冒泡,并且他们不基于W3C规范)
EventTarget.attachEvent(type, listener)
- this指向window
- 执行顺序相反
- detachEventEvent删除必须是同一引用
- EventTarget.detachEventEvent(type, listener)
分两个阶段
(1)事件目标
(2)事件冒泡
[4]dom2级别事件流(现代浏览器):
添加事件处理程序
分三个阶段
(1)事件捕获 (规范不设计涉及事件目标,但是实现里会触发事件对象)
(2)事件目标
(3)事件冒泡 (为了IE增加了事件冒泡)
3.事件触发
(1) html事件
称为html内联属性或html事件
[1] 如何触发
在dom元素上on + 事件名称 = “需要执行的函数” (需要括号,因为是执行函数)
1 | <button onclick="getEvent(event)">html事件</button> |
例子:
1 | <body> |
[2] Html事件使用注意事项
- Html不支持字符 需要转义
- 可以访问全局作用域下所有函数
- 有event 事件对象
- this的指针指向当前是触发事件的dom元素
1 | <body> |
- 可以通过setattribute 动态修改、移除事件
1 | <body> |
[3] Html事件的缺点
- 使用会存在时间差 写在body最后 try catch避免
- html事件作用域,全局作用域访问限定对象会出问题
- 代码耦合问题(需要修改两个地方)
(2) 属性事件(dom 0事件)
[1] 添加事件
获取dom元素,将on + 事件名称的属性赋予相关函数
1 | <body> |
[2] 删除事件
1 | <body> |
[3] 节点复制
事件会被复制,复制出来的事件不是指向同一个引用
[4]属性事件使用注意事项
增加事件 事件处理程序(小写)
1
El.onclick
支持Event对象
This指向当前dom
1
2
3function htmlEvent() {
console.log(this); // 指向当前dom
}
解决问题
- 解决了时间差问题
- 降低了代码耦合性
缺点
一次只能指定一个事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<body>
<button id="test">属性事件</button>
</body>
<script>
var test = document.getElementById('test');
test.onclick = hello;
test.onclick = world;
test.onclick = helloWolrd;
function hello() {
console.log('hello');
}
// world 会覆盖 world
function world() {
console.log('world');
}
// 必须放一个fp
function helloWolrd() {
console.log('hello');
console.log('world');
}
</script>
(3) 事件监听回调 (dom2)
注意事件类型没有on
1 | <body> |
p.s: 没有dom1没有做事件相关规定
[1]绑定事件与删除事件
1 | target.addEventListener(type, listener, options); |
type 表示监听事件类型的字符串。
listener
当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数
1
2
3
4
5
6
7var btn = document.getElementById('button');
btn.addEventListener('click',handleEvent);
function handleEvent() {
console.log(this); // 指向当前dom
}options 可选
一个指定有关 listener 属性的可选参数对象。可用的选项如下:capture: Boolean,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
效果同useCapture可参考
1
2
3
4
5
6
7
8
9var btn = document.getElementById('button');
var container = document.getElementById('container');
btn.addEventListener('click',function() {
console.log('button'); // 后触发
});
container.addEventListener('click',function() {
console.log('container'); // 先触发
},{ capture: true });
- once: Boolean,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。
1
2
3
4
var btn = document.getElementById('button');
btn.addEventListener('click',function() {
console.log('button'); // 只执行一次
},{once: true});
- passive: Boolean,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
mozSystemGroup: 只能在 XBL 或者是 Firefox' chrome 使用,这是个 Boolean,表示 listener 被添加到 system group。
关于passive可以参考:[https://zhuanlan.zhihu.com/p/24555031](****)
具体之后文章会具体聊聊这个问题
- true的话浏览器会开2个线程为了提高性能,
- 永远不会调用 preventDefault()
- 同一个dom对象,只要同一事件一个设置了passivefasle,浏览器都不会做性能优化,
- cancelable 为true都可以支持
option支持的安全检测例子:
1 | var passiveSupported = false; |
- useCapture 可选
Boolean,是指在DOM树中,注册了该listener的元素,是否会先于它下方的任何事件目标,接收到该事件。沿着DOM树向上冒泡的事件不会触发被指定为use capture(也就是设为true)的listener。当一个元素嵌套了另一个元素,两个元素都对同一个事件注册了一个处理函数时,所发生的事件冒泡和事件捕获是两种不同的事件传播方式。事件传播模式决定了元素以哪个顺序接收事件。进一步的解释可以查看 事件流 及 JavaScript Event order 文档。 如果没有指定, useCapture 默认为 false 。
例子:父容器元素与子容器元素都为false:
1 | var btn = document.getElementById('button'); |
例子:父容器元素与子容器元素都为true:
1 | var btn = document.getElementById('button'); |
例子:父容器元素为true与子容器元素false:
1 | var btn = document.getElementById('button'); |
例子:父容器元素为false与子容器元素true:
1 | var btn = document.getElementById('button'); |
注意事项:
- 事件类别没有on 如click keydown
- this指向window
- useCapture默认是fasle表示冒泡,true表示捕获
wantsUntrusted (only Gecko)
如果为 true , 则事件处理程序会接收网页自定义的事件。此参数只适用于 Gecko,主要用于附加组件的代码和浏览器本身。请见 Interaction between privileged and non-privileged pages.
在使用 options 对象中的特定值之前,最好确保用户的浏览器支持它,因为他并没有被所有浏览器所支持。查看更多细节关于option支持的安全检测。删除事件处理程序
1 | target.removeEventListener(type, listener[, useCapture]) |
- type:一个字符串,表示需要移除的事件类型,如 “click”。
- listener: 需要移除的 EventListener 函数(先前使用 addEventListener 方法定义的)
- useCapture 可选
指定需要移除的 EventListener 函数是否为事件捕获。如果无此参数,默认值为 false。
注意事项
- 用EventTarget.addEventListener指定,只能用EventTarget.removeEventListener删除
- 如果同一个监听事件分别为“事件捕获”和“事件冒泡”注册了一次,一共两次,这两次事件需要分别移除。两者不会互相干扰。
1 | <body> |
- 删除的函数,必须与添加时函数同一引用
1 | var div = document.getElementById('div'); |
1 | <body> |
(4) 事件目标支持情况
[1] element:
[x] html事件
[x] 属性事件 (dom0 事件)
[x] 事件监听回调 (dom2事件)
[2] document:
[x] html事件
[x] 属性事件 (dom0 事件)
[] 事件监听回调 (dom2事件)
[3] window:
实际body标签通过frameset等来支持html事件
[x] html事件
[x] 属性事件 (dom0 事件)
[x] 事件监听回调 (dom2事件)