Toast.js, a Library for Toast messages

Last week I created a small library for creating Toast messages. Here, I thought I would share my process in creating it and the code behind it.

The HTML #

The Toast is made up of two elements. The Toast message itself, .toastjs, and a container element, .toastjs-container.

<div class="toastjs-container" role="alert" aria-hidden="true">
<div class="toastjs"><!-- Content here --></div>
</div>

Until a Toast is being displayed, the container is hidden using the aria-hidden attribute.

The CSS #

To create the sliding in/out animation of the Toast, a transform is applied to the Toast container element. By default, the .toastjs-container is positioned off-screen to the left.

.toastjs-container {
transform: translateX(-150%);
transition: transform 1s;

/* Other styles */
position: fixed;
bottom: 30px;
left: 30px;
width: calc(100% - 60px);
max-width: 400px;
}

When the Toast message is being displayed, as is determined by it's aria-hidden attribute, the element is positioned to a normal state.

.toastjs-container[aria-hidden="false"] {
transform: translateX(0%);
}

The JavaScript #

The Toast is a custom Toast prototype. There are five main methods behind it.

Toast.prototype._createElements #

This function creates the Toast element and it's container, with all the appropriate attributes and classes. It also appends it to the body element.

Toast.prototype._createElements = function() {
return new Promise((resolve, reject) => {

this.toastContainerEl = document.createElement('div');
this.toastContainerEl.classList.add('toastjs-container');
this.toastContainerEl.setAttribute('role', 'alert');
this.toastContainerEl.setAttribute('aria-hidden', true);

this.toastEl = document.createElement('div');
this.toastEl.classList.add('toastjs');

this.toastContainerEl.appendChild(this.toastEl);
document.body.appendChild(this.toastContainerEl);

setTimeout(() => resolve(), 500);

})
};

Toast.prototype._addEventListeners #

This function adds event listeners to the buttons within the toast. By default, there is at least a close button. Optionally, there are custom buttons with custom callback functions that should be applied.

Toast.prototype._addEventListeners = function() {

document.querySelector('.toastjs-btn--close').addEventListener('click', () => {
this._close();
})

if ( this.options.customButtons ) {
const customButtonsElArray = Array.prototype.slice.call( document.querySelectorAll('.toastjs-btn--custom') );
customButtonsElArray.map( (el, index) => {
el.addEventListener('click', (event) => this.options.customButtons[index].onClick(event) );
});
}

};

Toast.prototype._close #

This is the function that is called when the Toast is closed. It first sets the correct aria-hidden attribute on the Toast container, then resets the main Toast element.

Toast.prototype._close = function() {
return new Promise((resolve, reject) => {
this.toastContainerEl.setAttribute('aria-hidden', true);
setTimeout(() => {

this.toastEl.innerHTML = '';
this.toastEl.classList.remove('default', 'success', 'warning', 'danger');

if ( this.focusedElBeforeOpen ) {
this.focusedElBeforeOpen.focus();
}

resolve();

}, 1000);
});
};

Toast.prototype._open #

This is the function that is called when the Toast is opened. It does the following things -

  • Sets the correct aria-hidden attribute to the Toast container
  • Adds the correct class to the Toast element
  • Adds the correct message content to the Toast element
  • Creates any custom buttons if specified
Toast.prototype._open = function() {

this.toastEl.classList.add(this.options.type);
this.toastContainerEl.setAttribute('aria-hidden', false);

let customButtons = '';
if ( this.options.customButtons ) {
customButtons = this.options.customButtons.map( (customButton, index) => {
return `<button type="button" class="toastjs-btn toastjs-btn--custom">${customButton.text}</button>`
} )
customButtons = customButtons.join('');
}

this.toastEl.innerHTML = `
<p>
${this.options.message}</p>
<button type="button" class="toastjs-btn toastjs-btn--close">Close</button>
${customButtons}
`
;

this.focusedElBeforeOpen = document.activeElement;
document.querySelector('.toastjs-btn--close').focus();
};

Toast.prototype._init #

Finally, this is the function that initialises the Toast. It calls all the other functions in their correct sequence.

Toast.prototype._init = function() {
Promise.resolve()
.then(() => {
if ( this.toastContainerEl ) {
return Promise.resolve();
}
return this._createElements();
})
.then(() => {
if ( this.toastContainerEl.getAttribute('aria-hidden') == 'false' ) {
return this._close();
}
return Promise.resolve();
})
.then(() => {
this._open();
this._addEventListeners();
})
};

You can view the full source for the library here, or view a demo.

Keep in touch KeepinTouch

Subscribe to my Newsletter 📥

Receive quality articles and other exclusive content from myself. You’ll never receive any spam and can always unsubscribe easily.

Elsewhere 🌐