原文网址:JS--事件(Event)--机制/原理_IT利刃出鞘的博客-CSDN博客
简介
说明
本文介绍JavaScript中的事件的机制。包括:事件的捕获与冒泡,事件的流程。
官网
事件大全:事件参考 | MDN
JavaScript事件介绍
事件 是某事发生的信号。所有的 DOM 节点都生成这样的信号(但事件不仅限于
DOM)。
我们也可以自定义事件。
常用的事件
鼠标事件
- click:当鼠标点击一个元素时(触摸屏设备会在点击时生成)。
- contextmenu:当鼠标右键点击一个元素时。
- mouseover / mouseout:当鼠标指针移入/离开一个元素时。
- mousedown / mouseup:当在元素上按下/释放鼠标按钮时。
- mousemove:当鼠标移动时。
键盘事件
- keydown:键盘按下
- eyup:键盘弹起
表单(form)元素事件
- submit:当访问者提交了一个 <form> 时。
- focus:当访问者聚焦于一个元素时,例如聚焦于一个 <input> 。
Document 事件
- DOMContentLoaded:当 HTML 的加载和处理均完成,DOM 被完全构建完成时。
CSS 事件
- transitionend:当一个 CSS 动画完成时。
三个阶段
说明
JavaScript 事件触发有三个阶段。
- CAPTURING_PHASE:捕获阶段
- AT_TARGET:目标阶段
- BUBBLING_PHASE:冒泡阶段
我们可通过事件对象的 eventPhase 属性,得知事件处于哪个阶段。eventPhase 为一个正整数,其定义可在 Event interface 查阅到。本处贴出:
const unsigned short CAPTURING_PHASE = 1;
const unsigned short AT_TARGET = 2;
const unsigned short BUBBLING_PHASE = 3;
事件捕获与冒泡
本处以下边这个HTML为例进行说明
<html>
<head>
<title>事件捕获</title>
</head>
<body>
<div>click</div>
</body>
</html>
事件捕获:从启动事件的元素节点开始,逐层往下传递,直到最下层节点,也就是div节点。
事件冒泡:从最底层的元素节点开始,逐层向上传递,直到最上层节点。
冒泡与捕获的实例
示例1:冒泡阶段处理事件
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>This is title</title>
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
</head>
<body>
<form id="id-form">FORM
<div id="id-div">DIV
<p id="id-p">P</p>
</div>
</form>
<script>
let form = document.getElementById('id-form');
form.addEventListener('click', (ev => {
console.log('form. eventPhase:' + ev.eventPhase);
}))
let div = document.getElementById('id-div');
div.addEventListener('click', (ev => {
console.log('div. eventPhase:' + ev.eventPhase);
}))
let p = document.getElementById('id-p');
p.addEventListener('click', (ev => {
console.log('p. eventPhase:' + ev.eventPhase);
}))
</script>
</body>
</html>
测试:用鼠标点击“P”所在的框
示例2:冒泡阶段处理事件
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>This is title</title>
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
</head>
<body>
<form id="id-form">FORM
<div id="id-div">DIV
<p id="id-p">P</p>
</div>
</form>
<script>
let form = document.getElementById('id-form');
form.addEventListener('click', (ev => {
console.log('form. eventPhase:' + ev.eventPhase);
}), true)
let div = document.getElementById('id-div');
div.addEventListener('click', (ev => {
console.log('div. eventPhase:' + ev.eventPhase);
}), true)
let p = document.getElementById('id-p');
p.addEventListener('click', (ev => {
console.log('p. eventPhase:' + ev.eventPhase);
}), true)
</script>
</body>
</html>
测试:用鼠标点击“P”所在的框
取消事件传递
说明
可以通过 e.stopPropagation 中断事件的向下或向上传递。
这样一来,事件传播被中断了,剩下的 listener 不能接收到事件。
需要注意:stopPropagation 不能阻止同一节点的其他 listener 的执行 。
不要在没有需要的情况下停止事件传递
时间传递很方便,但是不要在没有真实需求时阻止它。只有需求要求如此,而且在架构上经过深思熟虑才能停止事件传递。
有时 event.stopPropagation() 会产生隐藏的陷阱,以后可能会成为问题。
例如:
- 我们创建了一个嵌套菜单,每个子菜单各自处理对自己的元素的点击事件,并调用 stopPropagation ,以便不会触发外部菜单。
- 之后,我们决定捕获在整个窗口上的点击,以追踪用户的行为(用户点击的位置)。有些分析系统会这样做。通常,代码会使用document.addEventListener('click'…) 来捕获所有的点击。
- 我们的分析不适用于被 stopPropagation 所阻止点击的区域。太伤心了,我们有一个“死区”。
通常,没有真正的必要去阻止冒泡。一项看似需要阻止冒泡的任务,可以通过其他方法解决。其中之一就是使用自定义事件,我们还可以将我们的数据写入一个处理程序中的 event 对象,并在另一个处理程序中读取该数据,这样我们就可以向父处理程序传递有关下层处理程序的信息。
示例
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>This is title</title>
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
</head>
<body>
<form id="id-form">FORM
<div id="id-div">DIV
<p id="id-p">P</p>
</div>
</form>
<script>
let form = document.getElementById('id-form');
form.addEventListener('click', (ev => {
console.log('form. eventPhase:' + ev.eventPhase);
}))
let div = document.getElementById('id-div');
div.addEventListener('click', (ev => {
console.log('div. eventPhase:' + ev.eventPhase);
ev.stopPropagation();
}))
let p = document.getElementById('id-p');
p.addEventListener('click', (ev => {
console.log('p. eventPhase:' + ev.eventPhase);
}))
</script>
</body>
</html>
测试: 用鼠标点击“P”所在的框
可见:停止之后,外层的listener就接收不到事件了。
取消默认行为
说明
许多事件会自动触发浏览器执行某些行为。例如:
- 点击一个链接 —— 触发导航(navigation)到该 URL。
- 点击表单的提交按钮 —— 触发提交到服务器的行为。
- 在文本上按下鼠标按钮并移动 —— 选中文本。
有时我们不想要这种自定义的行为,想要自定义自己的行为,这时我们可以取消这个默认行为。
有两种方式来告诉浏览器我们不希望它执行默认行为:
- 主流的方式:使用 event 对象。有一个 event.preventDefault() 方法。
- 若处理程序是使用 on<event> (而不是 addEventListener )分配的,那返回 false 即可。
示例
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>This is title</title>
</head>
<body>
<a href="https://knife.blog.csdn.net/">这是常规链接</a><br>
<a href="https://knife.blog.csdn.net/" onclick="return false">这是return false</a><br>
<a href="https://knife.blog.csdn.net/" onclick="event.preventDefault()">这是event.preventDefault()</a>
</body>
</html>
测试
可见:阻止默认行为的下边那两个链接点击之后没反应;没有阻止默认行为的那个链接点击后会跳转。