Getting a next/previous button working on an image gallery

Getting a next/previous button working on an image gallery
0

#1

Hi,

Can someone please help regarding my modal for my image gallery. I want my next & previous buttons,(which are only visible on a reduced browser size) to intuitively click through the images in the gallery. I have put some snippets of the code in question below, as well as a link to the site. I would have done a codepen, but I cannot get it working on there for some reason.

My next button kind of works, but the var currentImage value is set to numerical value, which I think is causing a problem. I just don’t have any idea how to solve it.

I have no idea where to start regarding clicking back through the gallery with the previous button either.

Thanks for your help

https://kubix90.github.io/css_website_clone/

HTML

<section id="modal">
        <section class="modal-background">
            <div class="close-btn">&times;</div>
            <figure>
                <img class="img-main" src="public/images/bird.jpg" alt="Bird on a tree">
            </figure>
            <figure class="modal-gallery">
                <img src="public/images/bird.jpg" alt="Bird on a tree">
                <img src="public/images/boat.jpg" alt="Boat at night">
                <img src="public/images/dog.jpg" alt="Dog sitting">
                <img src="public/images/fox.jpg" alt="Fox sitting">
                <img src="public/images/island.jpg" alt="Island">
                <img src="public/images/table-flowers.jpg" alt="Flowers on a table">
                <img src="public/images/city.jpg" alt="Village during the day">
                <img src="public/images/prague.jpg" alt="City of Prague at night">
                <img src="public/images/ducks.jpg" alt="Ducks in a pond">
                <img src="public/images/squirrel.jpg" alt="Squirrel in the snow">
                <img src="public/images/lemon.jpg" alt="Lemon slice">
                <img src="public/images/church.jpg" alt="Small church at night">
            </figure>
            <section class="modal-btns">
                <button class="modal-btn prev-btn">&lt;</button>
                <button class="modal-btn next-btn">&gt;</button>
            </section>
        </section>
    </section>

JS

///// TOGGLE IMAGE GALLERY MODAL ON CLICK //////

var imgOverlays = document.querySelectorAll(".imgs section div");
var mainImg = document.querySelector(".img-main");
var allModalImgs = document.querySelectorAll(".modal-gallery img");
var modal = document.querySelector(".modal-background");
var closeBtn = document.querySelector(".close-btn");
var opacity = 0.4;

function resetOpacity() {
    for (i = 0; i < allModalImgs.length; i++) {
        allModalImgs[i].style.opacity = 1;
    }
}

document.querySelector("#gallery .imgs").addEventListener("click", function (e) {
    for (i = 0; i < imgOverlays.length; i++) {
        if (e.target == imgOverlays[i]) {
            modal.style.display = "block";
            mainImg.src = e.target.previousElementSibling.src;
        }
    }
});

for (i = 0; i < allModalImgs.length; i++) {
    allModalImgs[i].addEventListener("click", function (e) {
        resetOpacity();
        mainImg.src = e.target.src;
        e.target.style.opacity = opacity;
    });
};

closeBtn.addEventListener("click", function () {
    resetOpacity();
    modal.style.display = "none";
});

////// CLICK THROUGH GALLERY WITH RESPONSIVE BUTTONS //////

prevBtn = document.querySelector(".prev-btn");
nextBtn = document.querySelector(".next-btn");

var currentImg = 1;

nextBtn.addEventListener("click", function () {
    mainImg.src = allModalImgs[currentImg].src;

    currentImg = (currentImg += 1) % allModalImgs.length
});

#2

Beside knowing quantity of images, you’ll have to know number of clicks. You also need a way to “catch” so to speak these images. I’m suggesting getting their parent element and iterate through children. You also have to know previous if any child, max number of children, active child. And when you reach end of children, you can either: go back at first pic or iterate in reverse. Tip: parent identification can be “figure.modal-gallery” if you don’t have anymore gallery, then i suggest using id’s for parent figure. This should get you started.


#3

EDIT: I went back and created the following code to only have a single addEventListener to handle both the prev-btn and the next-btn. It moves to the next image on click and when it gets to the first or last image, it correctly either goes to the end or the start.

////// CLICK THROUGH GALLERY WITH RESPONSIVE BUTTONS //////

var modalBtns = document.querySelector(".modal-btns");

modalBtns.addEventListener("click", function (event) {
  var target = event.target;
  if (target.classList.contains('prev-btn') || target.classList.contains('next-btn')) {
    var direction = target.classList.contains('prev-btn') ? -1 : 1;
    var currImg = document.querySelector(".modal-gallery > img[src='" + mainImg.src + "']");
    var imgsUrls = [...allModalImgs].map(({src}) => src);
    var nextIndex = imgsUrls.indexOf(currImg.src)  + direction;
    nextIndex = nextIndex === imgsUrls.length ? 0 : nextIndex < 0 ? imgsUrls.length - 1 : nextIndex;
    mainImg.src = imgsUrls[nextIndex];
  }
});

You could move the following line outside of the callback function so the imgsArr is only created a single time.

var imgsUrls = [...allModalImgs].map(({src}) => src);

#4
  1. Embed data-* atribute for each <img> e.g) <img data-index="0">
  2. Maintain an array of images in .modal-gallery. Now you can open any specific image and know exactly where you are.
  3. From there, increment/decrement the index relative to the currently picked image’s index and show it.

You can add custom data attribute to your image when you are populating .modal-gallery. No one writes images in some gallery by hard coding them inside HTML. Often, you get the data required for image gallery from somewhere else and build them with JavaScript code.


#5

Hey,

Thanks for the response.

I’ve tried using this, but the var nextIndex is throwing up an error (cannot read property src of null). The var currImg variable looks good to me, not sure why it’s not being picked up in the code, any ideas?


#6

Cheers, there’s a lot of new info in this thread that I’ve not come across before. So going to have to do a bit of reading. Can’t say I 100% understand where to go with this (although I think I’m getting my head round Randells solution).

Thought this might be a bit simpler than it’s turning out, but good opportunity to learn some new bits i guess.


#7

Do you have a link to project with the code I suggested in it? If not, just post the html and JS code in a reply. Please use 3 backticks on line before and after code so the code formats correctly in the post.


#8

I’ve dealt with almost identical question recently. So, there is a sample code for this and listing of progression. Maybe, you will find one of them useful.

Link to original question

Link to the code samples


#9

I would give you a codepen, but I can’t get it working. The best i have is the link to the site and the GitHub repository below.

Hope that helps


#10

Cheers, I’ll take a look through this.


#11

@gunhoo93, @RandellDawson

Hi Both,

I am still struggling with this. I looked through your threads @gunhoo93 but struggled to understand what was going on. The approach you suggested seems simple enough, I know how to get an array of image src’s, but I don’t understand how to set the index value to the correct image src in the array based on what I clicked (hope that make sense).

I have added the code below that is throwing the up the error that i mentioned earlier @RandellDawson. Hopefully it’s good enough to use. I don’t really understand why the error I mentioned keeps occurring.

Thanks for your help both

HTML

    <section id="modal">
        <section class="modal-background">
            <div class="close-btn">&times;</div>
            <figure>
                <img class="img-main" src="public/images/bird.jpg" alt="Bird on a tree">
            </figure>
            <figure class="modal-gallery">
                <img src="public/images/bird.jpg" alt="Bird on a tree">
                <img src="public/images/boat.jpg" alt="Boat at night">
                <img src="public/images/dog.jpg" alt="Dog sitting">
                <img src="public/images/fox.jpg" alt="Fox sitting">
                <img src="public/images/island.jpg" alt="Island">
                <img src="public/images/table-flowers.jpg" alt="Flowers on a table">
                <img src="public/images/city.jpg" alt="Village during the day">
                <img src="public/images/prague.jpg" alt="City of Prague at night">
                <img src="public/images/ducks.jpg" alt="Ducks in a pond">
                <img src="public/images/squirrel.jpg" alt="Squirrel in the snow">
                <img src="public/images/lemon.jpg" alt="Lemon slice">
                <img src="public/images/church.jpg" alt="Small church at night">
            </figure>
            <section class="modal-btns">
                <button class="modal-btn prev-btn">&lt;</button>
                <button class="modal-btn next-btn">&gt;</button>
            </section>
        </section>
    </section>

JS

////// TOGGLE IMAGE GALLERY MODAL ON CLICK //////

var imgOverlays = document.querySelectorAll(".imgs section div");
var mainImg = document.querySelector(".img-main");
var allModalImgs = document.querySelectorAll(".modal-gallery img");
var modal = document.querySelector(".modal-background");
var closeBtn = document.querySelector(".close-btn");
var opacity = 0.4;

function resetOpacity() {
  for (i = 0; i < allModalImgs.length; i++) {
    allModalImgs[i].style.opacity = 1;
  }
}

document.querySelector("#gallery .imgs").addEventListener("click", function(e) {
  for (i = 0; i < imgOverlays.length; i++) {
    if (e.target == imgOverlays[i]) {
      modal.style.display = "block";
      mainImg.src = e.target.previousElementSibling.src;
    }
  }
});

for (i = 0; i < allModalImgs.length; i++) {
  allModalImgs[i].addEventListener("click", function(e) {
    resetOpacity();
    mainImg.src = e.target.src;
    e.target.style.opacity = opacity;
  });
}

closeBtn.addEventListener("click", function() {
  resetOpacity();
  modal.style.display = "none";
});

////// CLICK THROUGH GALLERY WITH RESPONSIVE BUTTONS //////

var modalBtns = document.querySelector(".modal-btns");
var imgsUrls = [...allModalImgs].map(({src}) => src);


modalBtns.addEventListener("click", function (event) {
  var target = event.target;
  if (target.classList.contains('prev-btn') || target.classList.contains('next-btn')) {
    var direction = target.classList.contains('prev-btn') ? -1 : 1;
    var currImg = document.querySelector(".modal-gallery > img[src='" + mainImg.src + "']");
    var nextIndex = imgsUrls.indexOf(currImg.src)  + direction;
    nextIndex = nextIndex === imgsUrls.length ? 0 : nextIndex < 0 ? imgsUrls.length - 1 : nextIndex;
    mainImg.src = imgsUrls[nextIndex];
  }
});

#12

Setting index of each image is a second concern. For starter, you should probably start with hard coded indexes.

In pseudocode, the structure looks like this

current-image-index = 0
images = <Gallery>.children

<Container>
    <Gallery>
        <Image data-index="0">
        <Image data-index="1">
        <Image data-index="2">
    <Modal>

<Image>.onClick( event
    current-image-index = event.target.attribute("data-index")
    display-modal( images[current-image-index] )
)

<Modal>.onClick( nextButton
    current-image-index += 1
    display-modal( images[current-image-index] )
)
...

#13

Cheers for this, ended up working it out using the data attribute. Code is below if you’re interested.

////// TOGGLE IMAGE GALLERY MODAL ON CLICK //////

var galleryImgOverlays = document.querySelectorAll("#gallery .imgs section div");

var modalContainer = document.querySelector(".modal-background");

var allModalImgs = document.querySelector(".modal-gallery").children;
var modalImgsSrc = [...allModalImgs].map(({src}) => src);
var modalMainImg = document.querySelector(".img-main");

var modalClose = document.querySelector(".close-btn");
var modalImgPrev = document.querySelector(".prev-btn");
var modalImgNext = document.querySelector(".next-btn");

var imgIndex = 0;

var opacity = 0.4;

function resetOpacity() {
  for (i = 0; i < allModalImgs.length; i++) {
    allModalImgs[i].style.opacity = 1;
  }
}

document.querySelector("#gallery .imgs").addEventListener("click", function (e) {
  for (i = 0; i < galleryImgOverlays.length; i++) {
    if (e.target == galleryImgOverlays[i]) {
      resetOpacity();
      modalContainer.style.display = "block";
      imgIndex = parseInt(e.target.dataset.index);
      modalMainImg.src = modalImgsSrc[imgIndex];
    }
  }
})

for (i = 0; i < allModalImgs.length; i++) {
  allModalImgs[i].addEventListener("click", function (e) {
    resetOpacity();
    modalMainImg.src = e.target.src;
    e.target.style.opacity = opacity;
  });
}

modalClose.addEventListener("click", function () {
  modalContainer.style.display = "none";
});

////// CLICK THROUGH GALLERY WITH RESPONSIVE BUTTONS //////

modalImgNext.addEventListener("click", function () {
  imgIndex = (imgIndex += 1) % modalImgsSrc.length;
  modalMainImg.src = modalImgsSrc[imgIndex];
});

modalImgPrev.addEventListener("click", function () {
  imgIndex = (imgIndex -= 1);
  if (imgIndex < 0) {
    imgIndex = modalImgsSrc.length - 1;
  }
  modalMainImg.src = modalImgsSrc[imgIndex];
});