ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[[参考文档]](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events) **events** are actions or occurrences that happen in the system you are programming — the system produces (or "fires") a signal of some kind when an event occurs, and also provides a mechanism by which some kind of action can be automatically taken (that is, some code running) when the event occurs. For example in an airport when the runway is clear for a plane to take off, a signal is communicated to the pilot, and as a result, they commence piloting the plane. `signal`->`action` * Events are not really part of the core JavaScript — they are defined in browser Web APIs. * The different contexts in which JavaScript is used have different event models — from Web APIs to other areas such as browser `WebExtensions` and `Node.js` (server-side JavaScript). ---- 目录: [TOC] ---- ## Web Event In the case of the Web, events are fired inside the browser window, and tend to be attached to a specific item that resides in it — this might be a single element, set of elements, the HTML document loaded in the current tab, or the entire browser window. There are a lot of different types of events that can occur, for example: * `mouse`: The user clicking the mouse over a certain element or hovering the cursor over a certain element. * `keyboard`: The user pressing a key on the keyboard. * `browser window`: The user resizing or closing the browser window. * `page`: A web page finishing loading. * `form`: A form being submitted. * `video`: A video being played, or paused, or finishing play. * `error`: An error occurring. MDN [`Event reference`](https://developer.mozilla.org/en-US/docs/Web/Events) gathered a lot of events that can be responded to. Each available event has an **event handler**, which is a block of code (usually a JavaScript function that you as a programmer create) that will be run when the event fires. When such a block of code is defined to be run in response to an event firing, we say we are **registering an event handler**. Note that event handlers are sometimes called **event listeners** — they are pretty much interchangeable for our purposes, although strictly speaking, they work together. The listener listens out for the event happening, and the handler is the code that is run in response to it happening. >[warning] Web events are not part of the core JavaScript language — they are defined as part of the APIs built into the browser. >[warning] Events are not unique to JavaScript — most programming languages have some kind of event model, and the way the model works often differs from JavaScript's way. **Events can differ in different programming environments.** In fact, the event model in JavaScript for web pages differs from the event model for JavaScript as it is used in other environments. [**`Node.js`**](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs) is a very popular JavaScript runtime that enables developers to use JavaScript to build network and server-side applications. The [Node.js event model](https://nodejs.org/docs/latest-v5.x/api/events.html) relies on listeners to listen for events and emitters to emit events periodically — it doesn't sound that different, but the code is quite different, making use of functions like `on()` to register an event listener, and `once()` to register an event listener that unregisters after it has run once. The [HTTP connect event docs](https://nodejs.org/docs/latest-v8.x/api/http.html#http_event_connect) provide a good example of use. [**`WebExtensions`**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions) is a technology to use JavaScript to build cross-browser add-ons — browser functionality enhancements.The event model is similar to the web events model, but a bit different — event listeners properties are camel-cased (such as `onMessage` rather than `onmessage`), and need to be combined with the `addListener` function. See the [runtime.onMessage page](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/onMessage#Examples) for an example. ## Ways of using web events There are a number of ways in which you can add event listener code to web pages. ### Event handler properties These are the properties that exist to contain event handler code. For example: ~~~javascript var btn = document.querySelector('button'); btn.onclick = function() { var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')'; document.body.style.backgroundColor = rndCol; } ~~~ The [`onclick` ](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick) property is the event handler property being used in this situation. It is essentially a property like any other available on the button (e.g.[`btn.textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent), or [`btn.style`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style)), but it is a special type — when you set it to be equal to some code, that code is run when the event fires on the button. You could also set the handler property to be equal to a **named function name** . The following would work just the same: ~~~javascript var btn = document.querySelector('button'); function bgChange() { var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')'; document.body.style.backgroundColor = rndCol; } btn.onclick = bgChange; ~~~ Some events are very general and available nearly anywhere (for example an `onclick` handler can be registered on nearly any element), whereas some are more specific and only useful in certain situations (for example it makes sense to use [onplay](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/GlobalEventHandlers.onplay) only on specific elements, such as [`<video>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video)). ### Inline event handlers — don't use these The earliest method of registering event handlers found on the Web involved**event handler HTML attributes**(or**inline event handlers**) like the one shown bellow——the attribute value is literally the JavaScript code you want to run when the event occurs. ~~~html <button onclick="bgChange()">Press me</button> ~~~ ~~~javascript function bgChange() { var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')'; document.body.style.backgroundColor = rndCol; } ~~~ The above example invokes a function defined inside a `<script>` element on the same page, but you could also insert JavaScript directly inside the attribute, for example: ~~~html <button onclick="alert('Hello, this is my old-fashioned event handler!');">Press me</button> ~~~ For a start, it is not a good idea to mix up your HTML and your JavaScript, as it becomes hard to parse — keeping your JavaScript all in one place is better; if it is in a separate file you can apply it to multiple HTML documents. With JavaScript, you could easily add an event handler function to all the buttons on the page no matter how many there were, using something like this: ~~~javascript var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length; i++) { buttons[i].onclick = bgChange; } //or buttons.forEach(function(button) { button.onclick = bgChange; }); ~~~ >[info] **Note**: Separating your programming logic from your content also makes your site more friendly to search engines. ### `addEventListener()` and `removeEventListener()` [`addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener): a new function,which is provided for browsers in the newest type of event mechanism that is defined in the[Document Object Model (DOM) Level 2 Events](https://www.w3.org/TR/DOM-Level-2-Events/) Specification. This functions in a similar way to the event handler properties, but the syntax is obviously different. We could rewrite our random color example to look like this: ~~~javascript var btn = document.querySelector('button'); function bgChange() { var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')'; document.body.style.backgroundColor = rndCol; } btn.addEventListener('click', bgChange); ~~~ Inside the`addEventListener()`function, we specify two parameters — the name of the event we want to register this handler for, and the code that comprises the handler function we want to run in response to it. Note that it is perfectly appropriate to put all the code inside the`addEventListener()`function, in an anonymous function, like this: ~~~javascript btn.addEventListener('click', function() { var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')'; document.body.style.backgroundColor = rndCol; }); ~~~ This mechanism has some advantages over the older mechanisms discussed earlier. 1. counterpart function, [`removeEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener), which removes a previously added listener. For example, this would remove the listener set in the first code block in this section: ~~~javascript btn.removeEventListener('click', bgChange); ~~~ For larger, more complex programs, this allows you to have the same button performing different actions in different circumstances — all you have to do is add or remove event handlers as appropriate. 2. you can also register multiple handlers for the same listener. The following two handlers would not be applied: ~~~javascript myElement.onclick = functionA; myElement.onclick = functionB; ~~~ The second line overwrites the value of`onclick`set by the first line. This would work, however: ~~~javascript myElement.addEventListener('click', functionA); myElement.addEventListener('click', functionB); ~~~ Both functions would now run when the element is clicked. ### What mechanism should I use? Of the three mechanisms, you definitely shouldn't use the HTML event handler attributes — these are outdated, and bad practice, as mentioned above. The other two are relatively interchangeable, at least for simple uses: * Event handler properties have less power and options, but better cross-browser compatibility (being supported as far back as Internet Explorer 8). You should probably start with these as you are learning. * DOM Level 2 Events (`addEventListener()`, etc.) are more powerful, but can also become more complex and are less well supported (supported as far back as Internet Explorer 9). You should also experiment with these, and aim to use them where possible. The main advantages of the third mechanism are that you can remove event handler code if needed, using`removeEventListener()`, and you can add multiple listeners of the same type to elements if required. For example, you can call `addEventListener('click', function() { ... })` on an element multiple times, with different functions specified in the second argument. This is impossible with event handler properties because any subsequent attempts to set a property will overwrite earlier ones. ## Other event concepts ### Event objects Sometimes inside an event handler function, you might see a parameter specified with a name such as `event`, `evt`, or simply `e`. This is called the **event object**, and it is automatically passed to event handlers to provide extra features and information. For example, let's rewrite our random color example again slightly: ~~~javascript function bgChange(e) { var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')'; e.target.style.backgroundColor = rndCol; console.log(e); } btn.addEventListener('click', bgChange); ~~~ `e.target`— which is the button itself. The`target`property of the event object is always a reference to the element that the event has just occurred upon. `e.target`is incredibly useful when you want to set the same event handler on multiple elements and do something to all of them when an event occurs on them. for example, we create 16 `<div>` elements using JavaScript. We then select all of them using `document.querySelectorAll()`, then loop through each one, adding an `onclick` handler to each that makes it so that a random color is applied to each one when clicked: ~~~html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Useful event target example</title> <style> div { background-color: red; height: 100px; width: 25%; float: left; } </style> </head> <body> <script> for(var i = 1; i &lt;= 16; i++) { var myDiv = document.createElement('div'); document.body.appendChild(myDiv); } function random(number) {return Math.floor(Math.random()*number);} function bgChange() { var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')'; return rndCol;} var divs = document.querySelectorAll('div'); for(var i = 0; i &lt; divs.length; i++) { divs[i].onclick = function(e) { e.target.style.backgroundColor = bgChange(); } } </script> </body> </html> ~~~ Most event handlers you'll encounter just have a standard set of properties and functions (methods) available on the event object; see the [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event ) object reference for a full list. Some more advanced handlers, however, add specialist properties containing extra data that they need to function. For example: The [Media Recorder API](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder_API), has a `dataavailable` event, which fires when some audio or video has been recorded and is available for doing something with (for example saving it, or playing it back). The corresponding [ondataavailable](https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/ondataavailable) handler's event object has a `data` property available containing the recorded audio or video data to allow you to access it and do something with it. ### Preventing default behavior Sometimes, you'll come across a situation where you want to prevent an event from doing what it does by default. The most common example is that of a web form, for example, a custom registration form. When you fill in the details and press the submit button, the natural behavior is for the data to be submitted to a specified page on the server for processing, and the browser to be redirected to a "success message" page of some kind (or the same page, if another is not specified.) we call the [`preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) function on the event object — which stops the form submission — and then display an error message in the paragraph below our form to tell the user what's wrong before the data submitted to a specified page on the server for processing. ### Event bubbling and capture Event bubbling and capture are two mechanisms that describe what happens when two handlers of the same event type are activated on one element. For example: ~~~html <button>Display video</button> <div class="hidden"> <video> <source src="rabbit320.mp4" type="video/mp4"> <source src="rabbit320.webm" type="video/webm"> <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p> </video> </div> ~~~ When the `<button>` is clicked, the video is displayed, by changing the class attribute on the `<div>` from `hidden` to `showing`(the example's CSS contains these two classes, which position the box off the screen and on the screen, respectively): ~~~javascript var btn = document.querySelector('button'); var videoBox = document.querySelector('div'); var video = document.querySelector('video'); btn.onclick = function() { videoBox.setAttribute('class','showing'); } videoBox.onclick = function() { videoBox.setAttribute('class','hidden'); }; video.onclick = function() { video.play(); }; ~~~ When an event is fired on an element that has parent elements (in this case, the `<video>` has the `<div>` as a parent), modern browsers run two different phases — the **capturing** phase and the **bubbling** phase. In the **capturing** phase: * The browser checks to see if the element's outer-most ancestor `<html>` has an `onclick` event handler registered on it in the capturing phase, and runs it if so. * Then it moves on to the next element inside `<html>` and does the same thing, then the next one, and so on until it reaches the element that was actually clicked on. `<html>`->`<video>` In the **bubbling** phase, the exact opposite occurs: * The browser checks to see if the element that was actually clicked on has an `onclick` event handler registered on it in the bubbling phase, and runs it if so. * Then it moves on to the next immediate ancestor element and does the same thing, then the next one, and so on until it reaches the `<html>` element. `<html>`<-`<video>` ![bubbling-capturing](https://img.kancloud.cn/9b/c7/9bc764f3cedca0b4d4eee3f8374d2ad8_960x452.png) :-: bubbling-capturing >[info] In modern browsers, by default, all event handlers are registered in the **bubbling phase**. The standard event object has a function available on it called [`stopPropagation()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation), which, when invoked on a handler's event object, makes it so that handler is run, but the event doesn't bubble any further up the chain, so no more handlers will be run. We can, therefore, fix our current problem by changing the second handler function in the previous code block to this: ~~~js video.onclick = function(e) { e.stopPropagation(); video.play(); }; ~~~ You can try making a local copy of the [show-video-box.html source code](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box.html) and fixing it yourself, or looking at the fixed result in [show-video-box-fixed.html](http://mdn.github.io/learning-area/javascript/building-blocks/events/show-video-box-fixed.html)(also see the[source code](https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box-fixed.html)here). >[info] **Note**: Why bother with both capturing and bubbling? Well, in the bad old days when browsers were much less cross-compatible than they are now, Netscape only used event capturing, and Internet Explorer used only event bubbling. When the W3C decided to try to standardize the behavior and reach a consensus, they ended up with this system that included both, which is the one modern browsers implemented. >[info] **Note**: As mentioned above, by default all event handlers are registered in the bubbling phase, and this makes more sense most of the time. If you really want to register an event in the capturing phase instead, you can do so by registering your handler using [`addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener), and setting the optional third property to `true`. ### Event delegation Bubbling also allows us to take advantage of **event delegation** — this concept relies on the fact that if you want some code to run when you click on any one of a large number of child elements, you can set the event listener on their parent and have events that happen on them bubble up to their parent rather than having to set the event listener on every child individually. Remember earlier that we said bubbling involves checking the element the event is fired on for an event handler first, then moving up to the element's parent, etc.? A good example is a series of list items — if you want each one of them to pop up a message when clicked, you can set the`click`event listener on the parent`<ul>`, and events will bubble from the list items to the`<ul>`. This concept is explained further on David Walsh's blog, with multiple examples — see [How JavaScript Event Delegation Works](https://davidwalsh.name/event-delegate). ## reference ### [MDN - Event Object](https://developer.mozilla.org/en-US/docs/Web/API/Event "The Event interface represents an event which takes place in the DOM.") ### [MDN - Event reference](https://developer.mozilla.org/en-US/docs/Web/Events)