15 1 0 4000 1 https://codeblock.co.za 300 true 0
Create a Lightbox Photo Gallery With JQuery

Create a Lightbox Photo Gallery With JQuery

2 Comments

The photos we share on our websites are usually the best ones, the ones we want the world to see. So we want them to displayed in a tidy, attractive manner. Lightboxes are a great way to do this and best of all, super simple to implement with some basic HTML and the JQuery library.

Prerequisites

  • An understanding of JavaScript and the JQuery JavaScript Library
  • PHP, but only to follow this tutorial
  • For Loops

For the example, I’m using a PHP array I created manually, but you may be getting your data from another source like an API or your own database. Either way, the data will still come in as an array so the same concepts will apply. Looking at the code below, each array record has a photo array that has a URL to the full size image, a thumbnail URL and a caption.

The full code for this tutorial is available on Github.

<?php

function myPhotos() {
    $photos = [
        [
            'url' => 'http://lorempixel.com/1280/560/transport/1',
            'thumbnail' => 'http://lorempixel.com/500/500/transport/1',
            'caption' => 'Photo 1'
        ],
        [
            'url' => 'http://lorempixel.com/1280/560/nature/2',
            'thumbnail' => 'http://lorempixel.com/500/500/nature/2',
            'caption' => 'Photo 2'
        ],
        [
            'url' => 'http://lorempixel.com/1280/560/nature/3',
            'thumbnail' => 'http://lorempixel.com/500/500/nature/3',
            'caption' => 'Photo 3'
        ],
      
    ...
 ];
 return $photos;
}

Before you start, see the example of what we’ll be building here.

The Photo Grid HTML

The HTML in the project is not complex at all. We’ll be using some of Bootstrap’s built-in selectors for the grid but most of the CSS will be our own.

First, we need to build our grid. We’ll use Bootstrap’s row and col classes to lay that out. In the PHP file, we include the file with the myPhotos() function that returns an array of photos and define a $count variable to store the total number of photos. We’ll need to reference the count later on.

<?php
    include_once 'photos.php';
    $photos = myPhotos();
    $count = count(myPhotos());
?>
<div class="row">
   <div class="col-sm-12 col-md-6 col-lg-4 col-xl-3">
      <!-- Each photo goes in here -->
   </div>
</div>

Next up, just to add a bit more structure to columns, we can add the Bootstrap mb-3 class to add a bottom margin to each column. We’ll also add another class called img-column which we’ll style later on.

Inside the column, let’s add our HTML for the image and caption. I’m wrapping the image and caption in a div which I’ll give a class of img-container. Later, we’ll add CSS styles to this div for the rotate and scale effects seen in the example so I need them to stay together, for the kids you know. Notice, the <img> tag also has a data attribute called image-index. I’m going to use this data attribute to target this image later with JQuery. The caption is right under the image with the photo-caption class and Bootstrap’s text-center class to keep the caption text centre-aligned.

Here’s the HTML for the img-container div.

<div class="img-container">
   <img class="img" src="" data-image-index="">
                    
   <div class="photo-caption text-center">
       <span><?php echo $photos[$index]['caption']; ?></span>
   </div>
</div>

And here’s our code so far.

<?php
    include_once 'photos.php';
    $photos = myPhotos();
    $count = count(myPhotos());
?>
<div class="row">
   <div class="col-sm-12 col-md-6 col-lg-4 col-xl-3">

      <div class="img-container">
         <img class="img" src="" data-image-index="">
                    
         <div class="photo-caption text-center">
            <span>Caption Goes Here</span>
         </div>
     </div>

   </div>
</div>

Loop Through the Photos Array

With the HTML done, we need to add some PHP to loop through the $photos array and output the column for every image in the array. We’re gonna need to use a for loop as opposed to a foreach loop because it gives us easy access to the current index. Since we’re accessing those indexes, we need to start the loop at 0 because the first index in the $photos array is 0. But that’s a bit of an issue because when we count in the real world, we start from 1. To overcome that, all we have to do is create another variable and add 1 to it. Let’s have a look at the for loop. You’ll see a variable called $current_position. This variable is set to whatever the index is plus 1 to get the actual number of the image.

<?php
     for ( $index = 0; $index < $count; $index++ ) : 
        $current_position = $index + 1;
     ?>
         <div class="col-sm-12 col-md-6 col-lg-4 col-xl-3 mb-3 img-column">
             <div class="img-container">

                 <img class="img" src="" data-image-index="">
                    
                 <div class="photo-caption text-center">
                     <span>Caption Goes Here</span>
                 </div>
             </div>
              
         </div>
<?php endfor; ?>

Insert The Photo’s Variables In The For Loop

Now that we have our loop going, we can use the indexes to access the keys in each photo’s array, the keys being url, thumbnail and caption. So, for example, at index 0, $photos[$index][‘thumbnail’] will get the first photo’s thumbnail. These variables need to be added to the img src attribute, the data-image-index attribute and the caption.

Here’s all the code for photo gallery grid.

<?php

    include_once 'photos.php';
    $photos = myPhotos();
    $count = count($photos);

?>
<div class="row">
    
    <?php
        for ( $index = 0; $index < $count; $index++ ) : 
        $current_position = $index + 1;
        ?>
            <div class="col-sm-12 col-md-6 col-lg-4 col-xl-3 mb-3 img-column">
                <div class="img-container">
                    <img class="img" src="<?php echo $photos[$index]['thumbnail']; ?>" data-image-index="<?php echo $current_position; ?>">
                    
                    <div class="photo-caption text-center">
                        <span><?php echo $photos[$index]['caption']; ?></span>
                    </div>
                </div>
                 
                
            </div>
    <?php endfor; ?>
  
</div>

If you load this page in the browser now, you should see your photo’s displayed but there’ll most likely be issues with the styling. To fix that, let’s add some CSS styles to these elements.

Adding CSS to the Photo Grid

I won’t go too much into the CSS since this tutorial is mostly focused on the JQuery and the inner workings of the grid and the lighbox but here’s the CSS for the grid. In a nutshell, there’s some simple styling to prevent the images from extending beyond the containers that holds them, there’s some hover effects and because the images aren’t actually in anchor tags, we’ll add the cursor: pointer CSS attribute to make it seem as they are.

.img-column:hover {
    z-index: 2;
}

.img {
    max-width: 100%;
    object-fit: contain;
}

.img-container {
    border: 3px solid #fff;
    background-color: #fff;
    transition: 0.2s;
    position: relative;
}

.img-container:hover {
    transform: scale(1.3, 1.3) rotate(-1deg);
    transition: 0.2s;
    box-shadow: 0 3px 6px 0 rgba(75, 37, 37, 0.5);
}

.img-container .img:hover {
    cursor:pointer;
}

.photo-caption {
    display: none;
    padding: 10px;
    background-color: #fff;
    color: #212121;
    position: absolute;
    bottom: 0;
    width: 100%;
}

.img-container:hover .photo-caption {
    display: block;
}

Creating the Lightbox for the Photo Gallery

In this section we’ll focus on creating the HTML for the lightbox. We need to make it look attractive and also make it easy for the user to navigate between images.

The Lightbox HTML

Since the lightbox HTML is in a seperate file we’ll request with Ajax, we need to include the photos array again. You can copy the exact code in the first PHP tag from the previous file into the lightbox PHP file.

<?php 
   include_once 'photos.php';
   $photos = myPhotos();
   $count = count($photos);
?>

Moving on to the HTML, we need to create a primary div that will hold the content of the lightbox which will act as an overlay covering the content of the page. For this, we’ll need make sure it takes up the entire width and height of the browser window and has a fixed position. You’ll see this later in the CSS, but for now let’s start adding the HTML.


<div id="lightbox">
      <button class="close-lightbox">×</button>
      <div class="lightbox-body">
      </div>
</div>

This is the basic structure of the lightbox. We have the primary div container explained above with an ID of lightbox. Since the lightbox will be taking up all the real estate on the screen, we need a way for user to dismiss it. Then, inside the lightbox div, we add the body which will be centred in the middle of the screen and be responsible for displaying the full size image and clickable thumbnails that allow the user to jump from image to image.

With the primary wrapping HTML in place, let’s add the rest of the content. We need the following elements.

  • “Slides” that will hold the full-size images.
  • Next and previous buttons.
  • An area for the caption.
  • A row of all the thumbnails.

Loop Through the Lightbox Images

Inside the lightbox body, let’s create a slide. This will be similar to how we created that image column earlier so we’ll need to loop through each image, creating an HTML element for each one of them. We don’t need to add the data attribute here since we won’t need to reference it in this area.

The slide element will obviously hold the full-size image but to give the user a reference, we’ll also add a span showing them which picture they’re currently viewing over the total. Later, we’ll user JQuery to change the text, depending on which image is showing.

 <?php
     for ( $index = 0; $index < $count; $index++ ) :
     $current_position = $index + 1;
 ?>
        
     <div class="slide">
         <span class="current-image-text">
             <?php echo 'Showing ' .  $current_position . ' of ' . $count . ' photos'; ?>
         </span>
         <img class="img" src="<?php echo $photos[$index]['url']; ?>">
     </div>
    
    <?php endfor; ?>

Previous and Next Lightbox Navigation Buttons and Caption

Now let’s add some HTML for the previous and next buttons and add an element for the caption. For the buttons, I’ve chosen to use some HTML entities for the sharp brackets (< and >) but you could use image icons, font icons or the sharp brackets themselves. The p tag in the caption container is blank on purpose. We’ll use the JQuery text() method to insert the caption later.

<button class="btn-link prev">❮</button>
<button class="btn-link next">❯</button>
<div class="caption-container">
     <p id="caption"></p>
</div>

HTML For the Lightbox Thumbnail Slider

For the final part of the lightbox, we’re going to build a thumbnail scroller/slider. This will be a Bootstrap row with each thumbnail being row column, so we have to do another loop to get each thumbnail image. We can have 12 images in a row with Bootstrap but we’ll override the row by not allowing it to wrap with CSS later.

For the thumbnails, we’re using the data-image-index attribute again because when the user clicks the thumbnail, we need to know which one was clicked. The $current_position variable in the loop will set that value same as before.

The most important class here is .thumbnail which is the selector we’ll use to listen for clicks later. The other classes are purely for styling purposes.

<div class="thumbnail-container">
   <div class="row thumbnail-scroller">
         <?php
             for ( $index = 0; $index < $count; $index++ ) :
             $current_position = $index + 1;
         ?>  
             <div class="col-1 px-1 mb-1">
                 <img class="img thumbnail" src="<?php echo $photos[$index]['thumbnail']; ?>" alt="<?php echo $photos[$index]['caption']; ?>" data-image-index="<?php echo $current_position; ?>">
             </div>
            
            <?php endfor; ?>
   </div>
</div>

And that’s it for the lightbox HTML. As mentioned before, we’ll be getting this lightbox through an Ajax call. So we can’t really see what it looks like now. So let’s jump into the JQuery to call the lightbox.

Here’s the full code for the lightbox.

<?php 
   include_once 'photos.php';
   $photos = myPhotos();
   $count = count($photos);
?>

<div id="lightbox">
  <button class="close-lightbox">×</button>
  <div class="lightbox-body">
    <?php
        for ( $index = 0; $index < $count; $index++ ) :
        $current_position = $index + 1;
    ?>
 
        <div class="slide">
          <span class="current-image-text">
              <?php echo 'Showing ' .  $current_position . ' of ' . $count . ' photos'; ?>
          </span>
          <img class="img" src="<?php echo $photos[$index]['url']; ?>">
        </div>
    
    <?php endfor; ?>
    
    <!-- Navigation buttons -->
    <button class="btn-link prev">❮</button>
    <button class="btn-link next">❯</button>

    <!-- Caption -->
    <div class="caption-container">
      <p id="caption"></p>
    </div>
    
    <div class="thumbnail-container">
        <div class="row thumbnail-scroller">
            <!-- Thumbails -->
            <?php
                for ( $index = 0; $index < $count; $index++ ) :
                $current_position = $index + 1;
            ?>  
                <div class="col-1 px-1 mb-1">
                  <img class="img thumbnail" src="<?php echo $photos[$index]['thumbnail']; ?>" alt="<?php echo $photos[$index]['caption']; ?>" data-image-index="<?php echo $current_position; ?>">
                </div>
            
            <?php endfor; ?>
        </div>
    </div>
    
  </div>
</div>

Adding the JQuery and Ajax to Load the Lightbox Photo Gallery

When the user flicks through images in the gallery, the app needs to keep track of what’s going on. In other words, it will need to know where the user is and where they want to go next. To make this possible, we will set a global variable that will be update whenever the user clicks a thumbnail or either of the navigation buttons. This variable must be declared above all the other code.

var currentSlide = 1;

The currentSlide variable’s default value is 1, basically, the first image in the array. We’ll update this each time the user performs an action that updates the lightbox.

Now we need a function that will show the image. I’ve called this function showSlide() it will take one parameter which will refer to the position of the image being requested. Remember, we set that data-image-index HTML attribute earlier. That value.

On the first two lines in the function, we’ll check for all HTML elements with the class of slide and store them in the slide variable. We can do the same for all elements with the thumbnail class.

var currentSlide = 1;
function showSlide(n) {
   let slides = $('.slide');
   let thumbnails = $('.thumbnail');
}

With these HTML elements now accessible, we can check to make sure that the requested image is in the array. For example, since our array only has 13 images, when the user clicks on the last image there’s nothing. To prevent this we can check that value. If it’s more than the total, we’ll go back to the first. Likewise, if it’s less than the total, we’ll go back to the last photo.

function showSlide(n) {
    let slides = $('.slide');
    let thumbnails = $('.thumbnail');
  
    if (n > slides.length) {currentSlide = 1}
    if (n < 1) {currentSlide = slides.length}
}

In the lightbox, only one full-size image should be shown and only one thumbnail should be active. In the next part of this function, we’ll hide all slides and remove the active class from all thumbnail elements. After that, we’ll determine which are current then show the current slide and add an active class to the appropriate thumbnail.

Finally, we’ll change the text of the caption by getting it from the alt attribute we set earlier.

Here’s the full function.

function showSlide(n) {
    // An array of elements with .slide class
    let slides = $('.slide');

    // An array of elements with .thumbnail class
    let thumbnails = $('.thumbnail');
    
    // Check if the requested index is greater than the index of the last image
    if (n > slides.length) {currentSlide = 1}

    // Check if the requested index is smaller than index of the first image
    if (n < 1) {currentSlide = slides.length}
  
  
    // Hide all elements with the slide class
    $(slides).hide();
 
    
    // Remove the .active class from all thumbnails
    $(thumbnails).removeClass('active');
  
  
    // The new side in the lightbox
    let newSlide = $(slides[currentSlide-1]);

    // The thumbnail that corresponds with the new slide in the lightbox
    let newThumbnail = $(thumbnails[currentSlide-1]);
  
  
    // Show the new slide
    newSlide.show();

    // Add the .active class to the thumbnail that corresponds with the new slide
    newThumbnail.addClass('active');

    // Update the caption
    $('#caption').text( newThumbnail.attr('alt') );
}

Handling the Image Click Events

Now that we have a function we can call, let’s use Ajax to get the lightbox HTML, call the showSlide() function when a user clicks either an image in the photo grid or a thumbnail and load it into the lightbox. Because the full-size image also has a class of img and we don’t want it to be involved in the click events, we’re being specific by targeting .img-column .img. In the function, we’re going to get the requested image by checking the value of the data-image-index attribute on the image.

The if statement checks if there are any current lightboxes in the DOM. This just ensure’s we always only have one to prevent clashes with IDs and just prevent polluting the DOM with unnecessary HTML.

The Ajax call uses the shorthand $.get method which sends a request to lightbox.php, the file we created earlier with the lightbox HTML. Since this is essentially an HTML file with no response, Ajax will determine that it is an HTML response. We then append the HTML to the body, show it if it’s hidden and call the showSlide() function. The parameter passed into the showSlide() function (currentSlide = index) updates the value of currentSlide before the function is called.

// // Click Events 
$(document).on('click', '.img-column .img, .thumbnail', function() {
    let index = $(this).data('image-index');
    
    if( !$('#lightbox').length ){
        $.get('lightbox.php', function(html) {
            $('body').append(html);
            $('#lightbox').show();
            showSlide(currentSlide = index);
        });
    }
    else {
        $('#lightbox').show();
        showSlide(currentSlide = index);
    }
});

Add Functionality to The Lightbox Navigation Arrows

The next two functions are for the click events on the lightbox navigation arrows. Basically, they get the value of what the current slide is and update currentSlide by either subtracting 1 (for previous) or adding 1 (for next).

// Navigation Buttons
$(document).on('click', '.prev', function() {
    showSlide(currentSlide -= 1);
});

$(document).on('click', '.next', function() {
    showSlide(currentSlide += 1);
});

Closing The Lightbox With a Click Event Handler

Finally, we need to add functionality to close the lighbox. This won’t just hide the lightbox, but remove it from the DOM completely.

// Close Button
$(document).on('click', '.close-lightbox', function() {
    $('#lightbox').remove();
});

If you test this project now, the code will work, but the lightbox HTML is gonna be quite messed up. Remember, we appending it to the body but it doesn’t have any styling whatsoever. We need it to be on top of everything so let’s do that.

Styling the Lightbox Photo Gallery With CSS

The CSS portion of the lightbox is really simple. The main container that wraps everything is hidden by default. We don’t want it showing as soon as we append it to the body. This gives us the freedom to do some things before we show it, like animations for example. It’s also fixed but will allow scrolling and has a z-index of 3 so it can be on top of all the other elements. The lightbox body element’s position is relative, it has an auto margin to center it and there’s some padding and a max-width.

#lightbox {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    z-index: 3;
    padding: 50px 30px;
    width: 100vw;
    height: 100vh;
    overflow: auto;
    background-color: rgba(0,0,0,0.8);
}

.lightbox-body {
    position: relative;
    background-color: #f5f5f5;
    margin: auto;
    padding: 0;
    width: 90%;
    max-width: 1200px;
}

Adding the Final CSS Styles to the Lightbox

Right now, you should have a completely working lightbox, but we can make look a lot better by adding some extra CSS. I won’t go into all of it but here’s the code for that.

.current-image-text {
  color: #f2f2f2;
  font-size: 12px;
  padding: 8px 12px;
  position: absolute;
  top: 0;
}

.slide {
  display: none;
}

.caption-container {
  text-align: center;
  background-color: rgb(8,127,121);
  padding: 10px 16px;
  color: #fff;
}

#caption {
    padding: 0;
    margin: 0;
}

.prev,
.next {
  cursor: pointer;
  position: absolute;
  top: 50%;
  width: auto;
  padding: 16px;
  margin-top: -50px;
  background: none;
  border: none;
  color: white;
  font-weight: bold;
  font-size: 20px;
  transition: 0.6s ease;
  border-radius: 0 3px 3px 0;
  user-select: none;
  -webkit-user-select: none;
}


.next {
  right: 0;
  border-radius: 3px 0 0 3px;
}

/* On hover, add a black background color with a little bit see-through */
.prev:hover,
.next:hover {
  background-color: rgba(0, 0, 0, 0.8);
}

.thumbnail-container {
    padding: 20px 24px;
}

.thumbnail-scroller {
    flex-wrap: nowrap;
    overflow-x: scroll;
    padding-bottom: 10px;
}

.thumbnail-scroller::-webkit-scrollbar {
	width: 10px;
    height: 10px;
	background-color: rgba(200,200,200,0);
    border-radius: 6px;
}


.thumbnail-scroller::-webkit-scrollbar-thumb {
    border-radius: 6px;
    background-color: rgb(8,127,121);
 }

.thumbnail {
    cursor: pointer;
}

.close-lightbox {
    color: #fff;
    position: absolute;
    top: 10px;
    right: 25px;
    font-size: 40px;
    font-weight: 700;
    border:none;
    background: none;
}

.close-lightbox:hover,
.close-lightbox:focus {
    border:none;
    color: #f00;
}

Conclusion

As you can see, coding and designing a lightbox photo gallery is really simple. This is one of this rare cases where the CSS is more than the scripting. Since we used Ajax to create this photo gallery, you can take this concept further by adding filters and pagination. Good luck and happy coding.

Is this still valid in 2021? Please let me know in the comments below.

Was This Helpful?

Authenticate and Safeguard Your Laravel API With Sanctum
Previous Post
Authenticate and Safeguard Your Laravel API With Sanctum
How to Merge Two or More Arrays in JavaScript
Next Post
How to Merge Two or More Arrays in JavaScript

2 Comments

  • 16th Nov 2020 at 5:57 pm
    Anon

    Pretty! This was an extremely wonderful post. Many thanks for providing this information.

    Reply

Leave a Reply