Skip to content

Instantly share code, notes, and snippets.

@ttntm
Forked from WebReflection/web_events.md
Created October 23, 2023 12:41
Show Gist options
  • Save ttntm/f555460c379d769295b8f0e3344dd492 to your computer and use it in GitHub Desktop.
Save ttntm/f555460c379d769295b8f0e3344dd492 to your computer and use it in GitHub Desktop.

This gist is a simple no-brainer description of the 3 ways (actually 2.5) the Web handle events.

<tag onclick />

The declarative inline HTML event listener is mostly an indirection of DOM Level 0 events, meaning this simply uses the equivalent of tag.onclick = listener behind the scene.

Example

<button onclick="console.log(event, 1)">click me</button>

Advantages

  • it's declarative on the template
  • it's great for RAD and prototypes development
  • it exposes a this reference to the current node out of the box
  • unless overridden or dropped, it can prevent any other bubbling listener if event.stopImmediatePropagation() is called

Disadvantages

  • it's an indirect evaluation that trust the global context and its leaked/exposed listeners
  • it uses a somehow deprecated temporary runtime global event reference out of evaluation
  • it can be easily replaced via button.onclick = ... so it doesn't guarantee at all it will be executed
  • it's limited as it cannot have options such as { once: true } or { passive: true } or even true to signal capturing instead of bubbling
  • it's limited as it doesn't allow at all { handleEvent(){} } use cases
  • it might be entirely disabled or throw errors out of CSP - Content Security Policy features (note this is not true for custom defined events such as lib:click="..." and friends, as these are not natively understood by the browser)

tag.onclick = listener

The DOM Level 0 accessor / listener easily allows one-off listeners to be attached, replaced, or detached (pass null) to any element but it's mostly as limited as the declarative approach.

Example

button.onclick = event => console.log(event, 2);

Advantages

  • it's great for RAD and prototypes development
  • it exposes a this reference to the current node out of the box, unless the listener is an arrow function (or a previously bound one)
  • it's great to replace or drop listeners on the fly
  • it never requires a refrence to the current listener to be removed or replaced
  • it can override the declerative HTML template approach in a snap
  • unless overridden or dropped, it can prevent any other bubbling listener if event.stopImmediatePropagation() is called

Disadvantages

  • it can be easily replaced via button.onclick = ... so it doesn't guarantee at all it will be executed
  • it's limited as it cannot have options such as { once: true } or { passive: true } or even true to signal capturing instead of bubbling
  • it's limited as it doesn't allow at all { handleEvent(){} } use cases and it silently fails if an object with handleEvent is assigned

tag.addEventListener('click', listener[, options])

The suggested and most updated DOM Level 3 event allows features otherwise impossible to obtain with any previous alternative.

Example

button.addEventListener('click', event => {
  console.log(event, 3);
});

button.addEventListener('click', {
  some: 'value',
  handleEvent(event) {
    console.log(event, 4);
    console.log(this.some); // "value"
  }
});

Advantages

  • it exposes a currentTarget reference to the current node out of the event but it also exposes a this reference to the handleEvent(){} listener, if any, falling back to the current node otherwise (if the listener is not an arrow function or an already bound callback)
  • it allows an extra options argument to enable { once: true }, { passive: true }, or any other extra feature available today and tomorrow
  • it allows handleEvent pattern for instances and object literals
  • it guarantees the listener is executed, unless the listener reference is explicitly removed or it's in the bubbling phase and DOM Level 0 events prevented it
  • if specified as capturing phase ({ capture: true } or just true as option) instead of bubbling (default), it can prevent any DOM Level 0 event to trigger
  • if specified for the capturing phase you can capture any event, even those that don't bubble up such as focus, at the document level, or at any level you desire.

Disadvantages

  • it won't implicitly replace or override DOM Level 0 events attached via HTML template or via direct accessor such as button.onclick = ..., unless capture phase is specified and event.stopImmediatePropagation() is invoked
  • if not well orchestrated, there's no way to remove a previously added listener as it must be the same reference or unless the signal option is passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment