I tried to build a carousel with only Vanilla JavaScript but I encounter some issues along the way. The carousel should display 5 items on the page from a total of 10, and when the "next" button is clicked should slide 3 elements. The problem is that will continue to translateX after the last element in the slider was reached.
const carousel = document.querySelector("[data-target='carousel']");
const card = carousel.querySelector("[data-target='card']");
const leftButton = document.querySelector("[data-action='slideLeft']");
const rightButton = document.querySelector("[data-action='slideRight']");
const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card);
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);
const cardCount = carousel.querySelectorAll("[data-target='card']").length;
let offset = 0;
const maxX = -((cardCount / 3) * carouselWidth + cardMarginRight * (cardCount / 3) - carouselWidth - cardMarginRight);
// click events
leftButton.addEventListener("click", function () {
if (offset !== 0) {
offset += carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
});
rightButton.addEventListener("click", function () {
if (offset !== maxX) {
offset -= carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
});
.wrapper {
height: 200px;
width: 100%;
position: relative;
overflow: hidden;
margin: 0 auto;
}
.button-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
}
.carousel {
margin: 0;
padding: 0;
list-style: none;
width: 100%;
display: flex;
position: absolute;
left: 0;
transition: all 1s ease;
}
.card {
background: black;
min-width: 344px;
height: 200px;
margin-right: 24px;
display: inline-block;
color: white;
font-size: 45px;
display: grid;
place-items: center;
}
<div class="wrapper">
<ul class="carousel" data-target="carousel">
<li class="card" data-target="card">1</li>
<li class="card" data-target="card">2</li>
<li class="card" data-target="card">3</li>
<li class="card" data-target="card">4</li>
<li class="card" data-target="card">5</li>
<li class="card" data-target="card">6</li>
<li class="card" data-target="card">7</li>
<li class="card" data-target="card">8</li>
<li class="card" data-target="card">9</li>
<li class="card" data-target="card">10</li>
</ul>
</div>
<div class="button-wrapper">
<button data-action="slideLeft">left</button>
<button data-action="slideRight">right</button>
</div>
The problem is in this line, you should not set it to equal because if that's not equal it continues sliding
if (offset !== maxX)
const carousel = document.querySelector("[data-target='carousel']");
const card = carousel.querySelector("[data-target='card']");
const leftButton = document.querySelector("[data-action='slideLeft']");
const rightButton = document.querySelector("[data-action='slideRight']");
const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card);
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);
const cardCount = carousel.querySelectorAll("[data-target='card']").length;
let offset = 0;
const maxX = -((cardCount / 3) * carouselWidth + cardMarginRight * (cardCount / 3) - carouselWidth - cardMarginRight);
// click events
leftButton.addEventListener("click", function () {
if (offset !== 0) {
offset += carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
});
rightButton.addEventListener("click", function () {
if (offset+500 > maxX) {
offset -= carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
});
.wrapper {
height: 200px;
width: 100%;
position: relative;
overflow: hidden;
margin: 0 auto;
}
.button-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
}
.carousel {
margin: 0;
padding: 0;
list-style: none;
width: 100%;
display: flex;
position: absolute;
left: 0;
transition: all 1s ease;
}
.card {
background: black;
min-width: 344px;
height: 200px;
margin-right: 24px;
display: inline-block;
color: white;
font-size: 45px;
display: grid;
place-items: center;
}
<div class="wrapper">
<ul class="carousel" data-target="carousel">
<li class="card" data-target="card">1</li>
<li class="card" data-target="card">2</li>
<li class="card" data-target="card">3</li>
<li class="card" data-target="card">4</li>
<li class="card" data-target="card">5</li>
<li class="card" data-target="card">6</li>
<li class="card" data-target="card">7</li>
<li class="card" data-target="card">8</li>
<li class="card" data-target="card">9</li>
<li class="card" data-target="card">10</li>
</ul>
</div>
<div class="button-wrapper">
<button data-action="slideLeft">left</button>
<button data-action="slideRight">right</button>
</div>
I think the problem here is the more or less fixed width of each card which leads to different results on different screen sizes.
min-width: 344px;
Ive changed to (just for example)
min-width: 19%; <!-- you need 5 cards on screen -->
and in your JavaScript something like this:
// get card size
const cardWidth = card.offsetWidth;
//and later on e.g for button to the right do offset 3 cards
offset -= (cardWidth + cardMarginRight) * 3;
Related
I created a slideshow with 3 slides but for some reason, it keeps adding an additional slide
const slideshow = document.getElementById("slideshow");
const slides = slideshow.children;
let currentSlide = 0;
function goToSlide(n) {
slides[currentSlide].classList.remove("active");
currentSlide = (n + slides.length) % slides.length;
slides[currentSlide].classList.add("active");
updateSlideshowCounter();
}
function nextSlide() {
goToSlide(currentSlide + 1);
}
function prevSlide() {
goToSlide(currentSlide - 1);
}
function updateSlideshowCounter() {
const slideshowCounter = document.getElementById("slideshow-counter");
slideshowCounter.textContent = `${currentSlide + 1} / ${slides.length}`;
}
const prevButton = document.getElementById("prev-button");
prevButton.addEventListener("click", prevSlide);
const nextButton = document.getElementById("next-button");
nextButton.addEventListener("click", nextSlide);
updateSlideshowCounter();
#slideshow {
position: relative;
text-align: center;
width: 400px;
height: 300px;
border: 1px black solid;
}
.slide {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 1s;
}
.slide.active {
opacity: 1;
}
#slideshow-controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
}
#prev-button,
#next-button {
padding: 10px 20px;
border: none;
background-color: #333;
color: #fff;
cursor: pointer;
}
#prev-button {
margin-right: 20px;
}
#next-button {
margin-left: 20px;
}
#slideshow-counter {
margin: 0 20px;
}
<div id="slideshow">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
<div id="slideshow-controls">
<button id="prev-button">Prev</button>
<span id="slideshow-counter"></span>
<button id="next-button">Next</button>
</div>
</div>
Can someone tell me what my mistake is and how I can get 3 slides in the output instead of 4.
You're defining your slides with the statement const slides = slideshow.children;. Your slideshow has a total of 4 direct children, so the counter is technically correct (see slide 1, slide 2, slide 3, and slideshow-controls).
One approach to get just the slides you want is to use const slides = document.getElementsByClassName("slide"). I hope this helps!
The problem is your slides variable is not assigned to the correct list of elements, as the previous answer said, you should replace slideshow.children with either document.getElementsByClassName('slide') or document.querySelectorAll('.slide'), use any of the two.
By using slideshow.children, you're not getting .slide classes, you're getting all children of #slideshow.
So, your variable in line 67, should be as the following:
const slides = document.querySelectorAll('.slide');
or
const slides = document.getElementsByClassName('.slide');
You should keep slideshow controls out of your slideshow div. I am attaching Code Below. Run it and check.
const slideshow = document.getElementById("slideshow");
const slides = slideshow.children;
let currentSlide = 0;
function goToSlide(n) {
slides[currentSlide].classList.remove("active");
currentSlide = (n + slides.length) % slides.length;
slides[currentSlide].classList.add("active");
updateSlideshowCounter();
}
function nextSlide() {
goToSlide(currentSlide + 1);
}
function prevSlide() {
goToSlide(currentSlide - 1);
}
function updateSlideshowCounter() {
const slideshowCounter = document.getElementById("slideshow-counter");
slideshowCounter.textContent = `${currentSlide + 1} / ${slides.length}`;
}
const prevButton = document.getElementById("prev-button");
prevButton.addEventListener("click", prevSlide);
const nextButton = document.getElementById("next-button");
nextButton.addEventListener("click", nextSlide);
updateSlideshowCounter();
#slideshowbox {
position: relative;
width: 400px;
height: 300px;
}
#slideshow {
position: relative;
text-align: center;
width: 400px;
height: 300px;
border: 1px black solid;
}
.slide {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: opacity 1s;
}
.slide.active {
opacity: 1;
}
#slideshow-controls {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
}
#prev-button,
#next-button {
padding: 10px 20px;
border: none;
background-color: #333;
color: #fff;
cursor: pointer;
}
#prev-button {
margin-right: 20px;
}
#next-button {
margin-left: 20px;
}
#slideshow-counter {
margin: 0 20px;
}
<div id="slideshowbox">
<div id="slideshow">
<div class="slide">Slide 1</div>
<div class="slide">Slide 2</div>
<div class="slide">Slide 3</div>
</div>
<div id="slideshow-controls">
<button id="prev-button">Prev</button>
<span id="slideshow-counter"></span>
<button id="next-button">Next</button>
</div>
</div>
Your slideshow div childs is throwing 4 because your 4th div is slideshow-controls. You may want to add -1 to the counter or redifine the way you make your div. Best of luck!
So I am working on this project and I made a slider that slides 3 pics at a time but when it comes to the last 3 pics it stops (because of the condition) but I want to make it into infinite loop and I want it to autoslide every 3 seconds. Does anybody know how I can rewrite this part of code to make this work?
const carousel = document.querySelector(".carouselSlides");
const card = carousel.querySelector(".card");
const leftButton = document.querySelector(".slideLeft");
const rightButton = document.querySelector(".slideRight");
const carouselWidth = carousel.offsetWidth;
const cardStyle = card.currentStyle || window.getComputedStyle(card);
const cardMarginRight = Number(cardStyle.marginRight.match(/\d+/g)[0]);
const cardCount = carousel.querySelectorAll(".card").length;
let offset = 0;
const maxX = -(
(cardCount / 3) * carouselWidth +
cardMarginRight * (cardCount / 3) -
carouselWidth -
cardMarginRight
);
leftButton.addEventListener("click", function() {
if (offset !== 0) {
offset += carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
});
rightButton.addEventListener("click", function() {
if (offset !== maxX) {
offset -= carouselWidth + cardMarginRight;
carousel.style.transform = `translateX(${offset}px)`;
}
});
.carouselContainer {
display: flex;
align-items: center;
justify-content: space-around;
position: relative;
overflow: hidden;
height: 58vh;
width: 92vw;
margin: 0 auto;
}
.carouselSlides {
display: flex;
margin: 0;
padding: 0;
list-style: none;
width: 100%;
position: absolute;
left: 0;
transition: all 1s ease;
}
.button-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.slideLeft {
position: relative;
color: #f1f1f1;
background-color: transparent;
border: none;
font-size: 4rem;
right: 26rem;
}
.slideRight {
position: relative;
color: #f1f1f1;
background-color: transparent;
border: none;
font-size: 4rem;
left: 26rem;
}
<div class="carouselContainer">
<div class="carouselSlides">
<div id="slide0" class="card">
<div class="card__thumbnail">
<span class="card__title">FADE</span>
<button class="morebtn" onclick="">READ MORE</button>
</div>
</div>
</div>
<div class="button-wrapper">
<button class="slideLeft">‹</button>
<button class="slideRight">›</button>
</div>
</div>
As the container slides to the left, I want each tab to be centered for a brief moment in the container.
Before the animation begins, I want the very first tab to be centered in the container on page load before the animation begins. I know I need to divide each element's offsetWidth by 2 so that it is calculated to be centered but I'm not sure where/how to do that.
Link to JS Fiddle: https://jsfiddle.net/nb5a92x3/
window.onload = function() {
const tabsContainer = document.querySelector('.tabsContainer');
const tabs = tabsContainer.querySelectorAll('.tab');
function animate(target) {
let siblings = [];
let elm = target;
let keyframes = [];
let offset;
// Push the widths of each previous element to an array
while (elm = elm.previousElementSibling) {
siblings.push(elm.offsetWidth);
}
// Add the sum of all previous elements
offset = siblings.reduce((prev, current) => prev + current, 0);
// Animate the container to the left
setTimeout(function() {
tabsContainer.style.left = `calc(50% - ${offset}px)`;
}, i * 800);
}
// Run the code
for (i = 0; i < tabs.length; i++) {
animate(tabs[i]);
}
}
body {
background: #ccc;
}
section {
text-align: center;
width: 375px;
background: white;
margin: 0 auto;
padding: 16px;
position: relative;
display: flex;
overflow: hidden;
}
.tabsContainer {
display: flex;
position: relative;
margin: 0;
padding: 0;
list-style: none;
gap: 0 16px;
transition: left 0.2s ease;
.tab {
border-radius: 25px;
background: gray;
text-align: center;
height: 34px;
display: inline-flex;
align-items: center;
padding: 0 16px;
}
}
<section>
<ul class="tabsContainer">
<li class="tab">TAB ONE</li>
<li class="tab">ANOTHER TAB HERE</li>
<li class="tab">A REALLY LONG TAB HERE</li>
<li class="tab">LAST TAB</li>
</ul>
</section>
If you set in CSS your container to margin-left: 50%
than all you need is x = EL_curr.offsetLeft + EL_curr.offsetWidth / 2
and apply that x value to the slider-container CSS translateX (which is far more performant than animating the CSS left property, since transform can be hardware accelerated).
// Utility functions:
const EL = (sel, par) => (par || document).querySelector(sel);
const ELS = (sel, par) => (par || document).querySelectorAll(sel);
// Tabs animator:
const tabsAnimator = (EL_slider) => {
const ELS_items = ELS(".tab", EL_slider);
const tot = ELS_items.length;
let curr = 0;
let tOut;
const anim = () => {
const EL_curr = ELS_items[curr];
const x = EL_curr.offsetLeft + EL_curr.offsetWidth / 2;
EL_slider.style.transform = `translateX(-${x}px)`;
curr += 1; // Increment counter
if (curr > tot - 1) curr = 0; // Reset counter
tOut = setTimeout(anim, 1500);
};
anim(); // Init!
};
ELS(".tabsContainer").forEach(tabsAnimator);
* {
margin: 0;
box-sizing: border-box;
}
.tabsWrapper {
width: 375px;
margin: 20px auto;
padding: 16px 0;
position: relative;
overflow: hidden;
background: #ccc;
border-radius: 3em;
}
.tabsContainer {
position: relative;
padding: 0;
display: inline-flex;
list-style: none;
gap: 0 15px;
transition: transform 0.3s;
margin-left: 50%;
}
.tabsContainer .tab {
border-radius: 25px;
background: gold;
white-space: nowrap;
padding: 5px 15px;
}
<section class="tabsWrapper">
<ul class="tabsContainer">
<li class="tab">5 OFF FRIDAY</li>
<li class="tab">10 OFFSUMMER</li>
<li class="tab">SAVE MORE</li>
<li class="tab">HALF OFF EVERYTHING</li>
</ul>
</section>
<section class="tabsWrapper">
<ul class="tabsContainer">
<li class="tab">JavaScript</li>
<li class="tab">HTML</li>
<li class="tab">CSS</li>
</ul>
</section>
don't use horizontal paddings on the wrapper or container slider.
don't just use section as a selector. It's too common. Use rather a specific class like .tabsWrapper on the wrapper parent.
Additionally, if you'd like to pause the animation on "mouseenter", use clearTimeout(tOut); — and just anim() in the "mouseleave" Event.
I've been looking at this Codepen, and trying to find a vanilla JS way to do this (my company doesn't use jQuery).
So far I've made the line the correct width when you click on a menu item, but I can't figure out how to make it stretch like in the Codepen. I added a custom attribute of index to keep track of numbers, and also applied a class to easily target the element. I wasn't sure if there's also a way to just have one. Feel free to change what I've already made.
EDIT: I updated the code below to make it work going left, but not right. Also it only works if the links are next to each other. Anyone?
My codepen: https://codepen.io/ahoward-mm/pen/jOmgxQJ?editors=0010 (desktop only).
var navList = document.querySelector(".navigation__list");
var navItems = navList.getElementsByClassName("navigation__item");
var navLine = document.querySelector(".navigation__line");
for (var i = 0; i < navItems.length; i++) {
navItems[i].classList.add(`navigation__item--${i + 1}`);
navItems[i].setAttribute("index", `${i + 1}`);
var prevItem = 0;
var currentItem = 1;
navItems[i].addEventListener("click", function() {
var current = document.getElementsByClassName("active");
if (current.length > 0) {
current[0].className = current[0].className.replace(" active", "");
}
this.className += " active";
prevItem = currentItem;
currentItem = this.getAttribute("index");
navLine.style.width = `${
document
.querySelector(`.navigation__item--${currentItem}`)
.querySelector(".navigation__link")
.getBoundingClientRect().width +
document
.querySelector(`.navigation__item--${prevItem}`)
.getBoundingClientRect().width
}px`;
navLine.style.left = `${
this.querySelector(".navigation__link").offsetLeft
}px`;
setTimeout(function() {
navLine.style.width = `${
document
.querySelector(`.navigation__item--${currentItem}`)
.querySelector(".navigation__link")
.getBoundingClientRect().width
}px`;
}, 700);
});
}
body {
color: #444;
display: flex;
min-height: 100vh;
flex-direction: column;
}
.navigation {
display: block;
position: sticky;
top: -0.5px;
background-color: #edece8;
margin: 60px 0 0;
text-align: center;
}
.navigation__list {
list-style: none;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 0;
position: relative;
}
.navigation__link {
color: inherit;
line-height: inherit;
word-wrap: break-word;
text-decoration: none;
background-color: transparent;
display: block;
text-transform: uppercase;
letter-spacing: 1.5px;
font-size: 0.875rem;
font-weight: bold;
margin: 15px (20px * 2) 0 (20px * 2);
position: relative;
}
.navigation__line {
height: 2px;
position: absolute;
bottom: 0;
margin: 10px 0 0 0;
background: red;
transition: all 1s;
}
.navigation__item {
list-style: none;
display: flex;
}
<nav class="navigation">
<ul class="navigation__list">
<li class="navigation__item">
<a class="navigation__link" href="#">Lorem ipsum</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Dolor</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Consectetur adipiscing</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Donec ut</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Placerat dignissim</a>
</li>
<div class="navigation__line"></div>
</ul>
</nav>
Here you go.
Idk what your JavaScript code did, so I just decided to convert the jQuery code from codepen to vanilla JavaScript from scratch. And it works.
(It was a good 2-2.5 hrs exercise.)
So, what I did is converted jQuery to its Vanilla JavaScript Equivalent.
For Ex: $() => document.querySelector(), elem.addClass() => elem.classList.add(), elem.find() => elem.querySelectorAll(), elem.css({prop: val}) => elem.style.prop = val; etc.
var nav = document.querySelector(".navigation");
var navLine = nav.querySelector(".navigation__line");
var pos = 0;
var wid = 0;
function setUnderline() {
var active = nav.querySelectorAll(".active");
if (active.length) {
pos = active[0].getBoundingClientRect().left;
wid = active[0].getBoundingClientRect().width;
navLine.style.left = active[0].offsetLeft + "px";
navLine.style.width = wid + "px";
}
}
setUnderline()
window.onresize = function() {
setUnderline()
};
nav.querySelectorAll("ul li a").forEach((elem) => {
elem.onclick = function(e) {
e.preventDefault();
if (!this.parentElement.classList.contains("active") &&
!nav.classList.contains("animate")
) {
nav.classList.add("animate");
var _this = this;
nav
.querySelectorAll("ul li")
.forEach((e) => e.classList.remove("active"));
try {
var position = _this.parentElement.getBoundingClientRect();
var width = position.width;
if (position.x >= pos) {
navLine.style.width = position.x - pos + width + "px";
setTimeout(() => {
navLine.style.left = _this.parentElement.offsetLeft + "px";
navLine.style.width = width + "px";
navLine.style.transitionDuration = "150ms";
setTimeout(() => nav.classList.remove("animate"), 150);
_this.parentElement.classList.add("active");
}, 300);
} else {
navLine.style.width = pos - position.left + wid + "px";
navLine.style.left = _this.parentElement.offsetLeft + "px";
setTimeout(() => {
navLine.style.width = width + "px";
navLine.style.transitionDuration = "150ms";
setTimeout(() => nav.classList.remove("animate"), 150);
_this.parentElement.classList.add("active");
}, 300);
}
} catch (e) {}
pos = position.left;
wid = width;
}
}
});
body {
color: #444;
display: flex;
min-height: 100vh;
flex-direction: column;
}
.navigation {
display: block;
position: sticky;
top: -0.5px;
background-color: #edece8;
margin: 60px 0 0;
text-align: center;
}
ul li:not(:last-child) {
margin-right: 30px;
}
li.active {
opacity: 1;
}
.navigation__list {
list-style: none;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 0;
position: relative;
}
.navigation__link {
color: inherit;
line-height: inherit;
word-wrap: break-word;
text-decoration: none;
background-color: transparent;
display: block;
text-transform: uppercase;
letter-spacing: 1.5px;
font-size: 0.875rem;
font-weight: bold;
margin: 15px (20px * 2) 0 (20px * 2);
position: relative;
}
.navigation__line {
height: 2px;
position: absolute;
bottom: 0;
margin: 10px 0 0 0;
background: red;
transition: all 0.3s;
left: 0;
}
.navigation__item {
list-style: none;
display: flex;
}
<link rel="stylesheet" href="style.css">
<body>
<nav class="navigation">
<ul class="navigation__list">
<li class="navigation__item active">
<a class="navigation__link" href="#">Lorem ipsum</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Dolor</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Consectetur adipiscing</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Donec ut</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Placerat dignissim</a>
</li>
<div class="navigation__line"></div>
</ul>
</nav>
<script src="./script.js"></script>
</body>
So, your code works fine for when you're switching to a link that's before the current one. The problem is that when you change to a link that's ahead, you need to adjust the left property only after you adjust the width.
I made multiple changes in the code for clarity and correctness. Like using classList to add/remove classes and dataset for custom HTML attributes.
However, the only functional change is to check if the previous item is ahead of the current one, and if it is, only apply the left adjustment later, after the width adjustment.
Edit: I just realized that there was second issue with the code. The second aspect that wasn't working as desired was when moving multiple links ahead or behind, the line wouldn't expand long enough. I updated the snippet to account for that by calculating the distance from the link that's earlier to the link that's later, plus the width of the later link.
var navList = document.querySelector(".navigation__list");
var navItems = navList.querySelectorAll(".navigation__item");
var navLine = document.querySelector(".navigation__line");
var prevItem = 0;
var currentItem = 1;
navItems.forEach((navItem, i) => {
navItem.classList.add(`navigation__item--${i + 1}`);
navItem.dataset.index = i + 1;
navItem.addEventListener("click", function () {
var current = document.querySelector(".active");
if (current) {
current.classList.remove("active");
}
this.classList.add("active");
prevItem = currentItem;
currentItem = this.dataset.index;
var movingAhead = currentItem > prevItem;
var aheadElem = movingAhead ? document.querySelector(`.navigation__item--${currentItem} .navigation__link`) : document.querySelector(`.navigation__item--${prevItem} .navigation__link`);
var behindElem = movingAhead ? document.querySelector(`.navigation__item--${prevItem} .navigation__link`) : document.querySelector(`.navigation__item--${currentItem} .navigation__link`);
navLine.style.width = `${(aheadElem.offsetLeft - behindElem.offsetLeft) + aheadElem.getBoundingClientRect().width
}px`;
if (!movingAhead) {
navLine.style.left = `${this.querySelector(".navigation__link").offsetLeft}px`;
}
setTimeout(function () {
var currentLink = document.querySelector(`.navigation__item--${currentItem} .navigation__link`);
navLine.style.width = `${
currentLink.getBoundingClientRect().width
}px`;
if (movingAhead) {
navLine.style.left = `${currentLink.offsetLeft}px`;
}
}, 700);
});
})
body {
color: #444;
display: flex;
min-height: 100vh;
flex-direction: column;
}
.navigation {
display: block;
position: sticky;
top: -0.5px;
background-color: #edece8;
margin: 60px 0 0;
text-align: center;
}
.navigation__list {
list-style: none;
margin: 0;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 0;
position: relative;
}
.navigation__link {
color: inherit;
line-height: inherit;
word-wrap: break-word;
text-decoration: none;
background-color: transparent;
display: block;
text-transform: uppercase;
letter-spacing: 1.5px;
font-size: 0.875rem;
font-weight: bold;
margin: 15px (20px * 2) 0 (20px * 2);
position: relative;
}
.navigation__line {
height: 2px;
position: absolute;
bottom: 0;
margin: 10px 0 0 0;
background: red;
// width: calc(100% - 80px);
transition: all 0.8s;
}
.navigation__item {
list-style: none;
display: flex;
}
<nav class="navigation">
<ul class="navigation__list">
<li class="navigation__item">
<a class="navigation__link" href="#">Lorem ipsum</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Dolor</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Consectetur adipiscing</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Donec ut</a>
</li>
<li class="navigation__item">
<a class="navigation__link" href="#">Placerat dignissim</a>
</li>
<div class="navigation__line"></div>
</ul>
</nav>
I'm creating a slider in Vanilla JavaScript.
I have a basic foundation.
Right now I'm not sure how to hide all of the other slides, and display only the current one. Then on the next/prev, show show the next slide, and hide the one that was before.
I have tried few things, but neither did work.
Here is the codepen: https://codepen.io/Aurelian/pen/LBGWpd?editors=0010
// Problem: Make the slides slide one after another
// and make the buttons work prev and next to bring prev or next slide
// Solution
// Select all slides
// On next/prev, get the left/right animate from 0 to 100 or from 0 to -100%
// On button press, get the prev/next parent node
// Add active class on the shown
// Solution Part 2
// Select all slides
// Get the length of the slides
// Display none on every slideItem
// Get the current slide
// Display block/flex on current slide
// On next, advance the slider index by 1
// On prev, advance the slider index by -1
// Make the slider slide every 3sec with setInterval
// Even listener on slider mousehover, stop the autoslide and add "PAUSED" HTML - add
// On live the slder, unpause and remove the HTML "PAUSED"
document.addEventListener('DOMContentLoaded', function() {
var slider = document.querySelector(".slider"),
sliderList = document.querySelector(".slider__list"),
sliderItems = document.querySelectorAll(".slider__item"),
sliderBtnPrev = document.querySelector(".slider__buttons--prev"),
sliderBtnNext = document.querySelector(".slider__buttons--next"),
sliderItemsLength = sliderItems.length,
currentIndex = 0,
isPaused = false;
function prevSlide() {
sliderItems[currentIndex].classList.remove('is-active');
currentIndex = (currentIndex + sliderItemsLength - 1) % sliderItemsLength;
sliderItems[currentIndex].classList.add('is-active');
}
function nextSlide() {
sliderItems[currentIndex].classList.remove('is-active');
currentIndex = (currentIndex + 1) % sliderItemsLength;
sliderItems[currentIndex].classList.add('is-active');
}
function advance() {
isPaused = false;
if ((isPaused = false) = 1) {
setInterval(function() {
nextSlide();
}, 3000);
}
}
sliderBtnPrev.addEventListener('click', function() {
prevSlide();
});
sliderBtnNext.addEventListener('click', function() {
nextSlide();
});
slider.addEventListener('mouseover', function() {
isPaused = true;
});
slider.addEventListener('mouseout', function() {
isPaused = false;
});
advance();
// On press next, change slide
// sliderItems.forEach(function(sliderItem) {
// sliderItem.style.display = "none";
// });
// function prevSlide() {
// console.log('prev slide');
// }
// function nextSlide() {
// console.log('next slide');
// // if (currentIndex) {
// // console.log(sliderItems[currentIndex])
// // }
// // sliderItemsLength.push[1];
// // currentIndex + 1;
// // console.log(currentIndex < (sliderItemsLength + 1));
// // console.log(sliderItems[currentIndex + 1])
// // if (sliderItemsLength < -1) {
// // sliderItemsLength++;
// // }
// // if (currentIndex < (sliderItemsLength + 1)) {
// // sliderItems[currentIndex].classList.add('is-active');
// // }
// // if (numbers.length > 3) {
// // numbers.length = 3;
// // }
// // myArray[myArray.length - 1] + 1
// if (currentIndex < (sliderItemsLength + 1)) {
// sliderItems[currentIndex].classList.add('is-active');
// currentIndex++;
// } else {
// sliderItems.style.display = "none";
// }
// }
});
* {
box-sizing: border-box;
}
ul,
ol {
margin: 0;
padding: 0;
list-style: none;
}
.slider {
position: relative;
height: 350px;
width: 100%;
}
.slider__list {
height: 100%;
overflow: hidden;
position: relative;
}
.slider__item {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
display: none;
height: 100%;
width: 100%;
position: absolute;
}
.slider__item.is-active {
display: flex;
}
.slider__item:first-child {
background-color: orange;
}
.slider__item:nth-child(2) {
background-color: gold;
}
.slider__item:last-child {
background-color: green;
}
.slider__title {
color: white;
font-weight: bold;
font-size: 1.5em;
}
.slider__buttons {
position: absolute;
right: 0;
left: 0;
top: 50%;
}
.slider__buttons--prev {
height: 40px;
width: 40px;
background-color: red;
cursor: pointer;
position: absolute;
bottom: 0;
margin: auto;
top: 0;
left: 30px;
}
.slider__buttons--next {
height: 40px;
width: 40px;
background-color: red;
cursor: pointer;
position: absolute;
bottom: 0;
top: 0;
margin: auto;
right: 30px;
}
<div class="container">
<div class="slider">
<ul class="slider__list">
<li class="slider__item is-active">
<span class="slider__title">1</span>
</li>
<li class="slider__item">
<span class="slider__title">2</span>
</li>
<li class="slider__item">
<span class="slider__title">3</span>
</li>
</ul>
<div class="slider__buttons">
<span class="slider__buttons--prev"></span>
<span class="slider__buttons--next"></span>
</div>
<!-- <ul class="slider__nav">
<li class="slider__nav-item"></li>
<li class="slider__nav-item"></li>
<li class="slider__nav-item"></li>
</ul> -->
</div>
</div>
You need to hide all slides by default display: none then define a class is-active to change the display to flex
Start counting from -1
The formula to calculate the next slide is :
(currentIndex + 1) % sliderItemsLength
The formula to calculate the prev slide is :
(currentIndex + sliderItemsLength - 1) % sliderItemsLength
document.addEventListener('DOMContentLoaded', function() {
var sliderList = document.querySelector(".slider__list"),
sliderItems = document.querySelectorAll(".slider__item"),
sliderBtnPrev = document.querySelector(".slider__buttons--prev"),
sliderBtnNext = document.querySelector(".slider__buttons--next"),
sliderItemsLength = sliderItems.length,
currentIndex = -1;
function prevSlide() {
sliderItems[currentIndex].classList.remove('is-active');
currentIndex = (currentIndex + sliderItemsLength - 1) % sliderItemsLength;
sliderItems[currentIndex].classList.add('is-active');
}
function nextSlide() {
if (currentIndex > 0) sliderItems[currentIndex].classList.remove('is-active');
else sliderItems[0].classList.remove('is-active');
currentIndex = (currentIndex + 1) % sliderItemsLength;
sliderItems[currentIndex].classList.add('is-active');
}
sliderBtnPrev.addEventListener('click', function() {
prevSlide();
});
sliderBtnNext.addEventListener('click', function() {
nextSlide();
});
nextSlide();
});
* {
box-sizing: border-box;
}
ul,
ol {
margin: 0;
padding: 0;
list-style: none;
}
.slider {
position: relative;
height: 350px;
width: 100%;
}
.slider__list {
height: 100%;
overflow: hidden;
position: relative;
}
.slider__item {
display: none;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
height: 100%;
width: 100%;
position: absolute;
}
.slider__item.is-active {
display: flex;
}
.slider__item:first-child {
background-color: orange;
}
.slider__item:nth-child(2) {
background-color: red;
}
.slider__item:last-child {
background-color: green;
}
.slider__title {
color: white;
font-weight: bold;
font-size: 1.5em;
}
.slider__buttons {
position: absolute;
right: 0;
left: 0;
top: 50%;
}
.slider__buttons--prev {
height: 40px;
width: 40px;
background-color: red;
cursor: pointer;
position: absolute;
bottom: 0;
margin: auto;
top: 0;
left: 30px;
}
.slider__buttons--next {
height: 40px;
width: 40px;
background-color: red;
cursor: pointer;
position: absolute;
bottom: 0;
top: 0;
margin: auto;
right: 30px;
}
<div class="container">
<div class="slider">
<ul class="slider__list">
<li class="slider__item">
<span class="slider__title">1</span>
</li>
<li class="slider__item">
<span class="slider__title">2</span>
</li>
<li class="slider__item">
<span class="slider__title">3</span>
</li>
</ul>
<div class="slider__buttons">
<span class="slider__buttons--prev"></span>
<span class="slider__buttons--next"></span>
</div>
<!-- <ul class="slider__nav">
<li class="slider__nav-item"></li>
<li class="slider__nav-item"></li>
<li class="slider__nav-item"></li>
</ul> -->
</div>
</div>