Razan Afghani
Razan Afghani

Reputation: 1

Carousel slides appear beneath each other on click

I'm using HTML/CSS and vanilla JS. I'm trying to create a simple carousel. However, whenever I click the 'next' button, the following slides show up under the last one. I'm not sure why this is happening. Code snippets are below. Can someone explain why this is happening?

By the way, I have not optimized this page for media queries yet, so it might look a little weird on smaller screens.

const buttons = document.querySelectorAll("[data-carousel-btn]");

buttons.forEach((button) => {
  button.addEventListener("click", () => {
    const offset = button.dataset.carouselBtn === "next" ? 1 : -1;
    const slidesContainer = button
      .closest("[data-carousel]")
      .querySelector("[data-carousel-slides");
    const slides = slidesContainer.querySelectorAll("[data-carousel-slide]");
    const activeSlide = slidesContainer.querySelector("[data-active]");
    const activeSlideIndex = [...slides].indexOf(activeSlide);
    const nextSlideIndex = activeSlideIndex + offset;
    if (nextSlideIndex < 0) {
      slides[slides.length + nextSlideIndex].dataset.active = true;
      return delete activeSlide.dataset.active;
    }
    if (nextSlideIndex >= slides.length) {
      slides[0].dataset.active = true;
      return delete activeSlide.dataset.active;
    }
    slides[nextSlideIndex].dataset.active = true;
    return delete activeSlide.dataset.active;
  });
});
.carouselContainer {
    width: 1000px;
    height: 500px;
    position: relative;

    display: flex;
    justify-content: center;

    border-style: dashed;
    border-color: #010043;
}

.carouselContainer>ul {
    padding: 0;
    margin: 0;
    list-style: none;
}

.slide {
    display: flex;
    width: 400px;
    height: 500px;
    justify-content: center;
    align-items: center;
    

    inset: 0;
    opacity: 0;
    transition-property: opacity;
    transition-duration: 200ms;
    transition-timing-function: ease-in-out;
    transition-delay: 200ms;

    border-style: dashed;
    border-color: var(--magenta6);
    

}

.slide[data-active] {
    opacity: 1;
    z-index: 1;
    transition-delay: 0ms;
}

.slideContent {
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-direction: column;

    width: 300px;
    height: 425px;

 
    border-style: dashed;
    border-color: #010043;
}

.slideContent .slideImg {
    margin: 0;
    width: 200px;
    height: 200px;
    border-radius: 100px;
    z-index: 10px;
}

.slideContent .slideTxt {
    border: magenta;
    border-style: dashed;
    height: 500px;
}

.carousel-button {
    position: absolute;
    background: none;
    border: none;
    outline: none;
    font-size: 4rem;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    color: rgba(255, 255, 255, 0.5);
    cursor: pointer;
    padding: 0 0.5rem;
    border-radius: 0.25rem;
    background-color: rgba(0, 0, 0, 0.1);
    transition: 0.5s;
}

.carousel-button:hover,
.carousel-button:focus {
    background-color: rgba(0, 0, 0, 0.3);
    color: #fff;
}

.carousel-button[data-carousel-btn="prev"] {
    left: 1rem;
}

.carousel-button[data-carousel-btn="next"] {
    right: 1rem;
}
    <section class="row4 animateOnScroll" style="margin: 0 100px 100px 100px;">

        <h2>don't just take it from us!</h2>
        <div class="carouselContainer" data-carousel>

            <button class="carousel-button" data-carousel-btn="prev">
                &#10094;
            </button>
            <button class="carousel-button" data-carousel-btn="next">
                &#10095;
            </button>

            <div class="carouselSlides">
                <ul data-carousel-slides>
                    <li class="slide" data-carousel-slide data-active>
                        <div class="slideContent">
                            <div>
                                <img class="slideImg" src="./assets/imgPlaceholder.jpg">
                            </div>
                            <div class="slideTxt">
                                <div class="slideDesc">
                                    <p style="color:black;">hello</p>
                                </div>
                            </div>
                        </div>

                    </li>
                    <li class="slide" data-carousel-slide>
                        <div class="slideContent">
                            <div>
                                <img class="slideImg" src="./assets/imgPlaceholder.jpg">
                            </div>
                            <div class="slideTxt">
                                <div class="slideDesc">
                                    <p style="color:black;">hello</p>
                                </div>
                            </div>
                        </div>

                    </li>
                    <li class="slide" data-carousel-slide>
                        <div class="slideContent">
                            <div>
                                <img class="slideImg" src="./assets/imgPlaceholder.jpg">
                            </div>
                            <div class="slideTxt">
                                <div class="slideDesc">
                                    <p style="color:black;">hello</p>
                                </div>
                            </div>
                        </div>

                </ul>
            </div>



        </div>


    </section>

Upvotes: 0

Views: 527

Answers (2)

zer00ne
zer00ne

Reputation: 43910

Basically there should be 2 containers:

  1. The element that is the parent of the <button>s and the "frame" that holds each slide. In the example it is article.box.
  2. The element that is the parent of each div.slide in the example is section.frame.

Each container should be position: relative and all children of said containers should be position: absolute. Doing so will:

  1. provide precision positioning of the <button>s and section.frame within the perimeters of article.box
  2. allow all div.slide to hide underneath the visible layers (z-index:0+) with z-index: -1 and be visible with .active class at z-index: 1.

The JavaScript is optional, it's just written better but function should be basically the same.

View in full page mode, the image placeholder service does not have dynamic images.

const data = [
  {img:"https://placem.at/people?random=1", cap:"People 1"},
  {img:"https://placem.at/places?random=1", cap:"Places 2"},
  {img:"https://placem.at/things?random=1", cap:"Things 3"}
];

const slides = genSlides(data);

const box = document.querySelector('.box')
const buttons = box.querySelectorAll("button");

buttons.forEach((button) => {
  button.addEventListener("click", function(event) {
    const offset = button.classList.contains("next") ? 1 : -1;
    const active = box.querySelector(".active");
    const actIdx = slides.indexOf(active);
    const nextIdx = actIdx + offset;
    active.classList.remove("active");
    if (nextIdx < 0) {
      return slides[slides.length + nextIdx].classList.add("active");
    }
    if (nextIdx >= slides.length) {
      return slides[0].classList.add("active");
    }
    return slides[nextIdx].classList.add("active");
  });
});

function genSlides(array) {
  const frame = document.querySelector(".frame");
  array.forEach(slide => {
    frame.insertAdjacentHTML("beforeend", `
      <div class="slide">
        <figure>
          <img src="${slide.img}" width="480">
          <figcaption class="cap">${slide.cap}</figcaption>
        </figure>
      </div>`);
  });
  const slides = Array.from(frame.querySelectorAll(".slide"));
  slides[0].classList.add("active");
  return slides;
}
.box {
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  width: 96vw;
  min-height: 96vh;
  border: 3px dashed #000;
}

.frame {
  display: flex;
  flex-flow: column nowrap;
  justify-content: center;
  align-items: center;
  /* The element that is the parent of all .slide should be relative so the 
     slides, which are absolute positioned, can sit within .frame's perimeter */
  position: relative;
}

.slide {
  display: flex;
  width: 100%;
  height: 100%;
  justify-content: center;
  align-items: center;
  /* All slides should be out of normal flow */
  position: absolute;
  /* All slides should be in the layer "under" the visible layer (z-index: 0+) */
  z-index: -1; 
  opacity: 0;
  animation: opacity 0.7s ease-in;
}

.active {
  opacity: 1;
  z-index: 1;
}

figure {
  display: flex;
  flex-flow: column nowrap;
  justify-content: center;
  align-items: center;
  max-height: 100%;
  margin: 0;
  padding: 0;
  border-style: 1px dashed #000;
}

img {
  object-fit: contain;
}

.cap {
  min-width: 100%;
  text-align: center;
  white-space: pre;
}

button {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 50%;
  z-index: 2;
  padding: 0 0.5rem;
  border: none;
  border-radius: 0.25rem;
  outline: none;
  font-size: 4rem;
  color: rgba(255, 255, 255, 0.5);
  background: none;
  background-color: rgba(0, 0, 0, 0.1);
  transform: translateY(-50%);
  transition: 0.5s;
  cursor: pointer;
}

button:hover,
button:focus {
  color: #fff;
  background-color: rgba(0, 0, 0, 0.3);
}

button:active {
  color: rgba(0, 0, 0, 0.5);
  background: none;
}

.prev {
  left: 1rem;
}

.next {
  right: 1rem;
}
<main>
  <h2>Content Title</h2>
  <article class="box">
    <button class="prev">&#10094;</button>
    <button class="next">&#10095;</button>
    <section class="frame"></section>
  </article>
</main>

Upvotes: 0

mikrec
mikrec

Reputation: 169

part of your problem is your ul element is unstyled, its child li elements are going to stack as they normally do on top of each other. giving the ul element display: flex; will put your li's side by side.

If I were you, I would review each nested element of my tree and figure out its purpose, then remove it if not necessary. for example, div.carouselSlides does not seem like its serving any purpose that the ul could not do itself, at least in this small example.

Also, looking at an established project for implementation ideas (or just using it) might be a good idea . https://swiperjs.com/ is very established with powerful config options

Upvotes: 2

Related Questions