Create lightbox with pure JavaScript

We are going to create a lightbox with pure JavaScript in this tutorial. A lightbox is a modal image gallery, it is used to display images in fullscreen in an overlay on the current page.

This plugin depends on annytab.effects (Pure javascript effects) that we have written about in a previous blog post and Font Awesome for icons.

We are going to create a lightbox that can be used to display one or multiple images in a modal window. Images in the same group can have a slideshow and the lightbox has pagination if there is more than one picture in a group. Images can also have a caption that shows at the bottom in the lightbox. The lightbox can be closed by clicking outside the box (optional) or by clicking on the x in the upper right corner.

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) and Internet Explorer (11.829.17134.0), without any polyfill. If you want to support older browsers, then you can get polyfills from polyfill.io.

Lightbox

Styling (CSS)

The lightbox is centered on the screen and the close-button is created in pure CSS. Font Awesome is used for icons.

/* Annytab lightbox*/
.annytab-lightbox-container {
    display: none;
    z-index: 100;
    position: fixed;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    text-align: center;
    background-color: #000000;
    background-color: rgba(0, 0, 0, 0.7);
}
.annytab-lightbox-margin {
    display: block;
    margin: 40px;
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
.annytab-lightbox-wrapper {
    display: inline-block;
    position: relative;
    max-width: 100%;
    background-color: #ffffff;
    border-radius: 8px;
    top: calc(50vh - 50px); /* adjust for margin */
    -ms-transform: translateY(-50%);
    transform: translateY(-50%);
}
.annytab-lightbox-padding {
    display: block;
    padding: 10px;
}
.annytab-lightbox-image {
    display: inline-block;
    max-width: 100%;
    max-height: calc(100vh - 100px); /* margin + padding */
    padding: 0;
    margin: 0; 
}
.annytab-lightbox-left-arrow {
    display: block;
    z-index: inherit;
    position: absolute;
    left: 20px;
    top: 50%;
    margin-top: -16px;
    cursor: pointer;
    width: 32px;
    height: 32px;
    border-radius: 4px;
    background-color: #000000;
    opacity: 0.5;
    filter: alpha(opacity=50);
    font-size: 30px;
    line-height: 32px;
    color: #ffffff;
}
.annytab-lightbox-right-arrow {
    display: block;
    z-index: inherit;
    position: absolute;
    right: 20px;
    top: 50%;
    margin-top: -16px;
    cursor: pointer;
    width: 32px;
    height: 32px;
    border-radius: 4px;
    background-color: #000000;
    opacity: 0.5;
    filter: alpha(opacity=50);
    font-size: 30px;
    line-height: 32px;
    color: #ffffff;
}
.annytab-lightbox-caption {
    display: none;
    position: absolute;
    bottom: 15px;
    left: 10px;
    right: 10px;
    max-height: 36px;
    overflow-y: auto;
    background-color: #000000;
    opacity: 0.6;
    filter: alpha(opacity=60);
    padding: 10px;
    color: #ffffff;
    text-align: left;
}
.annytab-lightbox-close {
    position: absolute;
    width: 24px;
    height: 24px;
    top: 8px;
    right: 8px;
    cursor: pointer;
    opacity: 0.6;
    filter: alpha(opacity=60);
}

.annytab-lightbox-close:hover {
    opacity: 1;
    filter: alpha(opacity=100);
}

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

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

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

JavaScript

This plugin have a constructor that takes options as a parameter. We add click events for all links with a given selector and this plugin have two public methods, open and close, that can be called from your scripts.

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

    'use_strict';

    // Constructor
    function lightbox(opts)
    {
        // Set default values for parameters
        opts = opts || {};

        // Set options
        this.options = { selector: '.annytab-lightbox-popup', fade_duration: 1000, close_click_outside: true, slideshow: true, slideshow_interval: 10000 };
        for (var option in this.options) {
            if (opts.hasOwnProperty(option) === true) {
                this.options[option] = opts[option];
            }
        }

        // Set variables
        resetVariables(this);

        // Get all links that should have a lightbox
        var links = document.querySelectorAll(this.options.selector);

        // Add events
        addLinkEvents(this, links);

    } // End of the constructor

    // Reset variables to default values
    function resetVariables(lb)
    {
        lb.current_slide = null;
        lb.last_slide = null;
        lb.container = null;
        lb.wrapper = null;
        lb.close_button = null;
        lb.automatic_slideshow = null;
        lb.left_arrow = null;
        lb.right_arrow = null;
        lb.caption_container = null;

    } // End of the resetVariables method

    // Add events
    function addLinkEvents(lb, links)
    {
        // Loop links
        for (var i = 0; i < links.length; i++)
        {
            // Add a click event
            window.onload = links[i].addEventListener('click', function (event) {

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

                // Open the lightbox
                lb.open(this);

            }, false);
        }

    } // End of the addLinkEvents method

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

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

            // Close the lightbox
            lb.close();

        }, false);

        // Add a close event
        if (lb.options.close_click_outside === true)
        {
            window.onload = lb.container.addEventListener('click', function (event) {

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

                // Close the lightbox
                if (event.target.contains(lb.wrapper) === true)
                {
                    lb.close();
                }

            }, false);
        }

        // Add paging if there is more than 1 slide
        if (lb.last_slide > 0)
        {
            // Show arrows
            lb.left_arrow.style.display = 'block';
            lb.right_arrow.style.display = 'block';

            // Add left arrow click event
            window.onload = lb.left_arrow.addEventListener('click', function (event) {

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

                // Show the previous slide
                lb.showSlide(-1);

                // Turn of the slideshow
                clearInterval(lb.automatic_slideshow);

            }, false);

            // Add right arrow click event
            window.onload = lb.right_arrow.addEventListener('click', function (event) {

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

                // Show the next slide
                lb.showSlide(1);

                // Turn of the slideshow
                clearInterval(lb.automatic_slideshow);

            }, false);

            // Create a slideshow
            if (lb.options.slideshow === true)
            {
                lb.automatic_slideshow = setInterval(function () { lb.showSlide(1); }, lb.options.slideshow_interval);
            }
        }
        else
        {
            // Hide arrows
            lb.left_arrow.style.display = 'none';
            lb.right_arrow.style.display = 'none';
        }

    } // End of the addContainerEvents method

    // Show a slide
    lightbox.prototype.showSlide = function (step)
    {
        // Set the current slide
        this.current_slide += step;

        // Make sure that the slide id not is outside borders
        if (this.current_slide > this.last_slide) {
            this.current_slide = parseInt(0);
        }
        if (this.current_slide < 0) {
            this.current_slide = parseInt(this.last_slide);
        }

        // Get slides
        var slides = this.container.querySelectorAll('.annytab-lightbox-image');
        var next_slide = this.container.querySelector('img[data-lightbox-id="' + this.current_slide + '"]');

        // Set a caption
        var caption = next_slide.getAttribute('data-lightbox-caption');
        if (caption !== null)
        {
            this.caption_container.innerHTML = caption;
            this.caption_container.style.display = 'block';
        }
        else
        {
            this.caption_container.style.display = 'none';
        }

        // Hide slides
        for (var i = 0; i < slides.length; i++)
        {
            slides[i].style.display = 'none';
        }

        // Fade in the next slide
        annytab.effects.fadeIn(next_slide, this.options.fade_duration, 'inline-block');

    }; // End of the showSlide method

    // Open a lightbox
    lightbox.prototype.open = function (link)
    {
        // Get the href attribute
        var href = link.getAttribute('href');

        // Get the group
        var group = link.getAttribute('data-lightbox-group');

        // Get the caption
        var caption = link.getAttribute('data-lightbox-caption');

        // Add the first image
        var source = '<img data-lightbox-id="0" src="' + href + '" class="annytab-lightbox-image" alt="image" style="display:none;"';
        source += caption !== null ? ' data-lightbox-caption="' + caption : '';
        source += '" />';

        // Create a counter
        var counter = 1;

        // Find all images in the group
        var images = document.querySelectorAll('[data-lightbox-group="' + group + '"]');

        // Loop images
        for (var i = 0; i < images.length; i++)
        {
            var url = images[i].getAttribute('href');
            if (url !== href)
            {
                source += '<img data-lightbox-id="' + counter + '" src="' + url + '" class="annytab-lightbox-image" alt="image" style="display:none;"';
                source += images[i].getAttribute('data-lightbox-caption') !== null ? ' data-lightbox-caption="' + images[i].getAttribute('data-lightbox-caption') : '';
                source += '" />';
                counter += 1;
            }
        }
        
        // Get the last slide and set the current slide
        this.last_slide = counter - 1;
        this.current_slide = parseInt(-1);

        // Create a lightbox
        this.container = document.createElement('div');
        this.container.setAttribute('class', 'annytab-lightbox-container');
        this.container.insertAdjacentHTML('beforeend', '<div class="annytab-lightbox-margin">'
            + '<div class="annytab-lightbox-wrapper">'
            + '<div class="annytab-lightbox-padding">'
            + source
            + '<div class="annytab-lightbox-left-arrow"><i class="fas fa-angle-left"></i></div>'
            + '<div class="annytab-lightbox-right-arrow"><i class="fas fa-angle-right"></i></div>'
            + '<div class="annytab-lightbox-caption"></div>'
            + '</div></div></div>'
            + '<div class="annytab-lightbox-close"></div >');
        document.body.appendChild(this.container);

        // Get references
        this.wrapper = this.container.querySelector('.annytab-lightbox-wrapper');
        this.close_button = this.container.querySelector('.annytab-lightbox-close');
        this.caption_container = this.container.querySelector('.annytab-lightbox-caption');
        this.left_arrow = this.container.querySelector('.annytab-lightbox-left-arrow');
        this.right_arrow = this.container.querySelector('.annytab-lightbox-right-arrow');

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

        // Add container events
        addContainerEvents(this);

        // Show the next slide
        this.showSlide(1);

    }; // End of the open method

    // Close a lightbox
    lightbox.prototype.close = function () {

        // Turn of the slideshow
        clearInterval(this.automatic_slideshow);

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

        // Remove the container
        var box = this.container;
        setTimeout(function ()
        {
            document.body.removeChild(box);

        }, this.options.fade_duration);

        // Reset variables (GC)
        resetVariables(this);

    }; // End of the close method

    // Return this object
    return lightbox;

})();

How to use this plugin

Add a link around images that should have a lightbox, set a data-lightbox-group attribute (optional) for images that should be included in a group and set a data-lightbox-caption attribute (optional) if you want a caption displayed on top of the image. We have used a class as the selector, it can be a class, a tag or a name.

<a href="/images/background_large.jpg" class="annytab-lightbox-popup" data-lightbox-group="gp1" data-lightbox-caption="Hello, I am the one how have taken this photo.">
    <img src="/images/background_small.jpg" alt="Background image" style="max-width:100%;border:1px solid #cccccc;" />
</a>
<a href="/images/logotype_large.jpg" class="annytab-lightbox-popup" data-lightbox-group="gp1">
    <img src="/images/logotype_small.jpg" alt="Background image" style="max-width:100%;border:1px solid #cccccc;" />
</a>

@*Open and close from JavaScript*@
<div id="testLink" href="/images/background_large.jpg"></div>
<div onclick="openLink()">Open link</div>
<div style="position:absolute;z-index:500;font-size:36px;margin-top:50px;" onclick="closeBox()">Close box</div>

Create a new instance of this plugin inside a script-tag and add options as a parameter. Lightboxes is added for all links with the choosen selector, you can manually call the open method and close method from script if you want.

<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.lightbox.js"></script>
<script>

    // Lightbox
    var lightbox = new annytab.lightbox({ selector: '.annytab-lightbox-popup', fade_duration: 1000, close_click_outside: true, slideshow: true, slideshow_interval: 10000 });

    // Open lightbox
    function openLink() { lightbox.open(document.getElementById('testLink')); }

    // Close lightbox
    function closeBox() { console.log('Closing'); lightbox.close(); }

</script>

Leave a Reply

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