Reputation: 1
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">
❮
</button>
<button class="carousel-button" data-carousel-btn="next">
❯
</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
Reputation: 43910
Basically there should be 2 containers:
<button>
s and the "frame" that holds each slide. In the example it is article.box
.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:
<button>
s and section.frame
within the perimeters of article.box
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">❮</button>
<button class="next">❯</button>
<section class="frame"></section>
</article>
</main>
Upvotes: 0
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