Home > front end >  How to make an image gallery out of a stack of images (on image click)?
How to make an image gallery out of a stack of images (on image click)?

Time:12-10

I have a stack of three images on top of each other. I want the images to be clickable so that every time I click the image on top of the stack, the image underneath is on top. So far, I could manage it that each image is clickable but the effect is not what I want. Do I have to add a class (with a high z-index) on click? Or how can I solve this? Thanks for your help!

Here's my code:

    <script>
        const images = document.querySelectorAll('.img-stack img');

        images.forEach(function (btn) {
            btn.onclick = function () {
                this.style.zIndex = "100";
            }
        });
    </script>
  main {
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  main .img-container {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    width: 800px;
    height: 400px;
  }
  main .img-container .text-container {
    width: 500px;
    padding: 120px;
  }
  main .img-container .img-stack {
    position: relative;
    width: 300px;
    height: 400px;
  }
  main .img-container .img-stack img {
    position: absolute;
    top: 0;
    left: 0;
    cursor: pointer;
  }
  main .img-container .img-stack img:nth-child(1) {
    width: 300px;
    height: 400px;
    background-color: skyblue;
    transform: rotate(-6deg);
  }
  main .img-container .img-stack img:nth-child(2) {
    width: 300px;
    height: 400px;
    background-color: coral;
    transform: rotate(9deg);
  }
  main .img-container .img-stack img:nth-child(3) {
    width: 300px;
    height: 400px;
    background-color: crimson;
    transform: rotate(3deg);
  }
<main>
        <div >
            <div >
                <h1>Lorem Ispum</h1>
                <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
                    labore et dolore magna aliquyam erat, sed diam voluptua.</p>
            </div>
            <div >
                <img>
                <img>
                <img>
            </div>
        </div>
    </main>

CodePudding user response:

Here's what you can do:

Problem

We want to , basically, rotate the images.
  1. The image that appears on top to be placed at the bottom

  2. The image that is in the middle to be placed on top

  3. Repeat.

Let's clarify how DOM img elements are rendered:

The img elements are positioned absolute. So:

The first img element (in your html) will appear at the bottom.
The second in the middle.
The third on top.

You can imagine placing cards from your hand to a table one by one. It will work the same way. The one you deal/place first it will appear last on the table.

Solution

I took the liberty to give ids to you img elements. I think it makes more sense because since we want to rotate them we don't want always the first image to have the same color.

Instead we want each img to be distinct and have each own color that will not change based on its position. So, giving ids in this situation helps.

If you undestand all these so far, the solution is pretty easy. Insice onClick() we just:

  1. grab the last (in our html) img tag, that renders on top and
  2. we put it first (in our html) to appear on the bottom with prepend()

More info: https://developer.mozilla.org/en-US/docs/Web/API/Element/prepend

P.S. You were pretty close and the z-index approach could work but generally I think messing around with z-index too much can be quite messy.
Cool effect, btw.

Let me know if you have further questions. :)

const imageNodes = document.querySelectorAll('.img-stack img');
        const imageStack = document.querySelector('.img-stack');
        imageNodes.forEach(btn => {
            btn.onclick =   function() {
                let image = document.querySelector('img:last-child');
                imageStack.prepend(image);   
            }
        });
main {
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  main .img-container {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    width: 800px;
    height: 400px;
  }
  main .img-container .text-container {
    width: 500px;
    padding: 120px;
  }
  main .img-container .img-stack {
    position: relative;
    width: 300px;
    height: 400px;
  }
  main .img-container .img-stack img {
    position: absolute;
    top: 0;
    left: 0;
    cursor: pointer;
  }
  main .img-container .img-stack #img1 {
    width: 300px;
    height: 400px;
    background-color: skyblue;
    transform: rotate(-6deg);
  }
  main .img-container .img-stack #img2 {
    width: 300px;
    height: 400px;
    background-color: coral;
    transform: rotate(9deg);
  }
  main .img-container .img-stack #img3 {
    width: 300px;
    height: 400px;
    background-color: crimson;
    transform: rotate(3deg);
  }
<main>
        <div >
            <div >
                <h1>Lorem Ispum</h1>
                <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
                    labore et dolore magna aliquyam erat, sed diam voluptua.</p>
            </div>
            <div >
                <img id="img1">
                <img id="img2">
                <img id="img3">
            </div>
        </div>
    </main>

CodePudding user response:

Based on our exchange, via the comment section:

I would like to confirm if I understand your question correctly. You have three images that are stack one above the other. What you want is that when the image at the top of the stack is click on, to move that image to the bottom of the stack, and display on top the image that was below the image that was just moved to the bottom. Is this correct? – acarlstein

@acarlstein that is correct. My idea was to solve it with adding a class (or id) to the top image but I'm stuck. – Sveninho

Here is one way using pure JavaScript. I will try to see if I have time to add a CSS way.

let stackImages = document.getElementsByClassName("stack-image");
document.getElementById("btn-method-1").addEventListener("click", (e) => {
  if (stackImages.length < 1) return; 
  let frontImage = stackImages[stackImages.length - 1];
  let backImage = stackImages[0];
  backImage.before(frontImage);
});
.container {
  position: relative;
}
.stack-image {
  position: absolute;
}
<button id="btn-method-1">Swap Image Position</button>
<div >
  <img  src="https://fakeimg.pl/350x200/?text=ImageC">
  <img  src="https://fakeimg.pl/350x200/?text=ImageB">
  <img  src="https://fakeimg.pl/350x200/?text=ImageA">
  <!-- bottom image is the one that will be always displayed to the user -->
</div>

Alternative by chancing the CSS zIndex:

let stackImages = document.getElementsByClassName("stack-image");
let len = stackImages.length;
let zIndexes = [...Array(len)].map((_,i) => i  );

function swapImages() {
  zIndexes.push(zIndexes.shift());
  for (let i = 0; i < len;   i){
   stackImages[i].style.zIndex = zIndexes[i]; 
  }
}

document.getElementById("btn-method-1").addEventListener("click", (e) => {
  swapImages();
});
.container {
  position: relative;
}
.stack-image {
  position: absolute;
}
<button id="btn-method-1">Swap Image Position via changing zIndex</button>
<div >
  <img  src="https://fakeimg.pl/350x200/?text=ImageC">
  <img  src="https://fakeimg.pl/350x200/?text=ImageB">
  <img  src="https://fakeimg.pl/350x200/?text=ImageA">
  <!-- bottom image is the one that will be always displayed to the user -->
</div>

  • Related