- 前言
- 关于 ECMASCRIPT 发展史和现状
- ES6 带来的重大特性
- ES2016(ES7)的改进
- ES2017(ES8)带来的重大新特性
- ES2018(ES9)带来的重大新特性
- JavaScript 编码风格指南
- JavaScript 词法结构(构建块)
- JavaScript 变量
- JavaScript 数据类型
- JavaScript 表达式
- 原型继承
- 如何使用 JavaScript 中的 Classes(类)
- JavaScript 异常处理
- JavaScript 中的分号(;)
- JavaScript 中的引号
- JavaScript 字面量模板(Template Literals)指南
- JavaScript 中的 function(函数)
- JavaScript 箭头函数(Arrow Function)
- JavaScript 中的闭包(Closures)
- JavaScript 数组(Arrays)
- JavaScript 中的循环(Loops)
- JavaScript 中的事件(Events)
- JavaScript 中的事件循环(Event Loop)
- JavaScript 异步编程和回调
- 理解 JavaScript 中的 Promises
- 用 async 和 await 编写现代 JavaScript 异步代码
- JavaScript 中的 循环(Loops) 和 作用域(Scope)
- JavaScript 定时器 setTimeout() 和 setInterval()
- JavaScript 中的 this
- JavaScript 严格模式(Strict Mode)
- JavaScript 中的 立即执行函数表达式(IIFE)
- JavaScript 中的数学运算符
- JavaScript 中的 Math 对象
- 介绍 ES Modules(模块)
- 介绍 CommonJS
- JavaScript 术语表
JavaScript 中的事件(Events)
浏览器中的 JavaScript 使用事件驱动的编程模型。 一切都始于事件。 本节介绍 JavaScript 事件以及事件处理的工作原理。
浏览器中的 JavaScript 使用事件驱动的编程模型。
一切都始于事件。
事件可能是 DOM 已加载,或者是异步请求完成,或用户单击元素或滚动页面,或用户安心键盘。
有很多不同类型的事件。
事件处理器(Event handlers)
你可以使用事件处理程序响应任何事件,事件处理程序只是在事件发生时调用的函数。
你可以为同一事件注册多个处理程序,并在事件发生时调用它们。
JavaScript 提供了三种注册事件处理程序的方法:
内联事件处理程序
由于他自身的限制,这种类型的事件处理程序今天很少使用,但这是 JavaScript 早期的唯一方法:
<a href="site.com" onclick="dosomething();">A link</a>
DOM 事件处理器
当一个对象只有一个事件处理器时这种方法很常用,因为在这种情况下无法添加多个处理程序:
window.onload = () => {
//window loaded
}
它在处理 XHR 请求时最常用:
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
//.. do something
}
你可以使用 if ('onsomething' in window) {} 检查是否已将处理程序分配给某个属性。
使用 addEventListener()
这是 现代 方式。这种方法允许我们根据需求注册多个处理程序,你会发现它是绑定处理程序最受欢迎的方式:
window.addEventListener('load', () => {
//window loaded
})
注意:IE8 及以下版本不支持这个方法,可以使用
attachEvent()代替。如果你需要支持旧浏览器,请记住这一点。
监听不同的元素
你可以监听 window 来拦截“全局”事件,比如键盘的使用,你也可以监听特定元素上发生的事件,比如鼠标点击了某个按钮。
这也是为什么 addEventListener 有时候在 window 上调用,有时间在某个 DOM 元素上。
Event 对象
事件处理器会获得一个 Event 对象作为第一个参数:
const link = document.getElementById('my-link')
link.addEventListener('click', event => {
// link clicked
})
这个对象包含很多有用的属性和方法,比如:
target,事件发生的目标 DOM 元素type,事件类型stopPropagation(),调用以阻止 DOM 事件传播
( 查看完整清单 )
其它属性提供给特定的事件,Event 只是不同事件的一个接口:
上面的每一个都链接到了 MDN 页面,你可以在那查看它们所有的属性。
例如,当一个键盘事件发生时,你可以检查哪个键被按下,通过 key 属性值得到一个可读格式的值( Escape , Enter 等等):
window.addEventListener('keydown', event => {
// key pressed
console.log(event.key)
})
在鼠标事件中,我们可以检查按下了哪个鼠标按钮:
const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
// mouse button pressed
console.log(event.button) //0=left, 2=right
})
事件冒泡和事件捕捉
事件冒泡和事件捕捉是事件传播的两个模型。
假设你的 DOM 结构是这样的:
<div id="container"> <button>Click me</button> </div>
你希望跟踪用户何时单击该按钮,并且你有两个事件侦听器,一个在 button 上,另一个在 #container 上。 请记住,单击子元素将始终传播到其父元素,除非你停止事件传播(我们稍后会看到)。
这些事件侦听器会按照顺序调用,这个顺序通过事件冒泡/事件捕捉模型决定。
冒泡 意味着事件从被点击的元素(子元素)一直向上传播到所有祖先元素,从最近的一个开始。
在我们的例子中, button 上的处理器会在 #container 之前发生。
捕捉 恰恰相反:最外部的事件会在特定处理器之前发生,比如 button 的处理程序。。
默认采用事件冒泡模型。
你也可以选择使用事件捕捉,通过将 addEventListener 的第三个参数设为 true :
document.getElementById('container').addEventListener(
'click',
() => {
//window loaded
},
true
)
注意: 首先运行所有捕获事件处理程序。
然后是所有冒泡的事件处理程序。
这个顺序遵循这个原则:DOM 遍历从 Window 对象开始的所有元素,直到找到被点击的元素项。执行此操作时,调用与事件关联的任何事件处理程序(捕获阶段)。
一旦找到目标元素,它会重复这个过程直到回到 Window 对象,此时调用相应的事件处理器(冒泡阶段)。
这样图可以帮助你理解这个过程:

停止传播
DOM 元素上的事件将传播到其所有父元素上,除非手动停止传播:
<html>
<body>
<section>
<a id="my-link" ...>a 上的 click 事件会传播到 section 然后是 body 。
你可以调用 Event 的 stopPropagation() 方法来停止事件传播,通常放在事件处理程序的末尾(注:我个人喜好放在事件处理程序的开始处):
const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
// process the event
// ...
event.stopPropagation()
})
常见事件
以下可能是你会处理的最常见事件的列表。
load
页面加载完成后,在 window 和 body 元素上触发 load 事件。
鼠标事件
单击鼠标按钮时 click 事件触发。 单击鼠标两次时触发 dbclick 事件。 当然,在这种情况下, click 事件会在此事件之前触发。 mousedown , mousemove 和 mouseup 可以和拖动事件结合在一起。小心使用 mousemove ,因为它会在鼠标移动过程中触发很多次(稍后会看到节流)。
键盘事件
当按下键盘键时 keydown 事件就会触发(当按下按钮时,任何时候重复键)。当键被释放时,将触发 keyup 事件。
滚动(Scroll)
每次滚动页面时都会在 window 上触发 scroll 事件。在事件处理程序中,你可以通过检查 window.scrollY (Y 轴)(注:我个人喜好用 document.documentElement.scrollTop ) 来检查当前的滚动位置。
请记住,此事件不是一次性的事件。它在滚动期间会发生很多次,而不仅仅是在滚动的结尾或开始时,所以不要在处理程序中进行任何频繁的计算或操作 – 而是使用节流代替。
节流(Throttling)
如上所述, mousemove 和 scroll 这两个事件都不是一次性事件,而是在持续操作的时间内连续调用它们的事件处理函数。
这是因为它们提供坐标,因此你可以跟踪正在发生的事件。
如果你在这些事件处理器中进行复杂的操作,则会影响性能并导致站点用户体验不佳。
像 Lodash 这样的库提供了 100 行代码实现的节流函数来处理这个问题。一个简单易懂的实现是使用 setTimeout 每隔 100ms 缓存一次滚动事件:
let cached = null
window.addEventListener('scroll', event => {
if (!cached) {
setTimeout(() => {
//you can access the original event at `cached`
cached = null
}, 100)
}
cached = event
})
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论