Multiple Image Galleries in a Single Page using ES6 JavaScript

Posted in Web-Development
Tags :
JavaScript HTML5 CSS3


Ever wondered how to create and embed many Image Slideshows into a single web page? This post shows you how!


In my previous post about Linked List, I needed to add multiple image galleries into it for the purose of showing step by step diagrams of each Linked List operation, however, the code I already had written was good for only a single Image Gallery, so I successfuly reworked that code into an ES6 Class to make it reusable across a single web page, to create multiple independent image galleries, In this post, I will share and explain that code to you

Starting out with HTML

We will implement our Image Gallery with simple HTLM5, ES6 JS and CSS3, no fancy libraries will be used for this. Our Image Gallery will have a counter on the top-left, the usual arrows to move to the next or previous image, a counter to show which image you are currently on, and each image will have a caption, now that this is out of the way, we can start out with the HTML skeleton of our image gallery.

The below code snippet just contains the first image gallery, the rest of the HTML has been elided for brevity a more complete version of the HTML can be found on either the CodePen of this code or on it’s GitHub respository, both of which are linked to in the end of this article.


<div class="container">
<div id="first" class="gallery-container">
  
  <div class="mySlides" style="display: block;">
    <div class="numbertext">1 / 3</div>
        <div class="fullscreen-btn" onclick="ImageGallery.toggleFullscreenImage('https://cdn.pixabay.com/photo/2020/05/15/14/03/lake-5173683_960_720.jpg')" src="https://cdn.pixabay.com/photo/2020/05/15/14/03/lake-5173683_960_720.jpg')"><i class="fas fa-expand"></i></div>
        <img class="imgSlide" onclick="ImageGallery.toggleFullscreenImage('https://cdn.pixabay.com/photo/2020/05/15/14/03/lake-5173683_960_720.jpg')" src="https://cdn.pixabay.com/photo/2020/05/15/14/03/lake-5173683_960_720.jpg" style="width:100%" >
    <div class="caption-container">
        <p id="caption"> Drying Lake </p>
    </div>
  </div>
  
  <div class="mySlides" style="display: none;">
    <div class="numbertext">2 / 3</div>
        <div class="fullscreen-btn" onclick="ImageGallery.toggleFullscreenImage('https://cdn.pixabay.com/photo/2018/08/12/16/59/ara-3601194_960_720.jpg')"><i class="fas fa-expand"></i></div>
        <img class="imgSlide" onclick="ImageGallery.toggleFullscreenImage('https://cdn.pixabay.com/photo/2018/08/12/16/59/ara-3601194_960_720.jpg')" src="https://cdn.pixabay.com/photo/2018/08/12/16/59/ara-3601194_960_720.jpg" style="width:100%" >
    <div class="caption-container">
        <p id="caption"> Colorful Parrot </p>
    </div>
  </div>
  
  <div class="mySlides" style="display: none;">
    <div class="numbertext">3 / 3</div>
        <div class="fullscreen-btn" onclick="ImageGallery.toggleFullscreenImage('https://cdn.pixabay.com/photo/2014/02/27/16/10/tree-276014_960_720.jpg')"><i class="fas fa-expand"></i></div>
        <img class="imgSlide" onclick="ImageGallery.toggleFullscreenImage('https://cdn.pixabay.com/photo/2014/02/27/16/10/tree-276014_960_720.jpg')" src="https://cdn.pixabay.com/photo/2014/02/27/16/10/tree-276014_960_720.jpg" style="width:100%" >
    <div class="caption-container">
        <p id="caption"> Flowers & Grass </p>
    </div>
  </div>
      <a class="prev">❮</a>
  <a class="next">❯</a>
  </div>
  </div>

In the above code we have a main div with the class container, In this div sits all your content, i.e the text and image galleries, and the CSS class container just centers its content and limits the width to about 50% wide. The next div has an id of first and a class of gallery-container, this is the div which is basically our first image gallery, and another seperate image gallery can be made just like this with just the id changed from first to something else, in the JS code we will write, the id is the unique identifier for this image gallery.

<div class="mySlides" style="display: block;">
    <div class="numbertext">1 / 3</div>
        <div class="fullscreen-btn" onclick="ImageGallery.toggleFullscreenImage('https://cdn.pixabay.com/photo/2020/05/15/14/03/lake-5173683_960_720.jpg')" src="https://cdn.pixabay.com/photo/2020/05/15/14/03/lake-5173683_960_720.jpg')"><i class="fas fa-expand"></i></div>
        <img class="imgSlide" onclick="ImageGallery.toggleFullscreenImage('https://cdn.pixabay.com/photo/2020/05/15/14/03/lake-5173683_960_720.jpg')" src="https://cdn.pixabay.com/photo/2020/05/15/14/03/lake-5173683_960_720.jpg" style="width:100%" alt=" tail.next = newNode; ">
    <div class="caption-container">
        <p id="caption"> A Lake of some kind </p>
    </div>
  </div>

Inside our gallery-container, we have the mySlides div, which is a single Image slide, this contains the number of the image, a fullscreen button, the Image itself, and a seperate container for the image’s caption, for each image you will have many such mySlides divs in your gallery-container image gallery.

I highly recommend to use a templating engine to create these mySlide divs in a loop from arrays with the Image src and caption text provided, like I have done with my Linked List Blog Post’s image slides, these divs can also be created using JS, but I will not be getting into that.

ES6 JavaScript to make it work

The way our multiple Image Galleries or slideshows will work, is every Image gallery container div in a given page will have a unique id, which the JS code will use to lock into that specific div container and perform the operations within, like showing previous/next image, making an image fullscreen etc, we will supply the id to the constructor function of this JS Class and also name the object that the constructor instantiates as the same as the id provided.

let first = new ImageGallery("first");

In the next line we see the code for our ImageGallery class and it’s constructor.

class ImageGallery {
  constructor(id) {

    this.container = document.getElementById(id);
    this.prevButton = this.container.querySelector(".prev");
    this.nextButton = this.container.querySelector(".next");
    this.slideIndex = 1;
    var that = this;

    this.showSlide(this.slideIndex);

    this.prevButton.addEventListener("click", function () {
      that.advanceSlide(-1);
    });
    this.nextButton.addEventListener("click", function () {
      that.advanceSlide(1);
    });
  }

}


let first = new ImageGallery("first");

In the first line of code we take in the id provided as a parameter to the constructor, and use it to find a div with a matching id using document.getElemendById(id), now this div contains our Image gallery, it’s slides and its previous and next buttons, so we create a class property this.prevButton and assign it the element found by matching the .prev class inside this.container using this.container.querySelector(".prev"), we do the same thing for the this.nextButton, we set another property slideIndex to 1, the slideIndex property is what controls which image is to be displayed according to it’s numerical position among other images in the gallery.

We then initialize the this.showSlide function which will be implemented later on, this shows the first image slide in the gallery, we then add onClick Event Listeners to both the this.prevButton and this.nextButton, so when a user clicks on either of these buttons, the this.advanceSlide() function is called, with -1 if prevButton is clicked to show the previous image, or 1 if nextButton is clicked to show the next image.

Now that we are done with our constructor, we will see the showSlide and advanceSlide methods, these must be inside the ImageGallery class, and i’ve elided the constructor that we have already seen in detail.

class ImageGallery {
 
  advanceSlide = function (n) {
    this.showSlide((this.slideIndex += n));
  };

  showSlide = function (n) {
    var slide = this.container.getElementsByClassName("imageSlide");
    if (n > slide.length) {
      this.slideIndex = 1;
    }
    if (n < 1) {
      this.slideIndex = slide.length;
    }
    for (var i = 0; i < slide.length; i++) {
      slide[i].style.display = "none";
    }
    slide[this.slideIndex - 1].style.display = "block";
  };


}

As you can see, advanceSlide is a wrapper function around this.showSlide, the parameter it supplies is assigned to the this.slideIndex and is this.slideIndex current value plus the parameter n, for e.g if there are 7 images in the gallery, and the slideIndex is set to 4, a click on prevButton will turn n to -1, which when added to 4 will be 3, decreasing the slideIndex, and this.showSlide() will then show the 3rdimage.

Next up is showSlide(), this controls the back and forth functionality of our image gallery, first we get an array of all the image divs that have the className imageSlide using var slide = this.container.getEelementsByClassName("imageSlide"), this gets the image div array and stored it in var slide.

The following if statements are to create wrap-around functionality, if the n provided is lesser than 1 or greater than the length of the slide array, if the n is lesser, like say 0 or -1, the slideIndex is set to 1, if the n is greater for e.g 8 or 9 than the slide.length that is say 7, the slideIndex is set to the slide.length, i.e the last image in the array.

We then have a for loop to hide every div in the slide array by setting it’s CSS display property to none, and then we pick our the image div element from the slide array using the this.slideIndex and display it by setting the CSS display property to block.

Now there is one last function to take care off, this is the toggleFunscreenImage() method given below, as before, previously explained functions are not shown for brevity.


class ImageGallery {

  static toggleFullscreenImage(imagename = null) {
    var fullscreenDisplayDiv = document.getElementById("fullscreen-display");
    var img = fullscreenDisplayDiv.getElementsByTagName("img");
    var close = fullscreenDisplayDiv.getElementsByTagName("i");
    if(imagename != null) {
        img[0].setAttribute("src", "/public/assets/images/" + imagename + ".png");
        close[0].style.display = "block";
        fullscreenDisplayDiv.className = "fullscreen-display-show";
        window.document.body.style.overflow = "hidden";
    } else {
        img[0].setAttribute("src", " ");
        close[0].style.display = "none";
        fullscreenDisplayDiv.className = "fullscreen-display-hide";
        window.document.body.style.overflow = "auto";
    }

  }

}

How this works is there is a hidden div on your page with the id of fullscreen-display, and every image in every one of your galleries has a Fullscreen icon button, this button upon being clicked calls the toggleFullscreenImage() function with the src of the image as the parameter, this src is then set to the img div inside the fullscreen-display div and this div is then un-hidden, and because of CSS classes it uses, goes fullscreen with the provided image, in this fullscreen div, a red X button is provided to close it, which calls the same toggleFullscreenImage() button but without any parameters.

Since that’s out of the way, we now explain the implementation, You will notice the toggleFullscreenImage method is marked as static which means that this specific function can be called without instantiation as an object, the next thing is the imagename = null as the parameter, this is a default parameter, which means if nothing is specified for imagename i.e the function is called without any parameters, set the imagename parameter to null.

We then get the fullscreenDisplayDiv using document.getElementById("fullscreen-display");, remember this is the hidden div that will go fullscreen with the provided image when the fullscreen button is clicked. The next vars, img and close are the image tag that will be displayed fullscreen and the red X button to close the fullscreen.

The if else block that comes next either opens the fullscreen or closes it, depending if imagename is null or not, if it is not and an image name is specified, the src attribute of the img is set to the imagename, the red X button represented by the close var is unhidden by setting it’s display to block, the fullscreen display div is revealed by chanding it’s class to fullscreen-display-show and the scrolling of the page is disabled using window.document.body.style.overflow = "hidden".

The Else part of the block does the opposite, which is triggered when the imagename is null, i.e the user has clicked the close button in the fullscreen mode.

CSS Styling

Of course our Image Gallery just wouldn’t work without the proper CSS styling and neither would the fullscreen functionality, so here it is.

:root {
  --main-blk-color: #25282b;
  scroll-behavior: smooth;
}
.mySlides {
  display: none;
}

.mySlides img {
  margin:0!important;
}

.gallery-container {
  position: relative;
  border:5px solid var(--main-blk-color);
}

.caption-container {
  text-align: center;
  background-color: var(--main-blk-color);
  padding: 2px 16px;
}

imgSlide img {
    display:block;
}


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

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

.prev:hover,
.next:hover {
  background-color: rgba(0, 0, 0, 0.8);
}

.numbertext {
  color: #f2f2f2;
  font-size: 12px;
  padding: 8px 12px;
  position: absolute;
  top: 0;
  background-color: rgba(0, 0, 0, 0.5);
}

#caption {
  color: white!important;
  margin: 1rem auto;
}

.container, p, h1 {
  width:50%;
  margin:auto;
}

p {
  font-size:20px;
  padding:1rem 0;
}

h1 {
  padding:2rem 0;
  font-size: 40px;
  text-align:center;
}
.fullscreen-btn {
  color: #f2f2f2;
  font-size: 24px;
  padding: 8px 12px;
  position: absolute;
  cursor:pointer;
  top: 0;
  right:0;
  background-color: rgba(0, 0, 0, 0.5);
}

.fullscreen-display-hide {
  display:none;
}

.fullscreen-display-hide i {
  display:none;
}

.fullscreen-display-show {

  position:fixed; /* takes up whole viewport */
  left:0; /*snaps div to left*/
  top:0;/*snaps div to left*/
  display:flex;
  z-index:1000;
  width: 100%;
  height:100%;
  background-color:rgba(0, 0, 0, 0.9);
}

.fullscreen-display-show  img {
  height: fit-content;
  margin:auto!important
}
.fullscreen-display-show i {
  display: block;
  font-size: 3rem;
  color:white;
  cursor:pointer;
  padding:1rem 1.5rem;
  background-color:#dc3545!important;
  height: fit-content;
  border-radius: 10px;
  margin:0.5rem;
  position:absolute;
  right:0;
}

Conclusion.

That’s a wrap, I hope you’ve learned much from this little post, a live demo of this code is available on my CodePen, and the rest of the code including the CSS is on GitHub, as mentioned earlier, you will probably need to write either a JavaScript generator function or use a templating engine( i’m using Jekyll’s Liquid templating engine btw ) for the grunt work of creating the imageSlide divs with their counters, image with src and caption, so good luck with that.

The CodePen is embedded below for convinience, but it looks better when opened in it’s own tab.

See the Pen by Shawn D'silva (@shade97) on CodePen.