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, check out our post on transpilation and polyfilling of JavaScript.
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>