Simple JavaScript Lightbox Gallery

Simple JavaScript Lightbox Gallery
Project: Simple lightbox
Author: Lora Lam
Edit Online: View on CodePen
License: MIT

An image lightbox is one of the popular components of a visual interface. It loads full-size images dynamically and displays them in a popup box. A basic interface of a lightbox comes with navigational arrows to slide the next/previous images and a cross button to close the lightbox. This tutorial explains how to create a simple lightbox gallery using HTML, CSS, and JavaScript.

How to Create a Simple JavaScript Lightbox Gallery

1. First of all, load the Normalize CSS and Google Fonts by adding the following CDN links into the head tag of your HTML document.

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:400,700'>

2. After that, create a div element with a class name "content",  and place your images wrapped with an anchor tag with the "data-src-full" attribute. Place the link of the full-sized image inside the data-src-full that will be loaded in the lightbox.

Similarly, create a div element with a class name "lightbox", define its tabindex attribute with -1 value, and define an aria-hidden attribute with true value.  Inside the lightbox, create two div elements and two buttons with the following mentioned class names.

<div class="content">
	<a href="#" data-src-full="path/to/full-size-image.jpg">
         <img src="path/to/thumbnail.jpg">
        </a>
        <a href="#" data-src-full="path/to/full-size-image.jpg">
        <img src="path/to/thumbnail.jpg">
        </a>
</div>
<div class="lightbox" tabindex="-1" role="dialog" aria-hidden="true">
	<div class="lightbox-loader"></div>
	<div class="lightbox-content"></div>
	<button class="lightbox-previous">&#8249;</button>
	<button class="lightbox-next">&#8250;</button>
</div>

3. Style the lightbox gallery using the following CSS styles. You can modify the CSS values in order to customize the lightbox.

@charset "UTF-8";
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  font-size: 62.5%;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

body {
  font-family: "Roboto", sans-serif;
  font-size: 1.6rem;
  line-height: 1.5em;
}

a {
  color: #647df9;
}

img {
  height: auto;
  max-width: 100%;
  vertical-align: middle;
}

.content {
  max-width: 480px;
  margin: 2em auto;
}
@media all and (min-width: 840px) {
  .content {
    max-width: 720px;
  }
}
.content img {
  margin: 0.5em;
}

.lightbox {
  width: 100%;
  height: 100%;
  padding-right: 10px;
  padding-left: 10px;
  background: rgba(250, 250, 250, 0.45);
  position: fixed;
  top: 0;
  left: 0;
  z-index: -1;
  opacity: 0;
  visibility: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  transition: z-index 0s 0.3s, opacity 0.3s, visibility 0s 0.3s;
}
@media all and (min-width: 768px) {
  .lightbox {
    padding-right: 35px;
    padding-left: 35px;
  }
}
.js-lightbox-loading .lightbox, .js-lightbox-active .lightbox {
  z-index: 10;
  opacity: 1;
  visibility: visible;
  transition: z-index 0s 0s, opacity 0.3s, visibility 0s 0s;
}
.lightbox:before {
  width: 18px;
  font-size: 3.2rem;
  line-height: 18px;
  color: #000;
  position: absolute;
  top: 8px;
  right: 9px;
  cursor: pointer;
  content: "×";
}
.lightbox .lightbox-content {
  min-width: 80px;
  min-height: 80px;
  background: #fff;
  opacity: 0;
  transform: translateY(-80px);
  transition: opacity 0s, transform 0s ease-out;
}
.js-lightbox-active .lightbox .lightbox-content {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.3s, transform 0.3s ease-out;
}
.js-lightbox-first .lightbox .lightbox-content {
  transform: translateX(40px);
  transition: transform 0.15s linear;
}
.js-lightbox-last .lightbox .lightbox-content {
  transform: translateX(-40px);
  transition: transform 0.15s linear;
}
.lightbox .lightbox-content img {
  min-width: 60px;
  min-height: 60px;
  max-height: calc(100vh - 90px);
  -webkit-transition: box-shadow 0.6s ease-out;
  box-shadow: 1px 1px 4px #ccc;
}
.lightbox .lightbox-content img:hover {
  box-shadow: 1px 8px 20px #999;
  -webkit-transition: box-shadow 0.6s ease-in;
}
.lightbox .lightbox-previous,
.lightbox .lightbox-next {
  width: 50px;
  height: 50px;
  font-size: 5rem;
  line-height: 0.75;
  color: #000;
  padding: 0 0 0.25em;
  background: rgba(255, 255, 255, 0);
  border: none;
  margin-top: -24px;
  position: absolute;
  top: 50%;
  outline: none;
}
.js-lightbox-loading .lightbox .lightbox-previous,
.js-lightbox-loading .lightbox .lightbox-next {
  display: none;
}
.lightbox .lightbox-previous {
  left: 0;
}
.lightbox .lightbox-next {
  right: 0;
}
.lightbox .lightbox-loader {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgba(200, 200, 200, 0.3);
  border-top: 1px solid rgba(0, 0, 0, 0.85);
  margin-top: -20px;
  margin-left: -20px;
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  -webkit-animation: spinner 1s linear infinite;
          animation: spinner 1s linear infinite;
}
.js-lightbox-loading .lightbox .lightbox-loader {
  display: block;
}

@-webkit-keyframes spinner {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

@keyframes spinner {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

4. Now, load the ImagesLoaded JS library that helps to load images dynamically inside the lightbox.

<script src='https://imagesloaded.desandro.com/imagesloaded.pkgd.js'></script>

5. Finally, add the following JavaScript function between the <script> and </script> tags before closing the body tag to initialize the lightbox.

console.clear();

var body = document.body,
	lightbox = document.querySelector('.lightbox'),
	lightboxContent = lightbox.querySelector('.lightbox-content'),
	lightboxImages = [],
	lightboxPrevious = lightbox.querySelector('.lightbox-previous'),
	lightboxNext = lightbox.querySelector('.lightbox-next'),

	photoLink = document.querySelectorAll('.content a');

function getTransitionPrefix() {
	var element = document.createElement('div'),
		transitions = {
			'transition': 'transitionend',
			'webkitTransition': 'webkitTransitionEnd',
			'msTransition': 'msTransitionEnd',
			'mozTransition': 'MoztransitionEnd'
		};

	for (var transition in transitions) {
		if (element.style[transition] !== undefined) {
			return transitions[transition];
		}
	}
}

function showLightbox(source) {
	body.classList.add('js-lightbox-loading');
	body.classList.remove('js-lightbox-active');
	body.classList.remove('js-lightbox-first');
	body.classList.remove('js-lightbox-last');
	
	lightboxContent.innerHTML = '<img src="' + source + '" />';
	imagesLoaded(lightboxContent, function () {
		body.classList.remove('js-lightbox-loading');
		body.classList.add('js-lightbox-active');
	});
}
function previousLightbox() {
	var currentIndex = lightboxImages.indexOf(lightboxContent.querySelector('img').src);
	
	currentIndex--;
	
	if (currentIndex >= 0) {
		showLightbox(lightboxImages[currentIndex]);
	} else {
		body.classList.add('js-lightbox-first');
		lightboxContent.addEventListener(getTransitionPrefix(), function() {
			body.classList.remove('js-lightbox-first');
		});
	}
}
function nextLightbox() {
	var currentIndex = lightboxImages.indexOf(lightboxContent.querySelector('img').src);
	
	currentIndex++;
	
	if (currentIndex < lightboxImages.length) {
		showLightbox(lightboxImages[currentIndex]);
	} else {
		body.classList.add('js-lightbox-last');
		lightboxContent.addEventListener(getTransitionPrefix(), function() {
			body.classList.remove('js-lightbox-last');
		});
	}
}
function hideLightbox() {
	body.classList.remove('js-lightbox-loading');
	body.classList.remove('js-lightbox-active');
	lightboxContent.innerHTML = '';
}

lightbox.addEventListener('click', function (event) {
	if ((event.target === this || event.target === this.querySelector('.loader') || event.target === this.querySelector('.close')) && (body.classList.contains('js-lightbox-loading') || body.classList.contains('js-lightbox-active'))) {
		hideLightbox();
	}
});
document.addEventListener('keyup', function (event) {
	if ((event.keyCode === 27 || event.keyCode === 33 || event.keyCode === 34) && (body.classList.contains('js-lightbox-loading') || body.classList.contains('js-lightbox-active'))) {
		hideLightbox();
	}
});
document.addEventListener('mousewheel', function (event) {
	if (body.classList.contains('js-lightbox-loading') || body.classList.contains('js-lightbox-active')) {
		hideLightbox();
		// REMEBER TO CHECK IF IMAGE IS SCROLLED TO TOP OR BOTTOM + SOME EXTRA
	}
});


var startX = 0,
	startY = 0,
	endX = 0,
	endY = 0;

document.addEventListener('touchstart', function (event) {
	if (event.target === lightboxContent || event.target === lightboxContent.querySelector('img')) {
		startX = event.pageX || event.touches[0].pageX;
		startY = event.pageY || event.touches[0].pageY;
	}
});
body.addEventListener('touchmove', function (event) {
	if (event.target === lightboxContent || event.target === lightboxContent.querySelector('img')) {
		endX = event.pageX || event.touches[0].pageX;
		endY = event.pageY || event.touches[0].pageY;
	}
});
document.addEventListener('touchend', function (event) {
	if (event.target === lightboxContent || event.target === lightboxContent.querySelector('img')) {
		var distanceX = startX - endX;
		var distanceY = startY - endY;
		
		// event.changedTouches[0].pageX
		
		if (Math.abs(distanceX) > Math.abs(distanceY)) {
			if (distanceX > 0) {
				nextLightbox();
			} else {
				previousLightbox();
			}
		} else {
			hideLightbox();
		}
	}
	
	console.log(event);
});

lightboxPrevious.addEventListener('click', function (event) {
	previousLightbox();
});
document.addEventListener('keyup', function (event) {
	if (event.keyCode === 37 && (body.classList.contains('js-lightbox-loading') || body.classList.contains('js-lightbox-active'))) {
		previousLightbox();
	}
});
lightboxNext.addEventListener('click', function (event) {
	nextLightbox();
});
document.addEventListener('keyup', function (event) {
	if (event.keyCode === 39 && (body.classList.contains('js-lightbox-loading') || body.classList.contains('js-lightbox-active'))) {
		nextLightbox();
	}
});

for (var pl = 0, pll = photoLink.length; pl < pll; pl++) {
	lightboxImages.push(photoLink[pl].getAttribute('data-src-full'));
	
	photoLink[pl].addEventListener('click', function (event) {
		event.preventDefault();
		showLightbox(this.getAttribute('data-src-full'));
	});
}

That’s all! hopefully, you have successfully created a simple JavaScript lightbox gallery. If you have any questions or suggestions, feel free to comment below.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply