Create toast notifications with pure JavaScript

We are going to create a toast notifications plugin with pure JavaScript in this tutorial. A toast notification is a small message that is shown over other contents.

This notification plugin is able to show different messages. Notifications can be shown during a limited time, this is visualized by a progress bar. A notification that gets focus will restart it’s self-destruction process. A notification can be closed and all notifications can be removed by calling the clear method.

This plugin depends on annytab.effects and Font Awesome, these dependencies can easily be replaced by you if you want to use other icons and/or another library for effects.

This plugin has been tested and is working with Google Chrome (75.0.3770.100), Mozilla Firefox (67.0.4), Microsoft Edge (42.17134.1.0), without any polyfill. It works in Internet Explorer (11.829.17134.0) with a polyfill for CustomEvent. If you want to support older browsers, then you can get polyfills from polyfill.io.

Toast notifications

JavaScript

This plugin does not have a constructor, it is static and can be used by calling the public methods: setOptions, show and clear. Notifications is created in a container and the container is removed when the last message is removed.

var annytab = annytab || {};
annytab.notifier = (function () {

    'use_strict';

    // Variables
    var options = { duration: 10000, position: 'top-center', fade_duration: 1000 };
    var container = null;
    
    // Update options
    function updateOptions(opts)
    {
        // Set default values for parameters
        opts = opts || {};

        // Update options
        for (var option in options)
        {
            if (opts.hasOwnProperty(option) === true)
            {
                options[option] = opts[option];
            }
        }

        // Return options
        return options;

    } // End of the updateOptions method

    // Create a box
    function create(type, message)
    {
        // Make sure that a container exists
        if (container === null)
        {
            container = document.createElement('div');
            container.setAttribute('class', 'annytab-notifier-container annytab-notifier-' + options.position);
            document.body.appendChild(container);
        }

        // Set icon (Font Awesome)
        var icon = '<i class="fas fa-exclamation fa-fw"></i>';
        if (type === 'success') { icon = '<i class="fas fa-check-circle fa-fw"></i>'; }
        else if (type === 'warning') { icon = '<i class="fas fa-exclamation-triangle fa-fw"></i>'; }
        else if (type === 'info') { icon = '<i class="fas fa-info-circle fa-fw"></i>'; }

        // Create a box
        var box = document.createElement('div');
        box.setAttribute('class', 'annytab-notifier-box annytab-notifier-' + type);
        box.insertAdjacentHTML('beforeend',
            '<div class="annytab-notifier-padding">'
            + '<div class="annytab-notifier-icon">' + icon + '</div>'
            + '<div class="annytab-notifier-message">'
            + message
            + '</div>'
            + '<div class="annytab-notifier-progress"></div>'
            + '<div class="annytab-notifier-close"></div >');
            + '</div>';
        container.appendChild(box);

        // Fade in the message box
        annytab.effects.fadeIn(box, options.fade_duration, 'block');

        // Add events
        addBoxEvents(box);

        // Return the box
        return box;

    } // End of the create method

    // Add box events
    function addBoxEvents(box)
    {
        // Variables
        var close_button = box.querySelector('.annytab-notifier-close');
        var progress = box.querySelector('.annytab-notifier-progress');
        var progress_interval = null;
        var close_timeout = null;

        // Add a close event
        window.onload = close_button.addEventListener('click', function (event) {

            // Prevent default click behaviour
            event.preventDefault();

            // Close the box
            close(box);

        }, false);

        // Check if the box should self destruct
        if (options.duration > 0)
        {
            // Add self destruction event
            window.onload = box.addEventListener('mouseleave', function (event) {

                // Prevent default click behaviour
                event.preventDefault();

                // Check if the box is closing
                if (annytab.effects.isVisible(close_button) === false) {
                    return;
                }

                // Display the progress bar
                progress.style.display = 'block';

                // Calculate the amount to decrease each interval
                var width = 100;
                var decrease = width / options.duration * 10;

                // Set an interval to show progress
                progress_interval = window.setInterval(function () {
                    width -= decrease;
                    progress.style.width = width + '%';

                }, 10);

                // Self destruct after some time
                close_timeout = window.setTimeout(function () {

                    // Clear timeout and interval
                    clearTimeout(close_timeout);
                    clearInterval(progress_interval);

                    // Close the box
                    close(box);

                }, options.duration);

            }, false);

            // On focus
            window.onload = box.addEventListener('mouseenter', function (event) {

                // Prevent default click behaviour
                event.preventDefault();

                // Check if the box is closing
                if (annytab.effects.isVisible(close_button) === false)
                {
                    return;
                }

                // Clear timeout and interval
                clearTimeout(close_timeout);
                clearInterval(progress_interval);

                // Reset progress bar
                progress.style.display = 'none';
                progress.style.width = '100%';

            }, false);

            // Trigger a mouse leave event
            box.dispatchEvent(new Event('mouseleave'));
        }

    } // End of the addBoxEvents method

    // Close a box
    function close(box)
    {
        // Get the close button
        var close_button = box.querySelector('.annytab-notifier-close');

        // Check if the box is closing
        if (annytab.effects.isVisible(close_button) === false) {
            return;
        }

        // Hide the close button
        close_button.style.display = 'none';

        // Fade out the box
        annytab.effects.fadeOut(box, options.fade_duration);

        // Wait for the fade effect to finish
        setTimeout(function ()
        {
            // Remove the box
            container.removeChild(box);

            // Check if we should remove the container
            if (container.querySelector('.annytab-notifier-box') === null) {
                document.body.removeChild(container);
                container = null;
            }
            
        }, options.fade_duration);

    } // End of the close method

    // Remove a container
    function removeContainer()
    {
        // Make sure that the container exists
        if (container !== null)
        {
            // Get all boxes in the container
            var boxes = container.querySelectorAll('.annytab-notifier-box');

            // Loop boxes
            for (var i = 0; i < boxes.length; i++) {

                // Close the box
                close(boxes[i]);
            }
        }

    } // End of the removeContainer method

    // Public methods
    return {

        setOptions: function (opts) {

            return updateOptions(opts);
        },
        show: function (type, message)
        {
            return create(type, message);
        },
        clear: function ()
        {
            removeContainer();
        }
    };

})();

Styling (CSS)

The container for notifications can be set at various positions on the screen, the center-center position might look a little blurry. The close button is created entirely in CSS and icons comes from Font Awesome.

/* Containers */
.annytab-notifier-container {
    display: block;
    z-index: 200;
    position: fixed;
    max-width: calc(100% - 10px); /* adjust for margin */
    max-height: 100%;
    overflow: auto;
}

/* General styles */
.annytab-notifier-box {
    display: none;
    position: relative;
    margin: 5px;
    width: 500px;
    max-width: calc(100% - 10px); /* adjust for margin */
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

.annytab-notifier-padding {
    display: block;
    padding: 15px 15px 15px 65px;
    text-align: left;
}

.annytab-notifier-icon {
    display: inline-block;
    position: absolute;
    font-size: 28px; /* 32px is actual height */
    top: calc(50% - 16px);
    left: 15px;
    margin: auto;
}

.annytab-notifier-message {
    font-size: 16px;
    line-height: 24px;
}

.annytab-notifier-progress {
    display: none;
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 4px;
    background-color: #d9d9d9;
}

/* Box positions */
.annytab-notifier-top-left {
    top: 5px;
    left: 5px;
}

.annytab-notifier-top-center {
    top: 5px;
    left: 50%;
    -ms-transform: translateX(-50%);
    transform: translateX(-50%);
}

.annytab-notifier-top-right {
    top: 5px;
    right: 5px;
}

.annytab-notifier-center-center {
    top: 50%;
    left: 50%;
    -ms-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
}

.annytab-notifier-bottom-left {
    bottom: 5px;
    left: 5px;
}

.annytab-notifier-bottom-center {
    bottom: 5px;
    left: 50%;
    -ms-transform: translateX(-50%);
    transform: translateX(-50%);
}

.annytab-notifier-bottom-right {
    bottom: 5px;
    right: 5px;
}

/* Message types */
.annytab-notifier-success {
    background-color: #ffffff;
    border: 4px solid #51A351;
    color: #51A351;
}

.annytab-notifier-error {
    background-color: #ffffff;
    border: 4px solid #BD362F;
    color: #BD362F;
}

.annytab-notifier-info {
    background-color: #ffffff;
    border: 4px solid #2F96B4;
    color: #2F96B4;
}

.annytab-notifier-warning {
    background-color: #ffffff;
    border: 4px solid #CC7606;
    color: #CC7606;
}

/* Close button */
.annytab-notifier-close 
{
    position: absolute;
    width: 12px;
    height: 12px;
    top: 2px;
    right: 2px;
    cursor: pointer;
}

.annytab-notifier-close:before, .annytab-notifier-close:after {
    position: absolute;
    right: 5px;
    content: ' ';
    height: 12px;
    width: 2px;
    background-color: #000000;
}

.annytab-notifier-close:before {
    transform: rotate(45deg);
}

.annytab-notifier-close:after {
    transform: rotate(-45deg);
}

How to use this plugin

You can set options before you start to call methods in the plugin, default values will be used if you don’t set any options. Set the duration to a value less than 1 if you do not want notifications to be shown during a limited time, notifications have to be closed or cleared in that case.

<!--<script src="https://polyfill.io/v3/polyfill.min.js?features=CustomEvent"></script>-->
<link href="/css/annytab.notifier.css" rel="stylesheet" />
<script src="/js/font-awesome/v5.3.1/all.min.js"></script>
<script src="/js/annytab-shared/annytab.effects.js"></script>
<script src="/js/annytab-shared/annytab.notifier.js"></script>
<script>

    // Set options
    // Positions: top-left, top-center, top-right, center-center, bottom-left, bottom-center, bottom-right
    annytab.notifier.setOptions({ duration: 10000, position: 'top-right', fade_duration: 1000 });

    // Show success
    annytab.notifier.show('success', 'Success, everything was perfect!');

    // Show error
    annytab.notifier.show('error', 'Something is wrong, it can be that or that.');

    // Show information
    annytab.notifier.show('info', 'I just want to inform you that it is going to rain today.');

    // Show warning
    annytab.notifier.show('warning', 'Warning for Internet Explorer, the worst browser that exists.');

</script>

Leave a Reply

Your email address will not be published. Required fields are marked *