javascript 事件
介绍 (Introduction)
JavaScript in the browser uses an event-driven programming model.
浏览器中JavaScript使用事件驱动的编程模型。
Everything starts by following an event.
一切都始于事件。
The event could be the DOM is loaded, or an asynchronous request that finishes fetching, or a user clicking an element or scrolling the page, or the user types on the keyboard.
该事件可能是加载DOM,完成获取的异步请求,用户单击元素或滚动页面或用户在键盘上键入。
There are a lot of different kind of events.
有很多不同类型的事件。
事件处理程序 (Event handlers)
You can respond to any event using an Event Handler, which is a function that’s called when an event occurs.
您可以使用事件处理程序 ( Event Handler)响应任何事件, 事件处理程序是事件发生时调用的函数。
You can register multiple handlers for the same event, and they will all be called when that event happens.
您可以为同一事件注册多个处理程序,并且在该事件发生时将全部调用它们。
JavaScript offer three ways to register an event handler:
JavaScript提供了三种注册事件处理程序的方式:
内联事件处理程序 (Inline event handlers)
This style of event handlers is very rarely used today, due to its constraints, but it was the only way in the JavaScript early days:
由于其约束,今天很少使用这种事件处理程序样式,但这是JavaScript早期的唯一方法:
<a href="site.com" onclick="dosomething();">A link</a>
DOM事件处理程序 (DOM on-event handlers)
This is common when an object has at most one event handler, as there is no way to add multiple handlers in this case:
当一个对象最多具有一个事件处理程序时,这是很常见的,因为在这种情况下无法添加多个处理程序:
window.onload = () => {
//window loaded
}
It’s most commonly used when handling XHR requests:
在处理XHR请求时最常用:
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
//.. do something
}
You can check if an handler is already assigned to a property using if ('onsomething' in window) {}
.
您可以使用if ('onsomething' in window) {}
检查是否已将处理程序分配给属性。
使用addEventListener()
(Using addEventListener()
)
This is the modern way. This method allows to register as many handlers as we need, and it’s the most popular you will find:
这是现代方式 。 此方法允许注册所需数量的处理程序,并且它是最受欢迎的处理程序:
window.addEventListener('load', () => {
//window loaded
})
Note that IE8 and below did not support this, and instead used its own
attachEvent()
API. Keep it in mind if you need to support older browsers.请注意,IE8和更低版本不支持此功能,而是使用其自己的
attachEvent()
API。 如果您需要支持较旧的浏览器,请记住这一点。
聆听不同的元素 (Listening on different elements)
You can listen on window
to intercept “global” events, like the usage of the keyboard, and you can listen on specific elements to check events happening on them, like a mouse click on a button.
您可以在window
上侦听以拦截“全局”事件,例如键盘的使用,也可以侦听特定元素以检查发生在它们上面的事件,例如鼠标单击按钮。
This is why addEventListener
is sometimes called on window
, sometimes on a DOM element.
这就是为什么有时在window
有时在DOM元素上调用addEventListener
原因。
事件对象 (The Event object)
An event handler gets an Event
object as the first parameter:
事件处理程序获取一个Event
对象作为第一个参数:
const link = document.getElementById('my-link')
link.addEventListener('click', event => {
// link clicked
})
This object contains a lot of useful properties and methods, like:
该对象包含许多有用的属性和方法,例如:
target
, the DOM element that originated the eventtarget
,即引发事件的DOM元素type
, the type of eventtype
,事件类型stopPropagation()
, called to stop propagating the event in the DOMstopPropagation()
,被称为停止在DOM中传播事件
( 请参阅完整列表 )。
Other properties are provided by specific kind of events, as Event
is an interface for different specific events:
其他属性由特定类型的事件提供,因为Event
是不同特定事件的接口:
- … and others … 和别的
Each of those has a MDN page linked, so you can inspect all their properties.
每个属性都有一个链接的MDN页面,因此您可以检查其所有属性。
For example when a KeyboardEvent happens, you can check which key was pressed, in a readable format (Escape
, Enter
and so on) by checking the key
property:
例如,当发生KeyboardEvent时,您可以通过检查key
属性来以可读格式( Escape
, Enter
等)检查按下了哪个键:
window.addEventListener('keydown', event => {
// key pressed
console.log(event.key)
})
On a mouse event we can check which mouse button was pressed:
在发生鼠标事件时,我们可以检查按下了哪个鼠标按钮:
const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
// mouse button pressed
console.log(event.button) //0=left, 2=right
})
事件冒泡和事件捕获 (Event bubbling and event capturing)
Bubbling and capturing are the 2 models that events use to propagate.
冒泡和捕获是事件用于传播的两个模型。
Suppose your DOM structure is
假设您的DOM结构是
<div id="container">
<button>Click me</button>
</div>
You want to track when users click on the button, and you have 2 event listeners, one on button
, and one on #container
. Remember, a click on a child element will always propagate to its parents, unless you stop the propagation (see later).
您想跟踪用户何时单击按钮,并且有2个事件侦听器,一个在button
,一个在#container
。 请记住,单击子元素将始终传播到其父元素,除非您停止传播(请参阅下文)。
Those event listeners will be called in order, and this order is determined by the event bubbling/capturing model used.
这些事件侦听器将按顺序调用,此顺序由所使用的事件冒泡/捕获模型确定。
Bubbling means that the event propagates from the item that was clicked (the child) up to all its parent tree, starting from the nearest one.
冒泡表示事件从被单击的项(子项)一直传播到其所有父树,从最近的树开始。
In our example, the handler on button
will fire before the #container
handler.
在我们的示例中, button
上的处理程序将在#container
处理程序之前触发。
Capturing is the opposite: the outer event handlers are fired before the more specific handler, the one on button
.
捕获是相反的:外部事件处理程序在更具体的处理程序(一个on button
之前触发。
By default all events bubble.
默认情况下,所有事件都会冒泡 。
You can choose to adopt event capturing by applying a third argument to addEventListener, setting it to true
:
您可以通过将第三个参数应用于addEventListener并将其设置为true
来选择采用事件捕获:
document.getElementById('container').addEventListener(
'click',
() => {
//window loaded
},
true
)
Note that first all capturing event handlers are run.
请注意, 首先运行所有捕获事件处理程序 。
Then all the bubbling event handlers.
然后是所有冒泡事件处理程序。
The order follows this principle: the DOM goes through all elements starting from the Window object, and goes to find the item that was clicked. While doing so, it calls any event handler associated to the event (capturing phase).
顺序遵循此原则:DOM从Window对象开始遍历所有元素,然后查找被单击的项目。 这样做时,它将调用与事件关联的任何事件处理程序(捕获阶段)。
Once it reaches the target, it then repeats the journey up to the parents tree until the Window object, calling again the event handlers (bubbling phase).
一旦到达目标,它将重复直到父级树的旅程,直到Window对象,再次调用事件处理程序(冒泡阶段)。
停止传播 (Stopping the propagation)
An event on a DOM element will be propagated to all its parent elements tree, unless it’s stopped.
除非停止,否则DOM元素上的事件将传播到其所有父元素树。
<html>
<body>
<section>
<a id="my-link" ...>
A click event on a
will propagate to section
and then body
.
上的click事件a
会传播到section
,然后body
。
You can stop the propagation by calling the stopPropagation()
method of an Event, usually at the end of the event handler:
您可以通过通常在事件处理程序的末尾调用Event的stopPropagation()
方法来停止传播:
const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
// process the event
// ...
event.stopPropagation()
})
热门活动 (Popular events)
Here’s a list of the most common events you will likely handle.
这是您可能会处理的最常见事件的列表。
加载 (Load)
load
is fired on window
and the body
element when the page has finished loading.
load
是在发射window
和body
当页面完成加载元素。
鼠标事件 (Mouse events)
click
fires when a mouse button is clicked. dblclick
when the mouse is clicked two times. Of course in this case click
is fired just before this event. mousedown
, mousemove
and mouseup
can be used in combination to track drag-and-drop events. Be careful with mousemove
, as it fires many times during the mouse movement (see throttling later)
click
鼠标按钮时触发click
。 两次单击鼠标时,请单击dblclick
。 当然,在这种情况下,在此事件之前会触发click
。 mousedown
, mousemove
和mouseup
可以组合使用来跟踪拖放事件。 请小心mousemove
,因为它在鼠标移动期间会触发多次(请参阅稍后进行节流 )
键盘事件 (Keyboard events)
keydown
fires when a keyboard button is pressed (and any time the key repeats while the button stays pressed). keyup
is fired when the key is released.
keydown
当键盘按钮被按下(以及任何时间关键重复在按钮保持按下)火灾。 释放键时会触发keyup
。
滚动 (Scroll)
The scroll
event is fired on window
every time you scroll the page. Inside the event handler you can check the current scrolling position by checking window.scrollY
.
每次滚动页面时,都会在window
上触发scroll
事件。 在事件处理程序内部,您可以通过检查window.scrollY
来检查当前滚动位置。
Keep in mind that this event is not a one-time thing. It fires a lot of times during scrolling, not just at the end or beginning of the scrolling, so don’t do any heavy computation or manipulation in the handler - use throttling instead.
请记住,此事件不是一次性事件。 它在滚动过程中会触发很多次,而不仅仅是在滚动结束或开始时,所以不要在处理程序中进行任何繁重的计算或操作,而应使用节流 。
节流 (Throttling)
As we mentioned above, mousemove
and scroll
are two events that are not fired one-time per event, but rather they continuously call their event handler function during all the duration of the action.
正如我们上面提到的, mousemove
和scroll
是两个事件,每个事件不会一次触发,而是在动作的所有持续时间内连续调用它们的事件处理函数。
This is because they provide coordinates so you can track what’s happening.
这是因为它们提供了坐标,因此您可以跟踪正在发生的事情。
If you perform a complex operation in the event handler, you will affect the performance and cause a sluggish experience to your site users.
如果在事件处理程序中执行复杂的操作,则会影响性能并给站点用户带来缓慢的体验。
Libraries that provide throttling like Lodash implement it in 100+ lines of code, to handle every possible use case. A simple and easy to understand implementation is this, which uses setTimeout to cache the scroll event every 100ms:
像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
})
javascript 事件