理解事件流之前,我们需要了解什么是事件:
一、什么是事件
JS的事件是指用户在网页上进行的交互操作,例如鼠标点击、键盘输入、滚动页面等行为。当这些行为发生时,页面中与之关联的JS代码就会产生相应的响应动作,以完成网页的交互效果。 这些事件都是浏览器浏览器实现了的, 每个标签身上的都有这些事件, 只是没有绑定对应的处理事件
const body = document.body
const div = document.createElement('div')
const text = document.createTextNode('hello world')
div.appendChild(text)
div.setAttribute("id", 'div1')
body.appendChild(div)
console.dir(div)
打印出来的结果
上图中表示的就是一个标签身上的一些事件,当用户做出相关操作时, 会触发这些事件身上绑定的函数。
- 事件流
事件流是指事件完整执行过程中的流动路径。当我们给一元素设置了事件监听(addEventListener )后他会按照一定的顺序去执行这个事件身上的事件。
事件冒泡
是由 IE 提出的,而 事件捕获
则是由 Netscape (网景)提出的事件流概念。 后来 ECMAScript
将两种模型进行了整合,制定了统一的标准:先捕获再冒泡 现在整合后的标准事件流就有了三个阶段 (每次事件的执行都会有这三个阶段):
- 事件捕获阶段(目标在捕获阶段不接收事件)
- 目标阶段 (事件的执行阶段,此阶段会被归入冒泡阶段)
- 事件冒泡阶段 (事件传回Dom根节点)
二、addEventListListener()
简述: 给指定元素添加事件的句柄
接收三个参数, 其中第三个参数的默认值是 false 指事件在冒泡阶段被执行
1.事件冒泡
指的是事件从 目标元素 沿着 DOM 树 往上 传递, 直到到达 DOM根节点 , 依次触发父元素身上绑定的事件处理程序。 着重于 从内向外 传播。
2.事件捕获
指的是事件从 DOM根节点 沿着DOM树 往下 传递,直到到达目标元素的父元素,依次触发父元素上的事件处理程序。着重于 从外向内 传播。
3.事件冒泡和事件捕获理解
...
<style>
.outter {
width: 200px;
height: 200px;
background-color: #f00;
}
.inner {
width: 100px;
height: 100px;
background-color: #0f0;
}
.middle {
width: 50px;
height: 50px;
background-color: #00f;
}
</style>
...
<main class="outter">
outter
<section class="inner">inner
<section class="middle">
middle
</section>
</section>
</main>
...
<script>
const outter = document.querySelector('.outter')
const inner = document.querySelector('.inner')
const middle = document.querySelector('.middle')
outter.addEventListener('click', function() {
alert('outter')
})
inner.addEventListener('click', function() {
alert('inner')
}, true)
// middle.addEventListener('click', function() {
// alert('middle')
// }, true)
</script>
点击 middle 会先执行 inner, 再执行 outter
inner.addEventListener('click', function() {
alert('inner')
}, true)
这段代码会在捕获阶段执行, 点击 middle 时, 根据事件流的机制, 会先执行 inner
outter.addEventListener('click', function() {
alert('outter')
}
第三个参数没有指定, 默认为 false 是冒泡, 会在冒泡阶段被执行, 根据事件流机制, 在冒泡阶段执行 outter
4.事件代理(事件委托)
简单描述: 子元素将事件委托给父元素执行
利用了事件的冒泡机制,把 目标元素 执行的事件函数委托给父元素执行
例子:
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let li=document.querySelectorAll('li') //获取所有li
for(let i=0;i<li.length;i++){
li[i].addEventListener('click',()=>{ //监听li点击事件
let e=li[i].innerHTML
console.log(e)
})
}
</script>
// 在实际开发过程中是不可能循环给每个元素添加监听句柄的,可以利用事件委托进行处理
<script>
let ul=document.getElementById("ul");
ul.addEventListener("click",(e)=>{
console.log(e.target.innerHTML);
})
</script>
事件委托的优点:
- 提高性能:每一个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少。
- 动态监听:使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以一样具有和其他元素一样的事件。
三、阻止事件冒泡
有两种方式:
- stopPropagation() :终止默认事件传播到其他容器上
- stopImmediatePropagation() :终止默认事件传播到其他容器上和自己这个容器上的其他事件
stopPropagation() :
<script>
const outter = document.querySelector('.outter')
const inner = document.querySelector('.inner')
const middle = document.querySelector('.middle')
outter.addEventListener('click', function() {
alert('outter')
})
inner.addEventListener('click', function() {
alert('inner')
}, true)
middle.addEventListener('click', function(e) {
alert('middle')
e.stopPropagation() // 阻止事件的冒泡, 当事件执行到目标元素, 不再向上冒泡
}, true)
</script>
// 点击 middle 将不会执行 outter, 在 middle 处, 阻止了事件的冒泡
stopImmediatePropagation() :
<script>
const outter = document.querySelector('.outter')
const inner = document.querySelector('.inner')
const middle = document.querySelector('.middle')
// outter.addEventListener('click', function() {
// alert('outter')
// })
// inner.addEventListener('click', function() {
// alert('inner')
// }, true)
middle.addEventListener('click', function(e) {
alert('middle')
})
middle.addEventListener('click', function(e) {
alert('middle, propagation')
})
</script>
// 当前代码, 会按顺序执行 middle 身上的事件
如果添加 stopImmediatePropagation() 函数, 那么他将阻止自身的其他事件, 和其他冒泡事件
<script>
const outter = document.querySelector('.outter')
const inner = document.querySelector('.inner')
const middle = document.querySelector('.middle')
outter.addEventListener('click', function() {
alert('outter')
})
// inner.addEventListener('click', function() {
// alert('inner')
// }, true)
middle.addEventListener('click', function(e) {
alert('middle')
e.stopImmediatePropagation()
})
middle.addEventListener('click', function(e) {
alert('middle, propagation')
})
</script>
// 只执行 middle
四、阻止默认事件
- preventDefault(): 阻止用户身上默认的事件
- defaultPrevented:返回一个布尔值,表明当前事件是否调用了
event.preventDefault()
方法。
preventDefault() :
<p>Please click on the checkbox control.</p>
<form>
<label for="id-checkbox">Checkbox:</label>
<input type="checkbox" id="id-checkbox" />
</form>
<div id="output-box"></div>
const checkbox = document.querySelector("#id-checkbox");
checkbox.addEventListener("click", checkboxClick, false);
function checkboxClick(event) {
let warn = "preventDefault() won't let you check this!<br>";
document.getElementById("output-box").innerHTML += warn;
event.preventDefault();
}
defaultPrevented:
当preventDefault
在一个给定的事件上被调用时,defaultPrevented
属性被切换到true
。 由于事件的传播,该事件以这个defaultPrevented
的值向上冒泡。
const link = document.querySelector('#my-link');
link.addEventListener('click', e => e.preventDefault());
document.addEventListener('click', documentClickHandler);
function documentClickHandler(event) {
if (event.defaultPrevented) {
}
else {
}
}
property
免费试用
更多热门智能应用