Tutorial
Creating a lightbox with Swiper JS
| Updated 03.05.22
There are probably a million different tutorials out there to teach you how to make a lightbox to show off images, but here is the way I ended up creating the module for this site. I use Swiper JS throughout my website to handle all of the carousels, and SCSS for my styling. Swiper JS is an amazing library that I encourage anyone to have a play with, it’s very user friendly and easy to get started with.
Below is an example of the module that we will be building.
Example Lightbox
Let’s get started
For the HTML I just have a div with an attribute of lightbox-toggle
and then an image inside. You can go ahead and create as many as you need.
<div class="cb-image-grid__image" lightbox-toggle>
<img src="http://via.placeholder.com/350x350/7176A7/fff?text=image+two" />
</div>
The JS
Firstly we need to grab some DOM elements that will be used later, I store the body
tag and all of the images that are inside a container with the attribute lightbox-toggle
as variables. In addition, we create a globals
object where we can store some variables later on, as well as an initial swiper variable.
The initLightbox
variable just checks to see if the lightboxImages
variable found any images, then returns a boolean value. We will use the initLightbox
variable to conditionally run our code.
let swiper;
window.globals = {};
const body = document.getElementsByTagName('body')[0];
const lightboxImages = document.querySelectorAll("[lightbox-toggle] img");
const initLightbox = (lightboxImages.length > 0);
Next up we create the “skeleton” for the lightbox carousel which Swiper will then bring to life once it initialises. Essentially we are just creating the divs needed for Swiper to work, as well as the “close” icon. We then append each of them into each of the correct places within the DOM.
At the end of the function, we create some global variables to reference the lightbox and the swiper wrapper within.
const createLightboxSkeleton = () => {
// Create skeleton for lightbox
const lightbox = document.createElement('div');
const lightboxContainer = document.createElement('div');
const lightboxClose = document.createElement('div');
const swiperContainer = document.createElement('div');
const swiperWrapper = document.createElement('div');
const swiperBtnNext = document.createElement('div');
const swiperBtnPrev = document.createElement('div');
const swiperPagination = document.createElement('div');
// Add classes
lightbox.classList.add('c-lightbox');
lightboxContainer.classList.add('c-lightbox__container');
lightboxClose.classList.add('c-lightbox__close');
lightboxClose.setAttribute('tabindex', '0');
lightboxClose.innerHTML = 'X';
swiperContainer.classList.add('swiper-container');
swiperWrapper.classList.add('swiper-wrapper');
swiperBtnNext.classList.add('swiper-button-next');
swiperBtnPrev.classList.add('swiper-button-prev');
swiperPagination.classList.add('swiper-pagination');
// Append created divs
lightboxContainer.appendChild(lightboxClose);
swiperContainer.appendChild(swiperWrapper);
swiperContainer.appendChild(swiperBtnNext);
swiperContainer.appendChild(swiperBtnPrev);
swiperContainer.appendChild(swiperPagination);
lightboxContainer.appendChild(swiperContainer);
lightbox.appendChild(lightboxContainer);
body.appendChild(lightbox);
// Set variables to reference the lightbox
globals.lightboxRef = document.querySelector('.c-lightbox');
globals.swiperWrapperRef = document.querySelector(
'.c-lightbox .swiper-wrapper',
);
};
Next up we use the initLightbox
variable to check we have enough images to run, then immediately call the createLightboxSkeleton
function.
From now on the code we write will be added within the initLightbox
if statement.
if (initLightbox) {
createLightboxSkeleton();
// The rest of the code will go here
}
First, you can see we should clear out any HTML from the swiper wrapper that may be leftover from any previous clicks on images, otherwise, it will just create additional images repeatedly.
Then we use the lightboxImages
variable we created initially, we are going to use it to loop through each image and apply a click event listener to each image so it can be clicked to open the lightbox.
If you look inside the imageClick
function you can see that we are first creating a “clone” of each image from the page to then be used within the lightbox carousel. We do this by looping through the images again and using javascript to create the dom elements we need, similarly to how we have done previously.
Next, we initialise the swiper using some configuration settings, I won’t go into that much detail about using Swiper, you can find out more by going through the docs here. The most important part here though is that we set the initialSlide
value to be the index from the loop so that whichever image we click on is the first image shown within the lightbox carousel.
Finally, still inside the imageClick
function, we add a class to open the lightbox carousel to the page and add some style to the body to prevent the user from vertically scrolling.
lightboxImages.forEach((el, index) => {
// Add click function to lightbox images
el.addEventListener('click', imageClick, false);
function imageClick() {
// Clear swiper before trying to add to it
globals.swiperWrapperRef.innerHTML = '';
// Loop through images with lightbox data attr
// Create html for lightbox carousel
lightboxImages.forEach((img) => {
// Create clone of current image in loop
const image = img.cloneNode(true);
// Create divs
const slide = document.createElement('div');
const imageContainer = document.createElement('div');
// Add classes
slide.classList.add('swiper-slide');
imageContainer.classList.add('c-lightbox__image');
// Append images to the slides, then slides to swiper wrapper
imageContainer.appendChild(image);
slide.appendChild(imageContainer);
globals.swiperWrapperRef.appendChild(slide);
});
// Init Swiper
swiper = new Swiper('.c-lightbox .swiper-container', {
initialSlide: index,
loop: true,
slidesPerView: 1,
speed: 750,
spaceBetween: 16,
watchOverflow: true,
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
pagination: {
el: '.swiper-pagination',
type: 'fraction',
},
zoom: true,
fadeEffect: {
crossFade: false,
},
keyboard: {
enabled: true,
onlyInViewport: true,
},
mousewheel: {
sensitivity: 1,
forceToAxis: true,
invert: true,
},
});
// Add the class to open the lightbox
// Add overflow hidden to the body to prevent scrolling
globals.lightboxRef.classList.add('open');
body.classList.add('overflowHidden');
}
});
Next up, still remaining within the initLightbox
if statement, we create some more event listeners to allow the user to close out the lightbox by either clicking or using the “esc” key.
// Close lightbox on click
document.addEventListener('click', ({ target }) => {
if (target.matches('.c-lightbox__close')) {
destroySwiper(swiper, 250);
globals.lightboxRef.classList.remove('open');
body.classList.remove('overflowHidden');
}
}, false);
// Close lightbox on escape key press
document.addEventListener('keydown', ({ key }) => {
if (key === 'Escape') {
destroySwiper(swiper, 250);
globals.lightboxRef.classList.remove('open');
body.classList.remove('overflowHidden');
}
});
Finally, you might have noticed there is a function called destroySwiper
within the last two event listeners. This does what it says and kills the lightbox swiper when it is no longer being used with a slight timeout so it gives it enough time to fade out. This function can be added at the top of the JS file under the variables we set initially.
const destroySwiper = (swiper, timeout) => {
setTimeout(() => {
swiper.destroy();
}, timeout);
};
The CSS
I won’t go into detail about the CSS I’ve used here, it should be pretty straightforward. Simply copy and paste into an SCSS file and you’re good to go.
[lightbox-toggle] {
cursor: zoom-in;
&:after {
position: absolute;
content: url('data:image/svg+xml; utf8, ');
height: 32px;
width: 32px;
bottom: 0;
right: 0;
opacity: 0;
will-change: opacity;
transition: opacity 0.2s;
}
&:hover {
&:after {
opacity: 1;
filter: drop-shadow(2px 4px 6px black);
}
}
}
.c-lightbox {
$c: &;
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
pointer-events: none;
position: fixed;
opacity: 0;
width: 100vw;
height: 100vh;
z-index: 9000000;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.85);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: opacity 0.3s;
&.open {
opacity: 1;
pointer-events: all;
}
#{$c}__container {
width: 100%;
height: 100%;
}
#{$c}__close {
z-index: 999999;
position: absolute;
cursor: pointer;
top: 1vh;
right: 1vw;
font-size: 30px;
padding: 20px;
color: white;
&:hover {
color: red;
}
}
.swiper-container {
width: 100%;
height: 100%;
}
.swiper-slide {
display: flex;
justify-content: center;
align-items: center;
}
.swiper-pagination {
color: white;
}
.swiper-button-next,
.swiper-button-prev {
color: white;
&:hover {
color: red;
}
&:after {
font-size: 30px;
}
}
#{$c}__image {
display: flex;
justify-content: center;
align-items: center;
position: relative;
height: 75%;
width: 75%;
text-align: center;
cursor: zoom-in;
img {
width: auto;
height: auto;
max-height: 90vh;
max-width: 90vw;
}
}
}
There you have it, a quick tutorial on how to create a simple modern lightbox using Swiper JS to handle the carousel functionality. Working on personal projects like this and creating tutorials has been an interesting experience for me. I’m so used to just working to tight deadlines that generally don’t leave time for you as a developer to really craft and labour over each part of your code. Just by writing this very tutorial I have updated and changed parts of the code to improve it greatly. It’s like doing a code review for yourself.
To see a working version with all of the code in one place, check out this Codepen here.
To create multiple lightboxes on one page that are independent of each other, please check out my new tutorial.
Awesome tutorial! Just what I was looking for!
Hi Dalton!
Awesome tutorial!! Indeed just what I was looking for. Love the clean code also.
I’m trying to make it work with the default wordpress gallery. Using swiper as my default slider for my wordpress and love the library. But I can’t get the lightbox to work. I wonder if you could take a peak and see what i’m doing wrong.
I’ve changed this const to query the wordpress gallery images:
const lightboxImages = document.querySelectorAll(“.wp-block-image img”);
The gallery page: https://staging-joostweddepohlgc.kinsta.cloud/client-galler/
Thanks and greets from Holland
Hi, thanks for the nice comment!
It looks like you’ve managed to get it working on your link, is that right?
Hi Dalton,
Yes got it working. Had to make the scss work with wordpress. Awesome work!!
Ah thats great news, glad you got it working!
easier to check out, i’ve tried to recreate it a codepen. but not working:
https://codepen.io/weddje/pen/oNGgRBR
Thanks for this. I was using Glightbox before and when I was starting to run into some issues, and I was thinking – Swiper already has all the necessary modules and features to do the same things as lightbox plugins do, and it is modern, well maintained. It just needs initialization and put over the page full width/height. Stumbled upon your article. I need more additions to do though, but this is great starting point and will allow for a lot more flexibility.
No worries, glad the tutorial could be of help to you!
I love Swiper JS so being able to make my own lightbox with it was really fun.
Let me know if you have any questions!
Hey Dadlton!
Very nice Tutorial! Perfectly explained and easy to follow.
I was just wondering: What would be necessary to make multiple separate Lightboxes on the same Page work with this code?
Like having a List of Items which each has some thumbnails which can be clicked to open an own Lightbox.
Hey I just created a tutorial that allows you to create multiple lightboxes on one page that are independent of each other – https://daltonwalsh.com/blog/multi-lightbox-with-swiper-js/
I hope this will help you.
Hi Dalton. Thanks so much for your gift to us 🙂
I’m also wondering how to adapt your code to allow multiple lightboxes on the same page.
Hi Hank 👋
Because of the interest, I’ll be creating a new post that will detail how to make the lightbox work with multiple on the page.
It’s a great suggestion, thank you.
Hi Hank, I created a tutorial that allows you to create multiple lightboxes on one page that are independent of each other – https://daltonwalsh.com/blog/multi-lightbox-with-swiper-js/
I hope this will help you.
Hi Dalton,
How we show the captions and postcategory title?
For the captions i’ve tried to pull them in like this:
const imageCaption = lightboxImages.next(‘figcaption’).text();
Hi Joost, I would need to see more of your code to get an idea of exactly what you are trying to do.
Are you trying to achieve something like this – https://northernbytesflyball.com/gallery/ Where each image has a caption that it brings to the lightbox on click?
Hey Dalton!
yes, that’s what I’m trying to do. But only showing the title in the lightbox, not on the thumbnails.
This is the code I have:
Cool,
So here is the code I went with for that example.
You would just need to adapt it to how your setup is by swapping my “caption” class for “figcaption” and hopefully that should work for you.
Cheers