JavaScript Events

Most programs don’t sit around waiting for you to do one specific thing. Things can happen at any time, anywhere, so we need a programming model that lets us be ready to do the right thing when something happens.

What is an Event?

An event is something that happens.

An event object is an object containing properties such as its type (click, open, changed, keypress, etc.), the time at which it was generated, how it should behave (e.g., should it propagate), and type-specific information such as the window coordinates for a click event.

Events are dispatched to event targets. You register event listeners (also called event handlers) with the targets. Listeners are functions that execute when the event lands at the target.

This is getting complicated, so we need to write programs. But first, let’s see which events are out there.

Browser Events

Here are some of the standard events in browser land:

Ambient Lightdevicelight
Battery statuschargingchange chargingtimechange dischargingtimechange levelchange
CSS Animationsanimationstart animationiteration animationend
CSS Transitionstransitionrun transitionstart transitionend transitioncancel
Clipboardcut copy paste
Device Orientationdevicemotion deviceorientation
Drag and Dropdragstart dragend dragenter dragleave drag dragover drop
Focusfocus blur
Formreset submit
Gamepadgamepadconnected gamepaddisconnected
Historypagehide pageshow popstate
IndexedDBcomplete success error abort versionchange upgradeneeded blocked
Inputchange input abort
Keyboardkeydown keypress keyup
Mediaseeking seeked suspend pause play waiting ended playing durationchange loadeddata emptied loadedmetadata ratechange timeupdate canplaythrough canplay stalled volumechange
Media Capturedevicechange
Misclanguagechange invalid input hashchange
Mouseclick dblclick mouseup mousedown mousemove mouseover mouseenter mouseout mouseleave wheel select contextmenu show
Networkonline offline
Notificationsnotificationclick
Pagevisibilitychange
Pointerlostpointercapture gotpointercapture pointerenter pointerleave pointermove pointerdown pointercancel pointerup pointerover pointerout pointerlockerror pointerlockchange
Printbeforeprint afterprint
Progressloadstart load progress timeout abort error loadend
Proximitydeviceproximity userproximity
Pushpushsubscriptionchange push
Resourceload beforeunload unload cached error abort
Resource Timingresourcetimingbufferfull
SVGbeginEvent endEvent repeatEvent SVGLoad SVGUnload SVGResize SVGScroll SVGZoom SVGError SVGAbort
Screen Orientationorientationchange
Selectionselectstart selectionchange
Server Sentopen message error
Service Workersmessage
Text Compositioncompositionstart compositionend compositionupdate
Touchtouchstart touchend touchmove touchcancel
Updatenoupdate obsolete updateready downloading checking error
Viewresize scroll fullscreenchange fullscreenerror
Web App Manifestappinstalled
Web Audioaudioprocess complete ended
Web Messagingmessage
Web Speechresume error error soundstart soundend speechstart speechend voiceschanged start end nomatch result mark boundary audioend audiostart start end pause
Web Storagestorage
Web Socketsopen message error close
Web Workersmessage
XMLHttpRequestreadystatechange

Many browsers, and their plugins, will create tons of additional non-standard events. You should checkout the Event Reference page at MDN, which lists well over 100 standard and non-standard events.

Handling Events in the Browser

See the Pen Transition Demo by Ray Toal (@rtoal) on CodePen.

In the browser, the possible event targets are:

The methods on event targets are: addEventListener, removeEventListener and dispatchEvent.

Adding an Event Listener

Here’s the simplest case: We attach an event handler for the click event to a <p> element. When the event happens, the handler is called with the event object as the argument.

<body>
  <p id="theP">Hello click me.</p>
  <script>
    document.getElementById('theP').addEventListener('click', (e) => {
      console.log(e.type, e.target.tagName, e.clientX, e.clientY, e.isTrusted);
    });
  </script>
</body>

When you click on the paragraph, you will see something like:

click P 28 16 true

Propagation

But wait. That <p> element was nested inside a <body> element, so doesn’t the body element get the event, too? If so, which one—the inner or outer element—would get the event first?

Actually all of the possible targets get the event...twice! If you click on the paragraph element, a click event is sent first to the window, then to the document, then to the body, then to the paragraph, then to the paragraph again, then the body again, the document again, and finally to the window again. Did you notice how this event propagation happens in two phases? First, the event propagates outside-to-inside in the capturing phase, then inside-to-outside in the bubbling phase. Let’s explore:

events.html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Exploring Events</title>
    <style>
      body, div, p, span {margin: 20px; padding: 8px}
      body {background-color: #eeffee}
      div {background-color: #ffffee}
      p {background-color: #eeffff}
      span {background-color: #ffeeff}
    </style>
  </head>
  <body id="thebody">
    <div id="thediv">
      Before
      <p id="theparagraph">Hello you can <span id="thespan">click here</span></p>
      After
    </div>
    <script src="events.js"></script>
  </body>
</html>
events.js
function say(arrow, e, name) {
  console.log(`${arrow} ${name} received ${e.type} during ${e.eventPhase}`);
}

function addListeners(el, name) {
  el.addEventListener('click', e => say('⬇︎', e, name), true);
  el.addEventListener('click', e => say('⬆︎', e, name), false);
}

document.querySelectorAll('[id]').forEach(el => addListeners(el, el.id));
addListeners(document, 'The document');
addListeners(window, 'The window');

Note the third argument to addEventListener: if true, the handler kicks in during the capture phase; if false, in the bubbling phase. We’ve added capture and bubble listeners to the document and all four elements, allowing us to see the propagation in action. Here’s what we see when we click on the innermost span:

capturebubble.png

This example shows off some properties of the event object:

Get the complete details at MDN or the DOM Living Standard.

Controlling Propagation

Within a handler, you can call e.stopPropagation(). It does just what you think it does.

Exercise: Modify the above script so that propagation is stopped when reaching the div during the capturing phase.
Exercise: Modify the above script so that propagation is stopped when reaching the div during the bubbling phase.

Trusted Events

You can programmatically fire events yourself with dispatchEvent. Such events are not trusted. This is best learned with an example. Trusted clicks come from your mouse, trackpad, or other real device, while a timer in our code dispatches nontrusted click events every few seconds:

<body>
  <p id="theP">Hello click me.</p>
  <script>
    const p = document.getElementById('theP');
    p.addEventListener('click', (e) => {
      console.log(e.isTrusted ? 'REAL CLICK' : 'FAKE CLICK!!');
    });
    // Send fake clicks every five seconds
    setInterval(() => p.dispatchEvent(new Event('click')), 5000);
  </script>
</body>

Multiple Event Listeners Per Target

TODO

Removing Listeners

TODO

Default Behaviors and How to Prevent Them

TODO

Common Browser UI Recipes

Now that we know a bit about events,

TODO

Web Workers

TODO

Ajax Events

TODO

Events in Node.js

Node.js is a JavaScript runtime system that provides access to files, processes, memory buffers, the console, the network, timers, etc.: all kinds of things for which event-driven programming shines.

Official documentation on Node.js Events.

TODO