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
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