Event constructor
Build-in event classes form a hierarchy, similar to DOM element classes. The root is the built-in Event class.
We can create Event objects like this:
let event = new Event(type[, options]);
Arguments:
- type – event type, a string like
"click"or our own like"my-event". - options– the object with two optional properties:
bubbles: true/false– iftrue, then the event bubbles.cancelable: true/false– iftrue, then the “default action” may be prevented. Later we’ll see what it means for custom events.
- By default both are false:
{bubbles: false, cancelable: false}.
dispatchEvent
After an event object is created, we should “run” it on an element using the call elem.dispatchEvent(event).
Then handlers react on it as if it were a regular browser event. If the event was created with the bubbles flag, then it bubbles.
In the example below the click event is initiated in JavaScript. The handler works same way as if the button was clicked:
<button id="elem" onclick="alert('Click!');">Autoclick</button><script>let event = new Event("click");elem.dispatchEvent(event);</script>
event.isTrusted
There is a way to tell a “real” user event from a script-generated one.
The property event.isTrusted is true for events that come from real user actions and false for script-generated events.
Custom events
For our own, completely new events types like "hello" we should use new CustomEvent. Technically CustomEvent is the same as Event, with one exception.
In the second argument (object) we can add an additional property detail for any custom information that we want to pass with the event.
For instance:
<h1 id="elem">Hello for John!</h1><script>// additional details come with the event to the handlerelem.addEventListener("hello", function(event) {alert(event.detail.name);});elem.dispatchEvent(new CustomEvent("hello", {detail: { name: "John" }}));</script>
The detail property can have any data. Technically we could live without, because we can assign any properties into a regular new Event object after its creation. But CustomEvent provides the special detail field for it to evade conflicts with other event properties.
Besides, the event class describes “what kind of event” it is, and if the event is custom, then we should use CustomEvent just to be clear about what it is.
event.preventDefault()
Many browser events have a “default action”, such as nagivating to a link, starting a selection, and so on.
For new, custom events, there are definitely no default browser actions, but a code that dispatches such event may have its own plans what to do after triggering the event.
By calling event.preventDefault(), an event handler may send a signal that those actions should be canceled.
In that case the call to elem.dispatchEvent(event) returns false. And the code that dispatched it knows that it shouldn’t continue.
Let’s see a practical example – a hiding rabbit (could be a closing menu or something else).
Below you can see a #rabbit and hide() function that dispatches "hide" event on it, to let all interested parties know that the rabbit is going to hide.
Any handler can listen for that event with rabbit.addEventListener('hide',...) and, if needed, cancel the action using event.preventDefault(). Then the rabbit won’t disappear:
<pre id="rabbit">|\ /|\|_|//. .\=\_Y_/={>o<}</pre><button onclick="hide()">Hide()</button><script>// hide() will be called automatically in 2 secondsfunction hide() {let event = new CustomEvent("hide", {cancelable: true // without that flag preventDefault doesn't work});if (!rabbit.dispatchEvent(event)) {alert('The action was prevented by a handler');} else {rabbit.hidden = true;}}rabbit.addEventListener('hide', function(event) {if (confirm("Call preventDefault?")) {event.preventDefault();}});</script>
Please note: the event must have the flag cancelable: true, otherwise the call event.preventDefault() is ignored.
Events-in-events are synchronous
Usually events are processed asynchronously. That is: if the browser is processing onclick and in the process a new event occurs, then it waits until the onclick processing is finished.
The exception is when one event is initiated from within another one.
Then the control jumps to the nested event handler, and after it goes back.
For instance, here the nested menu-open event is processed synchronously, during the onclick:
