这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战
为了实现JavaScript脚本和浏览器之间的交互,浏览器给我们提供的BOM、DOM等一些对象模型
事实上还有一种需要和浏览器经常交互的事情就是事件监听
浏览器在某个时刻可能会发生一些事件,比如鼠标点击、移动、滚动、获取、失去焦点、输入内容等等一系列 的事件
我们需要以某种方式(代码)来对其进行响应,进行一些事件的处理
在Web当中,事件在浏览器窗口中被触发,并且通过绑定到某些元素上或者浏览器窗口本身,那么我们就可以给这些元素或者window窗口来绑定事件的处理程序,来对事件进行监听
监听方式
在script中直接监听
<!--
我们可以将对于的事件响应逻辑写在行内
也可以单独抽离出现形成一个事件处理函数
-->
<div id="box" onclick="console.log('box被点击了')"></div>
通过元素的on来监听事件
使用这种添加方式,其实和在script中直接监听的本质区别不大
只不过使用on来监听事件,是获取元素后,在js代码中添加事件
而不是在html中对元素添加对应的事件
但他们本质上都是对元素的onXXX属性进行赋值
所以后一个事件处理函数会将前一个事件处理函数覆盖
只能绑定一个事件处理函数
const el = document.getElementById('box')
el.onclick = () => console.log('box被点击了')
通过EventTarget中的addEventListener来监听
使用addEventListener
方式来添加事件监听函数是为了解决使用onXXX方式添加对应的事件处理函数的时候
只能添加一个事件响应函数这个问题而提出的新api
使用addEventListener
方法可以给元素添加多个事件处理函数
多个事件处理函数之间被不冲突,包括使用onXXX方法添加的事件处理函数
const el = document.getElementById('box')
el.onclick = () => console.log('box被点击了1') // 被触发
el.addEventListener('click', () => console.log('box被点击了2')) // 被触发
el.addEventListener('click', () => console.log('box被点击了3')) // 被触发
事件流
我们在浏览器上对着一个元素点击时,你点击的不仅仅是这个元素本身
元素的父容器也会被点击,父容器的父容器也会被点击,... 一直到body元素
这是因为我们的HTML元素是存在父子元素叠加层级的
而这种事件触发的方式就被称之为事件流
因为早期浏览器开发时,不管是IE还是Netscape公司都发现了这个问题,但是他们采用了完全相反的事件流来对事件进行了传递
IE采用了内元素事件先被触发,再触发外层元素事件(事件冒泡 Event Bubble)的方式
而Netscape采用了外层元素先被触发,内层元素事件再被触发(事件捕获 Event Capture)的方式;
// addEventListener第三个参数就是用来控制对应的事件是使用冒泡的方式被触发,而是使用捕获的方式被触发
// 默认false --- 使用冒泡方式被触发
// 设置为true的时候,使用捕获的方式被触发
el.addEventListener('click', () => console.log('box被点击了2'), true)
如果我们同时有事件冒泡和时间捕获的监听,那么会优先监听到事件捕获,再处理事件冒泡
事件对象
当一个事件发生时,就会有和这个事件相关的很多信息
比如事件的类型是什么,你点击的是哪一个元素,点击的位置是哪里等等相关的信息
这些信息会被封装到一个Event对象中,并作为事件处理函数的参数进行传递
该对象给我们提供了想要的一些属性,以及可以通过该对象进行某些操作
// 获取事件对象
el.addEventListener('click', e => console.log(e))
const el = document.getElementById('box')
el.addEventListener('click', e => {
console.log(e.type) // => click 事件类型
console.log(e.target) // => el元素 实际触发事件的那个元素
// 在实际触发对应事件的元素的事件响应函数中
// e.target和e.currentTarget是同一个对象
console.log(e.currentTarget) // => el元素 真正处理事件的元素
console.log(e.offsetX, e.offsetY) // 元素被点击的位置
})
document.body.addEventListener('click', e => {
console.log(e.target) // el元素
console.log(e.currentTarget) // body元素
})
const el = document.getElementById('box')
el.addEventListener('click', e => {
// 阻止事件的进一步传递
// 包括事件冒泡和事件捕获都会被阻止
e.stopPropagation()
})
const el = document.getElementById('box')
el.addEventListener('click', e => {
// 取消事件的默认行为
e.preventDefault()
})
stopImmediatePropagation
vs stopPropagation
const el = document.getElementById('box')
el.addEventListener('click', e => {
// 如果一个元素上被绑定了多个事件响应函数
// 使用stopImmediatePropagation会阻止除当前事件处理函数之外的全部事件处理函数
// 包括绑定在同一个元素上的其它事件处理函数
e.stopImmediatePropagation()
console.log(111) // 触发
})
el.addEventListener('click', e => {
console.log(222) // 没有触发
})
el.addEventListener('click', e => {
console.log(333) // 没有触发
})
const el = document.getElementById('box')
el.addEventListener('click', e => {
// 使用Propagation会阻止除当前事件处理函数之外的全部事件处理函数
// 如果一个元素上被绑定了多个事件响应函数
// 这些事件处理函数都会被触发
e.stopPropagation()
console.log(111) // 触发
})
el.addEventListener('click', e => {
console.log(222) // 触发
})
el.addEventListener('click', e => {
console.log(333) // 触发
})