DOM事件

事件的本质是程序各个组成部分之间的一种通信方式,也是异步编程的一种实现。DOM 支持大量的事件,事件是程序在运行的时候,发生的特定动作或者特定的事情

下面是一些比较常用的DOM事件

鼠标事件

  • click (点击事件)
1
2
3
button.addEventListener('click',function(){
alert('我是点击事件的弹框')
})
  • mouseenter / mouseleave (鼠标移入/鼠标离开)
1
2
3
box.addEventListener('mouseenter',function(){
console.log('鼠标移入了');
})
1
2
3
box.addEventListener('mouseleave',function(){
console.log('鼠标离开了');
})
  • mousemove (鼠标移动)

    1
    2
    3
    box.addEventListener('mousemove',function(){
    console.log('我要开始摸了啊');
    })

    当鼠标在一个节点内部移动时触发。当鼠标持续移动时,该事件会连续触发。为了避免性能问题,建议对该事件的监听函数做一些限定,比如限定一段时间内只能运行一次。

  • mouseover / mouseout (鼠标移入 / 鼠标离开)

    区别:

    mouseenter事件只触发一次

    mouseover而只要鼠标在节点内部移动,mouseover事件会在子节点上触发多次

    mouseout事件和mouseleave事件

    在父元素内部离开一个子元素时,mouseleave事件不会触发,而mouseout事件会触发

键盘事件

  • keydown (键盘按下)

    1
    2
    3
    button.addEventListener('keydown',function(){
    alert('键盘按下了')
    })
  • keyup(键盘弹起)

    1
    2
    3
    button.addEventListener('keyup',function(){
    alert('键盘按弹起')
    })

焦点事件

  • focus(获得焦点)

    1
    2
    3
    input.addEventListener('focus' ,function(){
    console.log('获得焦点了!哈哈哈');
    })
  • blur(失去焦点)

    1
    2
    3
    input.addEventListener('blur' ,function(){
    console.log('你别走,没了你我可怎么活啊!燕子555');
    })

表单事件

  • input

    input事件的一个特点,就是会连续触发,比如用户每按下一次按键,就会触发一次input事件,获取到的都是及时最新的动态

  • change

    该事件跟change事件很像,不同之处在于input事件在元素的值发生变化后立即发生,而change在元素失去焦点时发生,而内容此时可能已经变化多次。也就是说,如果有连续变化,input事件会触发多次,而change事件只在失去焦点时触发一次。

事件监听

事件监听版本

L0

08ff94172c06fa9f07b09c94e98be78b83d2e932959044a9242cfb78bbabcb29

L2

1e0802f745a94cf96793a161dba6bd19b552d7c1e3eb366f935e009af5c5bd7d

区别:

on click 方式添加多个同名事件会被覆盖,addEventListener则不会

addEventListener

语法:

事件对象.addEventListener.(事件类型,事件处理函数,是否冒泡[默认为false]一般默认不写),第三个参数将会在下面的事件流中介绍到

事件三要素:

1.事件源 —–谁被触发了

2.事件类型 —–在什么情况下被触发

3.事件处理函数 —–触发之后干了什么

事件对象

为了正确处理事件,我们需要更深入地了解发生了什么。不仅仅是 “click” 或 “keydown”,还包括鼠标指针的坐标是什么?按下了哪个键?等等

当事件发生时,浏览器会创建一个 event 对象,将详细信息放入其中,并将其作为参数传递给处理程序

既然是一个对象那么它就有一些属性,其中比较常用的有key属性和target属性

e.key获取到用户输入的案件信息是回车还是空格啊

一般会用在判断用户按下的是不是回车键来进行执行指定的操作,比如说发布评论或是发送消息以及登录操作等等

e.target这个属性获取到的是触发事件的元素,也就是事件源

环境对象this

简单的理解,在普通函数中,谁调用函数, this 就指向谁,在全局环境中
window 对象,是浏览器的顶级对象,在全局执行 js,全局所有的东西都属于window,

小总结:

在全局中this的指向全局对象window对象

在普通函数中this指向调用者

构造函数中this指向新创建的对象

箭头函数有自己固定的this指向会继续沿用上一层作用域中的this指向

也可以通过call band apply改变this的指向

其他事件

页面加载事件

load 事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发

当页面完全加载后在控制台打印一段信息:

1
2
3
4
window.addEventListener('load', (e) => {
console.log('页面已经加载完成');
});

页面滚动事件

由于 scroll 事件可被高频触发,事件处理程序不应该执行高性能消耗的操作,如 DOM 操作。而更推荐的做法是使用 setTimeout或是使用lodash来进行节流,减少事件触发频率,提高性能

页面尺寸事件

页面窗口调整大小时会触发 resize 事件

事件流

事件传播的三个阶段:

  1. 捕获阶段(Capturing phase)—— 事件(从 Window)向下走近元素。

  2. 目标阶段(Target phase)—— 事件到达目标元素。

  3. 冒泡阶段(Bubbling phase)—— 事件从元素上开始冒泡。

    image-20230720230606149

事件捕获

也就是说:点击 <td>,事件首先通过祖先链向下到达元素(捕获阶段),然后到达目标(目标阶段),最后上升(冒泡阶段),在途中调用处理程序。

事件冒泡

冒泡(bubbling)原理很简单。

当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。

假设我们有 3 层嵌套 box > father > son,它们各自拥有一个处理程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<body>
<div class="box">
box

<div class="father">
father
<div class="son">son</div>
</div>
</div>

<script>
// 点击事件
const father = document.querySelector('.father')
const box = document.querySelector('.box')
const son = document.querySelector('.son')

son.addEventListener('click',function(){
alert('我是儿子')
})
father.addEventListener('click',function(){
alert('我是父盒子')
})
box.addEventListener('click',function(){
alert('我是最外面的盒子')
})

</script>

点击内部的 son 会首先会执行点击事件:

  1. 在该 son 上的。
  2. 然后是外部 father 上的。
  3. 然后是外部 box 上的。
  4. 以此类推,直到最后的 document 对象。
image-20230720224845147

因此,如果我们点击 son,那么我们将看到 3 个 alert:sonfatherbox

这个过程被称为“冒泡(bubbling)”,因为事件从内部元素“冒泡”到所有父级,就像在水里的气泡一样。

阻止冒泡

冒泡事件从目标元素开始向上冒泡。通常,它会一直上升到 <html>,然后再到 document 对象,有些事件甚至会到达 window,它们会调用路径上所有的处理程序。

但是任意处理程序都可以决定事件已经被完全处理,并停止冒泡。

用于停止冒泡的方法是 event.stopPropagation()

依旧使用上面的案例

1
2
3
4
5
6
7
8
9
10
11
son.addEventListener('click',function(e){
alert('我是儿子')
e.stopPropagation()
})
father.addEventListener('click',function(){
alert('我是父盒子')
})
box.addEventListener('click',function(){
alert('我是最外面的盒子')

})

我们给里面的盒子添加了一个阻止冒泡,这样点击son盒子就只会弹出我是儿子,只是停止了传播,并不是阻止了所有的事件,点击在父盒子和外面的盒子上依旧是会有弹出框的

事件委托(事件委托,事件代理)

事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown……)的函数委托到另一个元素;

一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

举个例子,比如一个宿舍的同学同时快递到了,一种方法就是他们都傻傻地一个个去领取,还有一种方法就是把这件事情委托给宿舍长,让一个人出去拿好所有快递,然后再根据收件人一一分发给每个宿舍同学;

在这里,取快递就是一个事件,每个同学指的是需要响应事件的 DOM 元素,而出去统一领取快递的宿舍长就是代理的元素,所以真正绑定事件的是这个元素,按照收件人分发快递的过程就是在事件执行中,需要判断当前响应的事件应该匹配到被代理元素中的哪一个或者哪几个。

优点

减少内存消耗

试想一下,若果我们有一个列表,列表之中有大量的列表项,我们需要在点击列表项的时候响应一个事件;

1
2
3
4
5
6
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
</ul>

如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能;

因此,比较好的方法就是把这个点击事件绑定到他的父层,也就是 ul 上,然后在执行事件的时候再去匹配判断目标元素;

所以事件委托可以减少大量的内存消耗,节约效率。

1
2
3
4
5
ul.addEventListener('click', function (e) {
if (e.target.tarName === 'LI') {
e.target.style.color = 'red'
}
})