1. 什么是事件
事件是在编程时系统内发生的动作或者发生的事情,比如PC端的事件有鼠标点击、键盘按下等等,移动端的事件有触摸事件、滑动事件等等操作行为,在编程语言中称为事件。
2. DOM事件流
JS通过浏览器提供的DOM接口来操作网页页面的文档元素,DOM事件流是DOM对象里对页面元素面对事件发生时做出的响应反映,HTML页面的元素是一层层的包含关系,DOM对HTML页面元素事件的处理是层层传递的流向关系。DOM事件流分为三个阶段用于事件的传递,附图解如下:
(1) 事件捕获阶段
(2) 处于目标阶段
(3) 事件冒泡阶段
如图所示,例如用户鼠标点击页面的一个div元素,则鼠标点击事件的传递过程为,首先整个文档页面捕获到点击事件,然后是html元素捕获到点击事件,然后是body元素,然后到div元素,div元素捕获到事件就是处于目标阶段,然后div元素的事件处理程序会针对该点击事件做出响应和处理,然后事件开始向底层冒泡,由div->body->html->Document元素冒泡下去,这个循环流动过程就是DOM对象提供的对事件发生的响应。
也就是说,整个事件流过程,层层包含的每个元素都有两次接触事件的机会,一次是在事件捕获阶段,一次是在事件冒泡阶段。
3. 事件处理程序
当事件发生时,可用JS通过DOM接口来操作页面元素的方式对事件做出反映,对事件做出反映和处理的程序就是事件处理程序,关于事件处理程序的编写分为两种方式:
3.1 注册 on-event 事件处理器
使用on-event事件处理器实质上可以理解为是将事件处理器作为元素的属性添加到元素上,如onclick、onfocus等元素属性。为html元素添加事件处理器属性又分为两种方式,分别为:
(1)在元素上使用 HTML attribute on {eventtype}
该方法已基本过时,html和js的耦合性太大
<div onclick="alert('old')">点击div</div>
//赋值为事件处理函数
<div onclick="fn()"></div> //必须是加括号的调用形式
<script>
fn(){
console.log('ok');
}
</script>
//此种方式的事件信息怎么传递
<div onclick="pe(event)">点击</div> //这里必须传递 event 关键字对象
<script>
pe(e){
console.log(e); //事件信息
}
</script>
(2) 通过 JavaScript 设置页面元素相应的属性 property
var div = document.getElementById("a");
div.onclick = function() { alert('new') };
3.2 添加 addEventListener() 事件侦听器
该种方式的事件处理程序是新的、功能和可选项更多的处理方式
div.addEventListener('click', function(e){
console.log('点击div')
})
语法为:
target.addEventListener(type, listener, useCapture);
target.addEventListener(type, listener, options);
type:表示监听事件类型的字符串
listener:事件处理函数,形参为事件信息
useCapture:Boolean类型的值,表示是否在捕获阶段触发事件,默认为false,默认在冒泡阶段处理事件,若设置为true,则会在捕获阶段处理事件。
options:一个指定有关 listener 属性的可选参数对象,相比于useCapture,可以有更多的设置,options对象的属性值都是Boolean值,默认全部为false,{capture: 是否捕获阶段监听, once: 是否只监
听一次, passive: 是否忽略preventDefault }
3.3 移除 removeEventListener() 事件侦听器
在捕获阶段的事件处理和冒泡阶段的事件处理是两个独立的事件处理程序,因此在移除事件的时候应该注明是要移除那个阶段的事件处理。
target.removeEventListener(type, listener);
target.removeEventListener(type, listener, true);
4. 冒泡与捕获
4.1 停止事件传播
可以阻止冒泡,也可以阻止捕获。
e.stopPropagation()
div.addEventListener('click', function(e){
e.stopPropagation();//停止事件传播语句
})
4.2 阻止默认事件
e.preventDefault()
与passive选项配合可优化移动端滚动性能
html的一些标签元素会有一些默认事件,如a标签的点击自动跳转刷新页面、表单里的提交按钮点击自动提交等等,如果我们想在事件发生时如点击时进行某些操作,可先阻止默认事件,再进行操作,再自定义事件。
4.3 事件代理(委托)
事件绑定代理给父元素,由父元素根据事件来源统一处理。
有时候,页面元素是动态生成的,提前写好的事件绑定可能在元素还未出现时就已经执行,则事件处理则无法执行,除非在生成时再次绑定。这样比较麻烦,因此可以用事件代理,由父元素检测事件告诉子元素。
在事件处理函数的形参e中,包含事件的相关信息,输如click事件的e事件信息为:
其中比较重要的信息有三个,一个是type:事件类型,一个是target:当前点击元素,target下有tagName、id、className、classList等Element节点元素公有的属性,一个是path,里面存储的是事件的传递经过的元素,先从右到左依次捕获,再从左向右冒泡。
target
因此有时会用target来判断当前点击对象,但是当判断的点击的元素里面有text文本内容或其他内时,若点击到里面的内容,则target就是里面的内容了,因此用target判断点击元素不准确。
path
path里记录了该事件整个的传递过程中遇到的元素,因此,可以在父元素的事件处理中,根据path来对子元素进行操作,即在父元素中处理子元素的事件,例如:
<div id="container">
<div class="box">
<span>box1</span>
</div>
<div class="box">
<span>box2</span>
</div>
<div class="box">
<span>box3</span>
</div>
</div>
<button id="add">add</button>
<p id="log"></p>
//事件代理
container.onclick = function(e){
let $box = e.path.find(el => el.classList && el.classList.contains('box'))
if($box) {
log.innerText = $box.innerText
}
}
5. 事件类型
在mdn上可查看完整的事件类型,mdn-events
click 与 dblclick
click是单击事件,dblclick是双击事件
mouseover 与 mouseout
mouseover是鼠标移入,mouseout是鼠标移出,但这两个不常用,因为,如果元素有子元素,那么移入子元素时会moseout再mouseover重新判断一次,在元素内移入移出会重复计算
mouseenter 与 mouseleave
mouseenter是鼠标进入,mouseleave是鼠标离开,这两个常用,和mouseover和mouseout不同,当鼠标进入一个元素时,在该元素的子元素内也是保持mouseenter状态不变,不会重新计算
focus 与 blur
focus是input输入框获得焦点事件,blur是输入框失去焦点事件。
change
change是input输入框的文本内容改变时触发的事件
keydown和keyup
keydown是键盘按下事件,keyup是键盘释放事件
submit
表单提交事件
scroll
文档或元素滚动事件
resize
文档视图改变事件