JavaScript 中的事件(Events) – JavaScript 完全手册(2018版)

10年服务1亿前端开发工程师

小编推荐:掘金是一个面向程序员的高质量技术社区,从 一线大厂经验分享到前端开发最佳实践,无论是入门还是进阶,来掘金你不会错过前端开发的任何一个技术干货。

注:本文为 《 JavaScript 完全手册(2018版) 》第22节,你可以查看该手册的完整目录。

浏览器中的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

你可以调用 EventstopPropagation() 方法来停止事件传播,通常放在事件处理程序的末尾(愚人码头注:我个人喜好放在事件处理程序的开始处):

const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
  // process the event
  // ...
  
  event.stopPropagation()
})

常见事件

以下可能是你会处理的最常见事件的列表。

load

页面加载完成后,在windowbody 元素上触发 load 事件。

鼠标事件

单击鼠标按钮时 click 事件触发。 单击鼠标两次时触发 dbclick 事件。 当然,在这种情况下,click 事件会在此事件之前触发。mousedownmousemovemouseup 可以和拖动事件结合在一起。小心使用 mousemove,因为它会在鼠标移动过程中触发很多次(稍后会看到节流)。

键盘事件

当按下键盘键时keydown 事件就会触发(当按下按钮时,任何时候重复键)。当键被释放时,将触发keyup 事件。

滚动(Scroll)

每次滚动页面时都会在 window 上触发 scroll 事件。在事件处理程序中,你可以通过检查 window.scrollY(Y轴)(愚人码头注:我个人喜好用 document.documentElement.scrollTop)来检查当前的滚动位置。

请记住,此事件不是一次性的事件。它在滚动期间会发生很多次,而不仅仅是在滚动的结尾或开始时,所以不要在处理程序中进行任何频繁的计算或操作 – 而是使用节流代替。

节流(Throttling)

如上所述,mousemovescroll 这两个事件都不是一次性事件,而是在持续操作的时间内连续调用它们的事件处理函数。

这是因为它们提供坐标,因此你可以跟踪正在发生的事件。

如果你在这些事件处理器中进行复杂的操作,则会影响性能并导致站点用户体验不佳。

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
})

如果你觉得本文对你有帮助,那就请分享给更多的朋友
关注「前端干货精选」加星星,每天都能获取前端干货
赞(1) 打赏
未经允许不得转载:WEB前端开发 » JavaScript 中的事件(Events) – JavaScript 完全手册(2018版)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

前端开发相关广告投放 更专业 更精准

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏