目录
在JavaScript中,事件流和事件模型是处理用户交互的关键概念。深入理解这些概念将使你能够更好地处理和响应用户的动作。本文将详细介绍JavaScript的事件流和事件模型。
事件
在了解什么是事件流之前,我们先了解一下什么是事件
事件是文档或浏览器窗口中发生的一些特定的交互瞬间
HTML事件就是发生在HTML元素上的事情
当在HTML中使用javaScript时,javaScript能够应对这些事件
比如鼠标点击、键盘按键、页面加载、表单提交...
事件机制
事件绑定、事件监听、事件委托(事件代理)
一、事件绑定
事件绑定是指将事件处理函数附加到特定的DOM元素上。
在JavaScript中,有以下几种绑定事件的方法(以click为例):
-
在DOM元素中直接绑定
可以直接在DOM元素上绑定某个事件
html<div onclick="hello()">click me</div> <script> function hello(){ alert("hello world!"); } </script>
-
在JavaScript代码中绑定
在JavaScript代码中绑定事件可以使JavaScript代码与HTML标签分离,文档结构清晰,便于管理和开发。
html<div id="btn">click me</div> <script> document.getElementById("btn").onclick = hello function hello(){ alert("hello world!"); } </script>
-
绑定事件监听函数
使用
addEventListener()
或attachEvent()
【IE标准】 来绑定事件监听函数。👇下面请看事件监听
二、事件监听
事件监听的优点
- 可以绑定多个事件
btn1.addEventListener("click",hello1)
btn2.addEventListener("click",hello2)
- 可以解除相应的绑定
addEventListener()
=>removeEventListener()
attachEvent()
=>detachEvent()
element.addEventListener(event, function, useCapture)
-
event
: (必需)事件名,支持所有 DOM事件 -
function
:(必需)指定要事件触发时执行的函数 -
useCapture
:(可选)指定事件是否在捕获或冒泡阶段执行。true => 在事件捕获阶段调用;false[默认] => 在事件冒泡阶段调用 -
注意:IE8以下不支持
-
示例👇
html<div id="btn">click me</div> <script> document.getElementById("btn").addEventListener("click", hello); function hello(){ alert("hello world!"); } </script>
element.attachEvent(event, function)
【IE标准】
-
event
:(必需)事件类型。需加on
,例如:onclick
。 -
function
:(必需)指定要事件触发时执行的函数。 -
示例👇
html<div id="btn1">click me</div> <script> document.getElementById("btn1").attachEvent("onclick",hello); function hello(){ alert("hello world!"); } </script>
三、事件委托
事件监听的优点
- 提高JavaScript性能。事件委托可以显著的提高事件的处理速度,减少内存的占用。 【DEMO】
- 动态的添加DOM元素,不需要因为元素的改动而修改事件绑定。
事件委托就是利用冒泡的原理,把事件加到父元素或祖先元素上,触发执行效果。
示例👇
html
<div id="btn">click me</div>
<script>
const btn = document.getElementById("btn");
document.onclick = function(event){
event = event || window.event;
var target = event.target || event.srcElement;
if(target == btn){
hello()
}
}
function hello(){
alert("hello world!");
}
事件流
事件流 是从页面中接收事件的顺序。
事件流模型
DOM事件流规范规定事件流分为 3 个阶段:
- 事件捕获阶段:事件从上往下查找对应元素,直到捕获到事件
- 处于目标阶段:目标元素后执行事件对应的处理函数
- 事件冒泡阶段:事件从目标元素开始冒泡
Tips: 在 DOM 事件流中,实际的目标(元素)在捕获阶段不会收到事件。这是因为捕获阶段从document到元素就结束了。下一阶段,即会在元素上触发事件的"处于目标"阶段,通常在事件处理时被认为是冒泡阶段的一部分。然后冒泡阶段开始,事件反向传播至文档。
DOM事件处理
DOM节点中有了事件,那我们就需要对事件进行处理。
DOM0
DOM0级事件具有极好的跨浏览器优势,会以最快的速度绑定。
-
内联模型(行内绑定):将函数名直接作为html标签中属性的属性值
html<div onclick="handleClick" >Click Me</div> <script> function handleClick(){ alert("hello world!"); } </script>
-
脚本模型(动态绑定),通过在JS中选中某个节点,然后给节点添加onclick属性。
html<div id="btn">Click Me</div> <script> const btn = document.getElementById("btn"); btn.onclick = handleClick() function handleClick(){ alert("hello world!"); } </script>
Tips1: 使用 on
开头的事件,同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数。
html
<div id="btn">Click Me</div>
<script>
const btn = document.getElementById("btn");
btn.onclick = handleClick()
btn.onclick = handleClick2()
function handleClick(){
alert("hello world!");
}
function handleClick2(){
alert("hello world222!");
}
// 输出 hello world222!
</script>
Tips2: DOM0级只支持冒泡阶段。
html
<div id="btn1" style="height: 200px;width: 200px;background: red;">
btn1
<div id="btn2" style="height: 150px;width: 150px;background: green;">
btn2
<div id="btn3" style="height: 100px;width: 100px;background: blue;">
btn3
</div>
</div>
</div>
<script>
const btn1 = document.getElementById("btn1");
const btn2 = document.getElementById("btn2");
const btn3 = document.getElementById("btn3");
btn1.onclick=function(){
console.log(1)
}
btn2.onclick=function(){
console.log(2)
}
btn3.onclick=function(){
console.log(3)
}
</script>
👇 运行效果
👇 当点击 btn3
时:
👇 当点击 btn1
时:
👉 可以发现:最先触发的是最底层 btn1的事件,最后才是顶层 btn3 的事件,因此很明显是事件冒泡 。SO... DOM0级只支持冒泡阶段.
DOM2
定义了两个方法:
addEventListener()
=> 添加事件侦听器removeEventListener()
=> 移除事件侦听器- 具体使用请上滑至 【事件监听】
大多数情况下,事件处理程序会被添加到事件流的冒泡阶段 ,主要原因是跨浏览器兼容性好。
把事件处理程序注册到捕获阶段通常用于在事件到达其指定目标之前拦截事件。如果不需要拦截,则不要使用事件捕获。
🫠 回头看:
html
<div id="btn1" style="height: 200px;width: 200px;background: red;">
btn1
<div id="btn2" style="height: 150px;width: 150px;background: green;">
btn2
<div id="btn3" style="height: 100px;width: 100px;background: blue;">
btn3
</div>
</div>
</div>
<script>
const btn1 = document.getElementById("btn1");
const btn2 = document.getElementById("btn2");
const btn3 = document.getElementById("btn3");
btn1.addEventListener('click',function (){
console.log(1)
},true)
btn2.addEventListener('click',function (){
console.log(2)
},true)
btn3.addEventListener('click',function (){
console.log(3)
},true)
</script>
👇 当点击 btn3
时:【DOM2中的顺序和DOM0中的顺序反过来了,最外层的btn最先触发,因为 addEventListener
最后一个参数是true,捕获阶段进行处理~】
IE 事件处理程序
IE 实现了与 DOM2 类似的方法
attachEvent()
=> 附加事件detachEvent()
=> 剥离事件- 具体使用请上滑至 【事件监听】
js
const btn = document.getElementById("btn");
btn.attachEvent("onclick", function() {
console.log("hello world!");
});
需要注意以下几点:
- 使用
attachEvent()
时,事件处理程序是在全局作用域中运行的,因此this
等于window
。这个差异对编写跨浏览器代码是非常重要的。 - 使用
attachEvent()
方法也可以给一个元素添加多个事件处理程序。事件处理程序会以添加它们的顺序反向触发。 - 使用
attachEvent()
添加的事件处理程序将使用detachEvent()
来移除, 只要提供相同的参数。匿名函数也无法移除。
常见的事件
未完待续...