We can use client-side JavaScript's event API to define and dispatch our events using CustomEvent
. It can be used to decouple modules, thus improving maintainability and readability. Assume our application needs to conduct a long computation or a network request on a regular basis, and the other functionality or user interface is unavailable while these operations are in progress. We should inform the user by showing a spinner. However, the long computation or a network request should not be concerned with how to notify the user. Instead, it may simply send one event to indicate that something is busy, followed by another event once it is no longer busy. Another user interface module can register event handlers for such events and respond appropriately.
Let's get started with the CustomEvent constructor. It takes two arguments; the first argument is the name of the event, and the second argument is the object which is optional. We can pass any data in the second argument detail
regarding the event.
Syntax
/**
* @typedef {object} Options
* @property {Any} detail Optional, default to null
* @property {boolean} bubbles Optional, default to false
* @property {boolean} cancelable Optional, default to false
* @property {boolean} composed Optional, default to false
*/
/**
* @param {string} name The name of the event
* @param {Options} options Optional
*/
new CustomEvent(name);
new CustomEvent(name, options);
Parameters
name { string }
The name of the event
options { object } optional
Object with the following fields:
detail { object } optional
Any data type that is associated with the event. The default is `null`.
bubbles { boolean } optional
A boolean value indicating whether the event bubbles. The default is false
.
cancelable {boolean} optional
A boolean value indicating whether the event can be cancelled. The default is false
.
Usage
Create a listener for this event.
document.addEventListener('busy', e => {
if (e.detail.isBusy) {
// Show loading spinner
} else {
// hide loading spinner
}
});
Create a custom event using the CustomEvent
constructor and then dispatch or trigger the event. We can pass data in the detail parameter.
const busyEvent = new CustomEvent('busy', {
detail: {
isBusy: true
}
});
document.dispatch(busyEvent);
Example
The following is an example of showing the loading spinner before the network request and hiding the loading spinner after the network request has finished.
/**
* Assume this function calls an network request
* And takes 60 seconds to finish it
*/
const longNetworkRequest = () => {
return new Promise((resolve, reject) => {
// Resolve after 60 seconds
setTimeout(resolve, 1000 * 60);
});
};
/**
* Dispatch busy event
* @param {boolean} isBusy
*/
const dispatchEvent = isBusy => {
document.dispatchEvent(new CustomEvent('busy', {
detail: {
isBusy
}
}))
};
const main = async () => {
// Notify the user interface that
// an network request is about to start
dispatchEvent(true);
try {
await longNetworkRequest();
} catch (err) {
// Handle error
} finally {
// After the network request has finished
// either successed or failed
// Notify the user interface about it
dispatchEvent(false);
}
};
Anywhere in your program, you can register a handler for busy
events. The following example show or hide the loading spinner.
document.addEventListener('busy', e => {
if (e.detail.isBusy) {
// Show loading spinner
} else {
// hide loading spinner
}
});