发现在写事件的时候,有很多零散的知识点傻傻分不清除,由此记录一下学习过程
事件(Event)
事件(Event)是由DOM元素产生的资源,它可以由 JavaScript 代码操作,这样说很抽象,其实可以举一些常见的事件例子:
- 当用户单击鼠标时
- 网页加载后
- 加载图像后
- 当鼠标移到元素上时
- 更改输入字段时
- 提交 HTML 表单时
- 当用户按下某个键时
事件流(Event flow)
事件流描述的是页面接受事件的顺序
事件会在元素节点之间按照特定的顺序进行传播,这个传播过程就叫做DOM事件流
事件流的过程
考虑这样一个情况,三个div嵌套,且三个div都注册了鼠标点击事件,如果点击红色区域的部分,会按照什么顺序执行呢?
- 蓝->绿->红(从外层到内层,Netscape Navigator提出,也就是事件捕获)
- 红->绿->蓝(从内层到外层,IE提出,也就是事件冒泡)
- 蓝->绿->红->绿->蓝 (先捕获,到达事件目标红色元素开始冒泡,W3C提出的中立说法)
<style>
#grandpa{
width: 400px;
height: 400px;
background-color: blue;
}
#father{
width: 300px;
height: 300px;
background-color: green;
}
#son{
width: 200px;
height: 200px;
background-color: red;
}
</style>
<body>
<div id="grandpa">
<div id="father">
<div id="son">
在红色区域到底是谁先被触发点击事件?
</div>
</div>
</div>
</body>
按照W3C的说法,dom事件流分为3个步骤
- 捕获阶段 document先接收到点击(监听)事件 但没有绑定事件,就会略过,再到html 同样没有绑定略过,再到body同理,但是蓝色和绿色的div有绑定点击事件会触发
- 当前目标阶段 找到了红色div,以及它的绑定点击事件
- 冒泡阶段 事件由最具体的元素接收到了以后又逐层往上传播到dom最顶层的过程
而我们可以通过element1.addEventListener(type, listener, useCapture)
来指定事件处理程序在哪个阶段执行
如果其最后一个参数为 true,则为捕获阶段设置事件处理程序,如果为 false,则为冒泡阶段设置事件处理程序。
但实际中我们更多关注的是冒泡阶段,所以用
element1.onclick = doSomething2
可以直接设定为冒泡阶段
注意:
- js代码中只能执行捕获或者冒泡其中的一个阶段
- onclick和attachEvent只能得到冒泡阶段
- addEventListener(type,function,true/false)
- 实际使用中更关注的是事件冒泡
- 有些事件是没有冒泡的 onblur onfocus onmousenter onmouseleave
官方是推荐使用addEventListener()来注册一个事件监听器的,理由如下(引自MND原文):
- 它允许为一个事件添加多个监听器。特别是对库、JavaScript 模块和其他需要兼容第三方库/插件的代码来说,这一功能很有用。
- 相比于
onXYZ
属性绑定来说,它提供了一种更精细的手段来控制listener
的触发阶段。(即可以选择捕获或者冒泡)。 - 它对任何事件都有效,而不仅仅是 HTML 或 SVG 元素。
事件对象
var div = document.querySelector("div");
div.onclick = function(event) {}
1 event也就是一个事件对象,写到我们侦听函数的小括号里面,当成形参来看
2 事件对象只有有了事件才会存在,是系统自动给创建的,不需要我们传递参数
3 事件对象 就是一个对象,里面有我们事件一系列相关数据的集合 跟事件相关的 比如按下鼠标的坐标 按下键盘的keyCode
4 这个事件对象可以自己命名,比如event, e ,evt等
5 事件对象有兼容性问题,ie 678通过window.event来识别 兼容性处理方式 e = e || window.event
事件对象的常见属性和方法
e.target
返回触发事件的对象
e.target返回的是触发事件的对象(元素)谁触发了这个事件
Event.currentTarget
总是指向事件绑定的元素
Event.target
则是事件触发的元素
比如触发事件是绑定在ul上的,我点击里面的li的时候,Event.currentTarget
始终指向的是ul
但是因为点击的是li,所以Event.target
会指向li。
function hide(e){
e.currentTarget.style.visibility = "hidden";
console.log(e.currentTarget);
// 该函数用作事件处理器时:this === e.currentTarget
}
var ps = document.getElementsByTagName('p');
for(var i = 0; i < ps.length; i++){
// console: 打印被点击的 p 元素
ps[i].addEventListener('click', hide, false);
}
// console: 打印 body 元素
document.body.addEventListener('click', hide, false);
e.type
返回触发事件的类型 hover click
e.cancelBubble
取消冒泡属性
e.preventDefault()
阻止默认的跳转链接
e.stopPropagation()
阻止冒泡
事件委托(委派)
不是按班里的人发东西,而是把东西交给班长,让班长去发
事件委托的原理:
不是每个子节点单独设置事件监听器,而是事件监听器设置在父节点上,然后利用冒泡原理影响设置每个子结点
给ul注册点击事件,利用事件对象target来找到当前点击的li,因为点击li,事件会冒泡到ul上,ul有注册事件,就会触发事件监听器,这样相当于ul里面的所有li都绑定了事件处理程序
事件委托的作用:
只操作了一次DOM,提高了程序的性能