JavaScript-DOM事件
事件监听
结合 DOM 使用事件时,需要为 DOM 对象添加事件监听,等待事件发生(触发)时,便立即调用一个函数。
元素对象.addEventListener('事件类型', 要执行的函数)
1 | <body> |
完成事件监听分成3个步骤:
- 获取 DOM 元素
- 通过
addEventListener
方法为 DOM 节点添加事件监听 - 等待事件触发,如用户点击了某个按钮时便会触发
click
事件类型 - 事件触发后,相对应的回调函数会被执行
事件监听三要素:
- 事件源:哪个DOM元素被事件触发,要获取DOM元素
- 事件类型:用什么方式触发,比如鼠标单击
click
、鼠标经过mouseover
等 - 事件调用的函数:要做什么
事件类型
鼠标事件:
click
:鼠标点击mouseenter
:鼠标经过mouseleave
:鼠标离开
1 | <body> |
焦点事件:
focus
:获得焦点blur
:失去焦点
键盘事件:
Keydown
:键盘按下触发Keyup
:键盘抬起触发
文本事件:
input
:用户输入事件
事件对象
任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象。
元素.addEventListener('click', function(e){ }) // e就是事件对象
事件回调函数的第1个参数即所谓的事件对象,通常习惯性的将这个对数命名为 event
、ev
、e
。
1 | <body> |
接下来简单看一下事件对象中包含了哪些有用的信息:
ev.type
:当前事件的类型ev.clientX/Y
:光标相对浏览器窗口左上角的位置ev.offsetX/Y
:光标相于当前 DOM 元素左上角的位置key
:用户按下的键盘值
注:在事件回调函数内部通过 window.event
同样可以获取事件对象。
环境对象
环境对象指的是函数内部特殊的变量 this
,它代表着当前函数运行时所处的环境。
1 | <script> |
结论:
this
本质上是一个变量,数据类型为对象- 函数的调用方式不同
this
变量的值也不同 - 【谁调用
this
就是谁】是判断this
值的粗略规则 - 函数直接调用时实际上
window.sayHi()
所以this
的值为window
回调函数
如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数。
1 | <script> |
函数 bar
做参数传给了 foo
函数,bar
就是所谓的回调函数了!!!
我们回顾一下间歇函数 setInterval
1 | <script> |
fn
函数做为参数传给了 setInterval
,这便是回调函数的实际应用了,结合刚刚学习的函数表达式上述代码还有另一种更常见写法。
1 | <script> |
结论:
- 回调函数本质还是函数,只不过把它当成参数使用
- 使用匿名函数作为回调函数比较常见
事件流
事件流指的是事件完整执行过程中的流动路径
了解事件的执行过程有助于加深对事件的理解,提升开发实践中对事件运用的灵活度。
如上图所示,任意事件被触发时总会经历两个阶段:捕获阶段和冒泡阶段。
简单的说,捕获阶段是从父到子的传导过程,冒泡阶段是从子向父的传导过程。
实际工作都是使用事件冒泡为主
事件捕获和冒泡
从DOM的根元素开始执行对应的事件(从外到里)
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
1 | <body> |
执行上述代码后发现,当单击事件触发时,其祖先元素的单击事件也相继触发
结合事件流的特征,我们知道当某个元素的事件被触发时,事件总是会先经过其祖先才能到达当前元素,然后再由当前元素向祖先传递,事件在流动的过程中遇到相同的事件便会被触发。
再来关注一个细节就是事件相继触发的执行顺序,事件的执行顺序是可控制的,即可以在捕获阶段被执行,也可以在冒泡阶段被执行。
如果事件是在冒泡阶段执行的,我们称为冒泡模式,它会先执行子盒子事件再去执行父盒子事件,默认是冒泡模式(如上图所示)。
如果事件是在捕获阶段执行的,我们称为捕获模式,它会先执行父盒子事件再去执行子盒子事件。
将第三个参数改为true
,即捕获模式
结论:
addEventListener
第3个参数决定了事件是在捕获阶段触发还是在冒泡阶段触发addEventListener
第3个参数为true
表示捕获阶段触发,false
表示冒泡阶段触发,默认值为false
- 事件流只会在父子元素具有相同事件类型时才会产生影响
- 绝大部分场景都采用默认的冒泡模式(其中一个原因是早期 IE 不支持捕获)
阻止冒泡
阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。
事件对象.stopPropagation()
注意:前提是阻止事件对象冒泡需要拿到事件对象,在冒泡模式和捕获模式均有效
1 | <body> |
结论:事件对象中的 ev.stopPropagation
方法,专门用来阻止事件冒泡。
有时候需要阻止默认行为的发生,例如阻止链接跳转,表单域跳转等,就需要使用另一个
e.preventDefault()
可以阻止默认行为
解绑事件
on事件方式,直接使用null覆盖就可以实现事件的解绑
1 | btn.onclick = function() { |
如果是使用了addEventListener()
方式,必须使用:
1 | function fn() { |
注意:匿名函数无法被解绑
鼠标经过事件:
mouseover
和mouseout
会有冒泡效果mouseenter
和mouseleave
没有冒泡效果 (推荐)
两种注册事件的区别:
事件委托
事件委托是利用事件流的特征解决一些现实开发需求的知识技巧,主要的作用是提升程序效率。
原理:
- 给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
实现:
事件对象.target.tagName
可以获得真正触发事件的元素
大量的事件监听是比较耗费性能的,如下代码所示
1 | <script> |
事件对象中的属性 target
或 srcElement
属性表示真正触发事件的元素,它是一个元素类型的节点。
优化过的代码只对祖先元素添加事件监听,相比对 10000 个元素添加事件监听执行效率要高许多!!!
其他事件
页面加载事件
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
有些时候需要等页面资源全部处理完了做一些事情
事件名:load
监听页面所有资源加载完毕:
- 给
window
添加load
事件
不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
1 | window.addEventListener('load', function() { |
当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded
事件被触发,而无需等待样式表、图像等完全加载
事件名:DOMContentLoaded
监听页面DOM加载完毕:
- 给
document
添加DOMContentLoaded
事件
1 | document.addEventListener('DOMContentLoaded', function() { |
元素滚动事件
滚动条在滚动的时候持续触发的事件
很多网页需要检测用户把网页滚动到某个位置后做一些处理,比如固定导航栏、返回顶部等
事件名:scroll
监听整个页面滚动:
- 给
window
或document
添加scroll
事件
1 | window.addEventListener('scroll', function() { |
- 监听某个元素的内部滚动,直接给某个元素添加即可
获取位置
scrollLeft
和scrollTop
(属性)- 获取被卷去的大小
- 获取元素内容往左、往上滚出去看不见的距离
- 这两个值是可读写的
- 尽量在
scroll
事件里面获取被卷去的距离
1 | div.addEventListener('scroll', function() { |
注意:document.documentElement
HTML文档返回对象为HTML元素
滚动到指定坐标
scrollTo()方法可以将内容滚动到指定的坐标
语法:元素.scrollTo(x, y)
1 | window.scrollTo(0, 1000) |
页面尺寸事件
会在窗口尺寸改变的时候触发事件:
事件名:resize
1 | window.addEventListener('resize', function() { |
- 获取宽高
- 获取元素的可见部分宽高(不包括边框、margin、滚动条等)
clientWidth
和clientHeight
元素尺寸与位置
获取宽高:
- 获取元素的自身宽高、包含元素自身设置的宽高、padding、border
offsetWidth
和offsetHeight
- 获取出来的是数值,方便计算
- 注意:获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0
获取位置:
- 获取元素距离自己定位父级元素的左、上距离
offsetLeft
和offsetTop
- 注意是只读属性
element.getBoundingClientRect()
方法返回元素的大小及其相对于视口的位置
属性 | 作用 | 说明 |
---|---|---|
scrollLeft和scrollTop | 被卷去的头部和左侧 | 配合页面滚动来用,可读写 |
clientWidth和clientHeight | 获得元素宽度和高度 | 不包含border,margin,滚动条 用于js获取元素大小,只读属性 |
offserWidth和offsetHeight | 获取元素宽度和高度 | 包含border,padding,滚动条等,只读 |
offsetLeft和offsetTop | 获取元素距离自己定位父级元素的左、上距离 | 获取元素位置的时候使用,只读属性 |