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

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


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.


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.setAttribute('role', 'alert');
        this.toastContainerEl.setAttribute('aria-hidden', true); 

        this.toastEl = document.createElement('div');


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



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', () => {

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



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


        }, 1000); 


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.toastContainerEl.setAttribute('aria-hidden', false); 

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

    this.toastEl.innerHTML = `
        <button type="button" class="toastjs-btn toastjs-btn--close">Close</button>

    this.focusedElBeforeOpen = document.activeElement;


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

Toast.prototype._init = function() {
    .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(() => {


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

blog comments powered by Disqus