This gist is a simple no-brainer description of the 3 ways (actually 2.5) the Web handle events.
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.
<button onclick="console.log(event, 1)">click me</button>
- 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
- 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 eventrue
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)
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.
button.onclick = event => console.log(event, 2);
- 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
- 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 eventrue
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 withhandleEvent
is assigned
The suggested and most updated DOM Level 3 event allows features otherwise impossible to obtain with any previous alternative.
button.addEventListener('click', event => {
console.log(event, 3);
});
button.addEventListener('click', {
some: 'value',
handleEvent(event) {
console.log(event, 4);
console.log(this.some); // "value"
}
});
- it exposes a
currentTarget
reference to the current node out of the event but it also exposes athis
reference to thehandleEvent(){}
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 justtrue
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.
- it won't implicitly replace or override DOM Level 0 events attached via HTML template or via direct accessor such as
button.onclick = ...
, unlesscapture
phase is specified andevent.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
As you've said, it's in the comments now for those who wish to dig deeper. 👍