Javascript - how to trigger a "if" condition by a change through CSS - javascript

I'm being tormented in the past 4 hours to find out how to do this, I don't know what I'm doing wrong, I have a page with multiple layers, I wish to trigger some transition when the needed page has opacity 1, it should be simple when u think of it, here is my code, please help ;)
slide1 = document.querySelector('.slide1');
function videoPlay() {
var videoOne = document.getElementById('myVideo');
if ((slide1.style.opacity) > 0 ) {
videoOne.play();
}
}
videoPlay();
.slide {
width: 100%;
background-size: cover;
background-position: center;
position: absolute;
}
.slide1 {
width: 100%;
background: none;
opacity: 0;
}
<div class="slide slide1">
<div class="slide-content">
<div class="secondColumn">
<video muted id="myVideo">
<source src="Media/Acqua.mp4" type="video/mp4">
</video>
<div class="lowerTab"></div>
</div>
</div>
here is the code which i use to change the opacity using the wheel :
//wheel event
document.addEventListener('wheel',
function scrollWheel(event) {
var fig =event.deltaY;
if (fig > 0) {
slideMove();
}
else if (fig<0) {
slideMovReverse();
}
})
//basic movement
function slideMove() {
if (current === sliderImages.length-1 ) {
current = -1
}
reset();
sliderImages[current+1].style.transition = "opacity 1s ease-in 0s";
sliderImages[current+1].style.opacity= "1.0";
current++;
}

You can use the transitionend event, but you'd have to set up the transition first. As it sits now, there's not much information in your question about the different slides, how the transitions are set up, etc. Here's a baseline to give you an idea:
const slide1 = document.querySelector('.slide1');
const videoEl = document.querySelector('.slide1__video');
const button = document.querySelector('button');
let inView = false;
slide1.addEventListener('transitionend', () => {
let content = 'Playing';
if (inView) {
content = ''
}
videoEl.textContent = content;
inView = !inView;
})
button.addEventListener('click', () => {
slide1.classList.toggle('active')
})
.slide1 {
transition: opacity 500ms linear;
opacity: 0;
border: 1px solid green;
padding: 10px;
margin-bottom: 24px
}
.slide1.active {
opacity: 1
}
<div class="slide1">
Slide 1
<div class="slide1__video"></div>
</div>
<button>Next</button>
Edit
It'll need some love but I think it's in the right direction to what you're after.
const slides = Array.from(document.querySelectorAll('.slide'));
document.addEventListener('wheel', onScroll);
const SCROLL_TOLERANCE = 100;
let currentIndex = 0;
let currentScroll = 0;
function onScroll(e) {
if (e.deltaY > 0) {
currentScroll += 1;
} else {
currentScroll -= 1;
}
if (currentScroll >= (currentIndex * SCROLL_TOLERANCE) + 15) {
showNext();
} else if (currentScroll <= (currentIndex * SCROLL_TOLERANCE) - 15) {
showPrevious();
}
}
function showNext() {
if (currentIndex === slides.length - 1) {
return console.warn('At the end.');
}
currentIndex += 1;
setSlide();
}
function showPrevious() {
if (currentIndex === 0) {
return console.warn('At the beginning.');
}
currentIndex -= 1;
setSlide();
}
function setSlide() {
let newOpacity = 0;
slides.forEach(slide => {
if (+slide.dataset.index === currentIndex) {
newOpacity = 1
} else {
newOpacity = 0;
}
slide.style.opacity = newOpacity;
slide.addEventListener('transitionend', () => {
console.log('Done transitioning!');
// Do things here when the transition is over.
})
});
}
html,
body {
padding: 0;
margin: 0;
font-family: sans-serif;
font-size: 18px
}
.slide {
border: 3px solid #efefef;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
transition: all 500ms linear;
opacity: 0;
transition-delay: 250ms;
}
.slide.active {
opacity: 1;
}
<div class="slide active" data-index="0">
Slide 1
</div>
<div class="slide" data-index="1">
Slide 2
</div>
<div class="slide" data-index="2">
Slide 3
</div>
<div class="slide" data-index="3">
Slide 4
</div>

Related

Video Touch Slider with HTML, CSS & JavaScript

I recently was playing around with the code showcase in this tutorial: Touch Slider CodePen
I would like to use something like this but with videos instead of images.
I swapped the HTML, JS, and CSS code to work with the tag. With this, the code does work and you are able to scroll over one video initially just like the images had worked. After this, it seems the js 'isDragging' or some event in te JS seems to freeze and I am unable to slide to another video or image.
Would anyone be able to play around with the JS shown in this CodePen and get a working slider with videos?
Thanks!
slides = Array.from(document.querySelectorAll('.slide'))
let isDragging = false,
startPos = 0,
currentTranslate = 0,
prevTranslate = 0,
animationID = 0,
currentIndex = 0
slides.forEach((slide, index) => {
const slideImage = slide.querySelector('video')
slideImage.addEventListener('dragstart', (e) => e.preventDefault())
// Touch events
slide.addEventListener('touchstart', touchStart(index))
slide.addEventListener('touchend', touchEnd)
slide.addEventListener('touchmove', touchMove)
// Mouse events
slide.addEventListener('mousedown', touchStart(index))
slide.addEventListener('mouseup', touchEnd)
slide.addEventListener('mouseleave', touchEnd)
slide.addEventListener('mousemove', touchMove)
})
// Disable context menu
window.oncontextmenu = function (event) {
event.preventDefault()
event.stopPropagation()
return false
}
function touchStart(index) {
return function (event) {
currentIndex = index
startPos = getPositionX(event)
isDragging = true
// https://css-tricks.com/using-requestanimationframe/
animationID = requestAnimationFrame(animation)
slider.classList.add('grabbing')
}
}
function touchEnd() {
isDragging = false
cancelAnimationFrame(animationID)
const movedBy = currentTranslate - prevTranslate
if (movedBy < -100 && currentIndex < slides.length - 1) currentIndex += 1
if (movedBy > 100 && currentIndex > 0) currentIndex -= 1
setPositionByIndex()
slider.classList.remove('grabbing')
}
function touchMove(event) {
if (isDragging) {
const currentPosition = getPositionX(event)
currentTranslate = prevTranslate + currentPosition - startPos
}
}
function getPositionX(event) {
return event.type.includes('mouse') ? event.pageX : event.touches[0].clientX
}
function animation() {
setSliderPosition()
if (isDragging) requestAnimationFrame(animation)
}
function setSliderPosition() {
slider.style.transform = `translateX(${currentTranslate}px)`
}
function setPositionByIndex() {
currentTranslate = currentIndex * -window.innerWidth
prevTranslate = currentTranslate
setSliderPosition()
} ```
Replace the <img/> tag with <video/> and replace the img reference in JS
HTML:
<div class="slider-container">
<div class="slide">
<h2>Airpods</h2>
<h4>$199</h4>
<video width="320" height="240" controls>
<source src="https://player.vimeo.com/external/367564948.sd.mp4?s=d969af3ae466e775628a8d281105fd03a8df12ae&profile_id=165&oauth2_token_id=57447761"/>
<video />
Buy Now
</div>
<div class="slide">
<h2>iPhone 12</h2>
<h4>$799</h4>
<video width="320" height="240" controls>
<source src="https://player.vimeo.com/external/334344435.sd.mp4?s=d367341a941ffa97781ade70e4f4a28f4a1a5fc8&profile_id=165&oauth2_token_id=57447761"/>
Buy Now
</div>
<div class="slide">
<h2>iPad</h2>
<h4>$599</h4>
<video width="320" height="240" controls>
<source src="https://player.vimeo.com/external/369639344.sd.mp4?s=b892fce959245aa4ae7ab08bc4b1af2766acdf4e&profile_id=165&oauth2_token_id=57447761"/>
Buy Now
</div>
</div>
CSS:
#import url('https://fonts.googleapis.com/css2?family=Open+Sans&display=swap');
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
font-family: 'Open Sans', sans-serif;
height: 100%;
width: 100%;
overflow: hidden;
background-color: #333;
color: #fff;
line-height: 1.7;
}
.slider-container {
height: 100vh;
display: inline-flex;
overflow: hidden;
transform: translateX(0);
transition: transform 0.3s ease-out;
cursor: grab;
}
.slide {
max-height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1rem;
user-select: none;
}
.slide img {
max-width: 100%;
max-height: 60%;
transition: transform 0.3s ease-in-out;
}
.slide h2 {
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
.slide h4 {
font-size: 1.3rem;
}
.btn {
background-color: #444;
color: #fff;
text-decoration: none;
padding: 1rem 1.5rem;
}
.grabbing {
cursor: grabbing;
}
.grabbing .slide img {
transform: scale(0.9);
}
JS:
/*
This JS code is from the following project:
https://github.com/bushblade/Full-Screen-Touch-Slider
*/
const slider = document.querySelector('.slider-container'),
slides = Array.from(document.querySelectorAll('.slide'))
let isDragging = false,
startPos = 0,
currentTranslate = 0,
prevTranslate = 0,
animationID = 0,
currentIndex = 0
slides.forEach((slide, index) => {
const slideImage = slide.querySelector('video')
slideImage.addEventListener('dragstart', (e) => e.preventDefault())
// Touch events
slide.addEventListener('touchstart', touchStart(index))
slide.addEventListener('touchend', touchEnd)
slide.addEventListener('touchmove', touchMove)
// Mouse events
slide.addEventListener('mousedown', touchStart(index))
slide.addEventListener('mouseup', touchEnd)
slide.addEventListener('mouseleave', touchEnd)
slide.addEventListener('mousemove', touchMove)
})
// Disable context menu
window.oncontextmenu = function (event) {
event.preventDefault()
event.stopPropagation()
return false
}
function touchStart(index) {
return function (event) {
currentIndex = index
startPos = getPositionX(event)
isDragging = true
// https://css-tricks.com/using-requestanimationframe/
animationID = requestAnimationFrame(animation)
slider.classList.add('grabbing')
}
}
function touchEnd() {
isDragging = false
cancelAnimationFrame(animationID)
const movedBy = currentTranslate - prevTranslate
if (movedBy < -100 && currentIndex < slides.length - 1) currentIndex += 1
if (movedBy > 100 && currentIndex > 0) currentIndex -= 1
setPositionByIndex()
slider.classList.remove('grabbing')
}
function touchMove(event) {
if (isDragging) {
const currentPosition = getPositionX(event)
currentTranslate = prevTranslate + currentPosition - startPos
}
}
function getPositionX(event) {
return event.type.includes('mouse') ? event.pageX : event.touches[0].clientX
}
function animation() {
setSliderPosition()
if (isDragging) requestAnimationFrame(animation)
}
function setSliderPosition() {
slider.style.transform = `translateX(${currentTranslate}px)`
}
function setPositionByIndex() {
currentTranslate = currentIndex * -window.innerWidth
prevTranslate = currentTranslate
setSliderPosition()
}

How to translateX to a position without removing transition property?

I want to translateX to a position if change is more than -150, but due to having transition property in container it shows the animation of travelling to the new translate value. I want it to have directly jump to the -400px translateX value without showing the animation to going to it and still have the transition property in place for future scrolls
const config = {
individualItem: '.slide', // class of individual item
carouselWidth: 400, // in px
carouselId: '#slideshow', // carousel selector
carouselHolderId: '#slide-wrapper', // carousel should be <div id="carouselId"><div id="carouselHolderId">{items}</div></div>
}
document.addEventListener("DOMContentLoaded", function(e) {
// Get items
const el = document.querySelector(config.individualItem);
const elWidth = parseFloat(window.getComputedStyle(el).width) + parseFloat(window.getComputedStyle(el).marginLeft) + parseFloat(window.getComputedStyle(el).marginRight);
// Track carousel
let mousedown = false;
let movement = false;
let initialPosition = 0;
let selectedItem;
let currentDelta = 0;
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.style.width = `${config.carouselWidth}px`;
});
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.addEventListener('pointerdown', function(e) {
mousedown = true;
selectedItem = item;
initialPosition = e.pageX;
currentDelta = parseFloat(item.querySelector(config.carouselHolderId).style.transform.split('translateX(')[1]) || 0;
});
});
const scrollCarousel = function(change, currentDelta, selectedItem) {
let numberThatFit = Math.floor(config.carouselWidth / elWidth);
let newDelta = currentDelta + change;
let elLength = selectedItem.querySelectorAll(config.individualItem).length - numberThatFit;
if(newDelta <= 0 && newDelta >= -elWidth * elLength) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(${newDelta}px)`;
// IMPORTANT LINE
if(newDelta <= 0 && newDelta <= -150) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(-1000px)`;
}
}
}
document.body.addEventListener('pointermove', function(e) {
if(mousedown == true && typeof selectedItem !== "undefined") {
let change = -(initialPosition - e.pageX);
scrollCarousel(change, currentDelta, document.body);
movement = true;
}
});
['pointerup', 'mouseleave'].forEach(function(item) {
document.body.addEventListener(item, function(e) {
selectedItem = undefined;
movement = false;
});
});
});
.slide-wrapper {
transition: 400ms ease;
transform: translateX(0px);
width: 400px;
height: 400px;
}
.slide-number {
pointer-events: none;
}
<!DOCTYPE html>
<html>
<head>
<title>HTML and CSS Slideshow</title>
<style>
body {
font-family: Helvetica, sans-serif;
padding: 5%;
text-align: center;
font-size: 50;
overflow-x: hidden;
}
/* Styling the area of the slides */
#slideshow {
overflow: hidden;
height: 400px;
width: 400px;
margin: 0 auto;
}
/* Style each of the sides
with a fixed width and height */
.slide {
float: left;
height: 400px;
width: 400px;
}
/* Add animation to the slides */
.slide-wrapper {
/* Calculate the total width on the
basis of number of slides */
width: calc(728px * 4);
/* Specify the animation with the
duration and speed */
/* animation: slide 10s ease infinite; */
}
/* Set the background color
of each of the slides */
.slide:nth-child(1) {
background: green;
}
.slide:nth-child(2) {
background: pink;
}
.slide:nth-child(3) {
background: red;
}
.slide:nth-child(4) {
background: yellow;
}
/* Define the animation
for the slideshow */
#keyframes slide {
/* Calculate the margin-left for
each of the slides */
20% {
margin-left: 0px;
}
40% {
margin-left: calc(-728px * 1);
}
60% {
margin-left: calc(-728px * 2);
}
80% {
margin-left: calc(-728px * 3);
}
}
</style>
</head>
<body>
<!-- Define the slideshow container -->
<div id="slideshow">
<div id="slide-wrapper" class="slide-wrapper">
<!-- Define each of the slides
and write the content -->
<div class="slide">
<h1 class="slide-number">
1
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
2
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
3
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
4
</h1>
</div>
</div>
</div>
</body>
</html>
If I understood your question properly, I think you would have to remove the transition property before changing the value and then apply it again once the transition is done.
const item = selectedItem.querySelector(config.carouselHolderId)
item.style.cssText = `transform: translateX(${newDelta}px); transition: none`;
// Restore the transition
item.style.transition = '';
You could temporarily disable the transition:
const config = {
individualItem: '.slide', // class of individual item
carouselWidth: 400, // in px
carouselId: '#slideshow', // carousel selector
carouselHolderId: '#slide-wrapper', // carousel should be <div id="carouselId"><div id="carouselHolderId">{items}</div></div>
}
document.addEventListener("DOMContentLoaded", function(e) {
// Get items
const el = document.querySelector(config.individualItem);
const elWidth = parseFloat(window.getComputedStyle(el).width) + parseFloat(window.getComputedStyle(el).marginLeft) + parseFloat(window.getComputedStyle(el).marginRight);
// Track carousel
let mousedown = false;
let movement = false;
let initialPosition = 0;
let selectedItem;
let currentDelta = 0;
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.style.width = `${config.carouselWidth}px`;
});
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.addEventListener('pointerdown', function(e) {
mousedown = true;
selectedItem = item;
initialPosition = e.pageX;
currentDelta = parseFloat(item.querySelector(config.carouselHolderId).style.transform.split('translateX(')[1]) || 0;
});
});
const scrollCarousel = function(change, currentDelta, selectedItem) {
let numberThatFit = Math.floor(config.carouselWidth / elWidth);
let newDelta = currentDelta + change;
let elLength = selectedItem.querySelectorAll(config.individualItem).length - numberThatFit;
if(newDelta <= 0 && newDelta >= -elWidth * elLength) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(${newDelta}px)`;
// IMPORTANT LINE
if(newDelta <= 0 && newDelta <= -150) {
const el = selectedItem.querySelector(config.carouselHolderId);
el.classList.add("jump");
el.style.transform = `translateX(-1000px)`;
setTimeout(() => el.classList.remove("jump"), 10);
}
}
}
document.body.addEventListener('pointermove', function(e) {
if(mousedown == true && typeof selectedItem !== "undefined") {
let change = -(initialPosition - e.pageX);
scrollCarousel(change, currentDelta, document.body);
movement = true;
}
});
['pointerup', 'mouseleave'].forEach(function(item) {
document.body.addEventListener(item, function(e) {
selectedItem = undefined;
movement = false;
});
});
});
.slide-wrapper {
transform: translateX(0px);
width: 400px;
height: 400px;
transition: 400ms ease;
user-select: none;
}
.slide-number {
pointer-events: none;
}
.slide-wrapper.jump
{
transition-duration: 10ms;
}
<!DOCTYPE html>
<html>
<head>
<title>HTML and CSS Slideshow</title>
<style>
body {
font-family: Helvetica, sans-serif;
padding: 5%;
text-align: center;
font-size: 50;
overflow-x: hidden;
}
/* Styling the area of the slides */
#slideshow {
overflow: hidden;
height: 400px;
width: 400px;
margin: 0 auto;
}
/* Style each of the sides
with a fixed width and height */
.slide {
float: left;
height: 400px;
width: 400px;
}
/* Add animation to the slides */
.slide-wrapper {
/* Calculate the total width on the
basis of number of slides */
width: calc(728px * 4);
/* Specify the animation with the
duration and speed */
/* animation: slide 10s ease infinite; */
}
/* Set the background color
of each of the slides */
.slide:nth-child(1) {
background: green;
}
.slide:nth-child(2) {
background: pink;
}
.slide:nth-child(3) {
background: red;
}
.slide:nth-child(4) {
background: yellow;
}
/* Define the animation
for the slideshow */
#keyframes slide {
/* Calculate the margin-left for
each of the slides */
20% {
margin-left: 0px;
}
40% {
margin-left: calc(-728px * 1);
}
60% {
margin-left: calc(-728px * 2);
}
80% {
margin-left: calc(-728px * 3);
}
}
</style>
</head>
<body>
<!-- Define the slideshow container -->
<div id="slideshow">
<div id="slide-wrapper" class="slide-wrapper">
<!-- Define each of the slides
and write the content -->
<div class="slide">
<h1 class="slide-number">
1
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
2
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
3
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
4
</h1>
</div>
</div>
</div>
</body>
</html>

JavaScript and CSS Carousel/Slideshow with z-index

I need to make a carousel/slideshow in plain JavaScript mixed with CSS that slides through the images one by one loop seamlessly.
I can't seem to get any code working. I've tried several approaches but can't. It's got to be with z-index, and not making use of flex.
This is my third attempt at coding this. Can't seem to get the logic. It has to have navigation buttons to switch between images. Can someone help me out?
const getHeader = document.querySelector('.wp-custom-header');
const getImages = document.querySelectorAll('.wp-custom-header img');
const computeImages = function () {
getImages.forEach((img, index) => {
if (index > 0) img.classList.add('out');
});
};
computeImages();
let counter = 0;
let reinit = false;
// getHeader.classList.add('transformSlide');
const slideShowTimer = setInterval(() => {
if (counter > 0 && counter < getImages.length - 1) {
getImages[counter + 1].classList.add('transform-slide');
getImages[counter + 1].classList.add('onqueue-current');
getImages[counter + 1].classList.remove('out');
} else if (counter === 0 && reinit === false) {
getImages[counter + 1].classList.add('transform-slide');
getImages[counter + 1].classList.add('onqueue-current');
getImages[counter + 1].classList.remove('out');
} else if (counter === 0 && reinit === true) {
getImages[counter].classList.add('transform-slide');
getImages[counter].classList.add('onqueue-current');
getImages[counter].classList.remove('out');
getImages[getImages.length - 1].classList.add('out');
getImages[getImages.length - 1].classList.remove('transform-slide');
getImages[getImages.length - 1].classList.remove('onqueue-current');
}
counter++;
}, 2000);
getHeader.addEventListener('transitionend', () => {
if (counter >= 1) {
if (!reinit) {
getImages[counter - 1].classList.remove('transform-slide');
getImages[counter - 1].classList.remove('onqueue-current');
getImages[counter - 1].classList.add('out');
} else {
getImages[counter].classList.remove('transform-slide');
getImages[counter].classList.remove('onqueue-current');
getImages[counter].classList.add('out');
}
}
if (counter >= getImages.length - 1) {
console.log(counter);
counter = 0;
reinit = true;
}
});
This is the HTML
<div id="wp-custom-header" class="wp-custom-header">
<img alt="" src="./image01.svg" />
<img alt="" src="./image02.svg" />
<img alt="" src="./image03.svg" />
<img alt="" src="./image04.svg" />
<img alt="" src="./image05.svg" />
<img alt="" src="./image06.svg" />
</div>
The CSS
.wp-custom-header {
position: relative;
display: block;
width: 100%;
height: var(--header-size);
}
.wp-custom-header img {
position: absolute;
display: block;
min-width: 100%;
-o-object-fit: cover;
object-fit: cover;
transition: var(--slide-transform) ease-in-out;
}
.wp-custom-header img.out {
/* left: -450px; */
z-index: 0;
transform: translateX(100%);
}
.wp-custom-header img.onqueue-next {
z-index: 0;
left: 0px;
}
.wp-custom-header img.onqueue-current {
z-index: 1;
transform: translateX(0px);
}
.transform-slide {
transition: var(--slide-transform) ease-in-out;
}
I took the liberty to tinker with your code. I've reworked your code into smaller functions and added a looping mechanic. Now you have buttons that will loop infinitely no matter how many slides there are in your carousel.
I've added a previous and a next button. Hovering over the images will stop the autoslide functionality from running so that you can control going to the next and previous slide. Whenever you stop hovering the carousel continues.
Hope that this is what you were looking for.
const header = document.querySelector('.wp-custom-header');
const images = document.querySelectorAll('.wp-custom-header img');
const buttons = document.querySelectorAll('.wp-custom-header button');
let activeSlideIndex = 0;
let interval = null;
const updateCarousel = () => {
images.forEach((image, index) => {
if (index === activeSlideIndex) {
image.classList.add('active');
} else if (image.classList.contains('active')) {
image.classList.remove('active');
}
});
};
const nextSlide = () => {
if (activeSlideIndex + 1 < images.length) {
activeSlideIndex++;
} else {
activeSlideIndex = 0;
}
};
const prevSlide = () => {
if (activeSlideIndex - 1 >= 0) {
activeSlideIndex--;
} else {
activeSlideIndex = images.length - 1;
}
};
const startInterval = () => setInterval(() => {
nextSlide();
updateCarousel();
}, 2000);
const stopInterval = () => {
clearInterval(interval);
interval = null;
};
interval = startInterval();
const controls = {
'prev': prevSlide,
'next': nextSlide
};
header.addEventListener('mouseenter', () => {
if (interval !== null) {
stopInterval();
}
});
header.addEventListener('mouseleave', () => {
interval = startInterval();
});
buttons.forEach(button => {
button.addEventListener('click', event => {
const value = event.target.value;
const action = controls[value];
action();
updateCarousel();
});
});
.wp-custom-header {
position: relative;
}
.wp-custom-header-images {
display: block;
width: 100%;
height: 250px;
}
.wp-custom-header-images img {
position: absolute;
display: block;
width: 100%;
height: 100%;
-o-object-fit: cover;
object-fit: cover;
opacity: 0;
will-change: opacity;
transition: opacity 250ms ease-in-out;
z-index: 0;
}
.wp-custom-header-images img.active {
z-index: 1;
opacity: 1;
}
.wp-custom-header-button {
position: absolute;
top: 50%;
border: 1px solid #d0d0d0;
background-color: #f0f0f0;
border-radius: 50%;
width: 50px;
height: 50px;
cursor: pointer;
transform: translate(0, -50%);
z-index: 2;
}
.wp-custom-header-button[value="prev"] {
left: 15px;
}
.wp-custom-header-button[value="next"] {
right: 15px;
}
<div id="wp-custom-header" class="wp-custom-header">
<button class="wp-custom-header-button" value="prev">Prev</button>
<div class="wp-custom-header-images">
<img alt="" src="https://picsum.photos/seed/a/640/360" class="active" />
<img alt="" src="https://picsum.photos/seed/b/640/360" />
<img alt="" src="https://picsum.photos/seed/c/640/360" />
<img alt="" src="https://picsum.photos/seed/d/640/360" />
<img alt="" src="https://picsum.photos/seed/e/640/360" />
<img alt="" src="https://picsum.photos/seed/f/640/360" />
</div>
<button class="wp-custom-header-button" value="next">Next</button>
</div>

Javascript: Carousel slides out of the loop

I am currently building a carousel with vanilla JS and I've noticed that, unlike previous carousels I built before with jQuery, this one slides an extra slide out of the loop (which I defined with the .length property). The problem is happening in both directions.
Why is this happening and what can I do to fix it? Here's a quick example:
let currentSlide = 0
const holder = document.querySelector('.holder')
const totalSlides = holder.querySelectorAll('.holder-child')
const next = document.querySelector('.next')
const prev = document.querySelector('.prev')
const moveSlide = slide => {
const leftPosition = -slide * 100 + 'vw'
holder.style.left = leftPosition
}
const nextSlide = () => {
currentSlide = currentSlide + 1
moveSlide(currentSlide)
if (currentSlide > totalSlides.length - 1) {
currentSlide = 0
}
}
const prevSlide = () => {
currentSlide = currentSlide - 1
moveSlide(currentSlide)
if (currentSlide < 0) {
currentSlide = totalSlides.length - 1
}
}
next.addEventListener('click', () => {
nextSlide()
})
prev.addEventListener('click', () => {
prevSlide()
})
.flex {
display: flex;
}
.slideshow {
position: relative;
overflow: hidden;
width: 100vw;
}
.holder {
position: relative;
top: 0;
left: 0;
width: 500vw;
transition: left 1s;
}
.holder-child {
display: flex;
width: 100vw;
}
.speaker {
padding: 0 8vw 0 8vw;
}
.controls {
justify-content: space-between;
}
<h2>Test</h2>
<div class="slideshow">
<div class="controls flex">
<p class="prev pointer">prev</p>
<p class="next pointer">next</p>
</div>
<div class=" holder flex">
<div class="holder-child">
<p>slide 1</p>
</div>
<div class="holder-child">
<p>slide 2</p>
</div>
</div>
JSFiddle: https://jsfiddle.net/5f8cbxdr/
It looks like a minor logic issue caused by moveSlide() being called prior to the final calculation of currentSlide during your prevSlide() and nextSlide() methods.
Consider moving the call of moveSlide() to the end of your prevSlide() and nextSlide() functions. This will mean that when the edge cases occur (ie your currentSlide is at either end of the range of slides), the subsequent call to moveSlide() will cause the slides to be positioned base on the re-adjusted value of currentSlide:
let currentSlide = 0
const holder = document.querySelector('.holder')
const totalSlides = holder.querySelectorAll('.holder-child')
const next = document.querySelector('.next')
const prev = document.querySelector('.prev')
const moveSlide = slide => {
const leftPosition = -slide * 100 + 'vw'
holder.style.left = leftPosition
}
const nextSlide = () => {
currentSlide = currentSlide + 1
if (currentSlide > totalSlides.length - 1) {
currentSlide = 0
}
/* Final value for currentSlide now determined, so call moveSlide()
to position slides consistently with most up to date currentSlide
value */
moveSlide(currentSlide)
}
const prevSlide = () => {
currentSlide = currentSlide - 1
if (currentSlide < 0) {
currentSlide = totalSlides.length - 1
}
/* Final value for currentSlide now determined, so call moveSlide()
to position slides consistently with most up to date currentSlide
value */
moveSlide(currentSlide)
}
next.addEventListener('click', () => {
nextSlide()
})
prev.addEventListener('click', () => {
prevSlide()
})
.flex {
display: flex;
}
.slideshow {
position: relative;
overflow: hidden;
width: 100vw;
}
.holder {
position: relative;
top: 0;
left: 0;
width: 500vw;
transition: left 1s;
}
.holder-child {
display: flex;
width: 100vw;
}
.speaker {
padding: 0 8vw 0 8vw;
}
.controls {
justify-content: space-between;
}
<h2>Test</h2>
<div class="slideshow">
<div class="controls flex">
<p class="prev pointer">prev</p>
<p class="next pointer">next</p>
</div>
<div class=" holder flex">
<div class="holder-child">
<p>slide 1</p>
</div>
<div class="holder-child">
<p>slide 2</p>
</div>
</div>

fade in or out depending on active class, fade in works, out does not

This is probably best viewed in the fiddle here.
This is a simple fade in/out slideshow for a portfolio. The displayed slide has a class "active". It should fade out before the next slide fades in. Instead it disappears instantly. The fade in of the next slide is working fine.
This is the basic html code.
var x = document.getElementById("inner-portfolio-wrapper").childElementCount;
var j = 1;
var k;
function clickMe() {
if (x > 1) {
if (j === x) {
j = 1;
k = x;
} else {
k = j;
j++;
}
console.log("j = " + j);
console.log("k = " + k);
document.getElementById("portfolio-item-" + k).style.display = "none";
document.getElementById("portfolio-item-" + j).style.display = "block";
document.getElementById("portfolio-item-" + j).classList.add("active");
document.getElementById("portfolio-item-" + k).classList.remove("active");
}
}
#inner-portfolio-wrapper {
position: relative;
width: 150px;
height: 50px;
}
.portfolio-item {
display: none;
animation: fadeIn 2s;
}
.portfolio-item .active {
display: block;
animation: fadeOut 2s;
}
.portfolio-item:first-child {
display: block;
}
#keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<div id="inner-portfolio-wrapper">
<div id="portfolio-item-1" class="portfolio-item">
<h2>
ITEM 1
</h2>
</div>
<div id="portfolio-item-2" class="portfolio-item">
<h2>
ITEM 2
</h2>
</div>
<div id="portfolio-item-3" class="portfolio-item">
<h2>
ITEM 3
</h2>
</div>
</div>
<button type="button" onclick="clickMe()">
Click Me
</button>
Any help to get the fade out working would be appreciated. Everything else is working just fine.
For now, fadeOut animation doesn't work because click on button immediately removes .active from the item and it gets style display: none.
To get the desired effect the only thing your onClick function has to do - is to trigger fadeOut animation. All next actions have to be called as callback of animationEnd event.
You also need to make some changes in styles:
.portfolio-item {
display: none;
}
.portfolio-item.active {
display: block;
animation: fadeIn 2s;
}
.portfolio-item.active.out {
display: block;
animation: fadeOut 2s;
}
And finally, it works:
//detect the supported event property name and assign it to variable
// Function from David Walsh: http://davidwalsh.name/css-animation-callback
function whichAnimationEvent() {
var t,
el = document.createElement("fakeelement");
var animations = {
"animation": "animationend",
"OAnimation": "oAnimationEnd",
"MozAnimation": "animationend",
"WebkitAnimation": "webkitAnimationEnd"
}
for (t in animations) {
if (el.style[t] !== undefined) {
return animations[t];
}
}
}
var animationEvent = whichAnimationEvent();
//Declare global variables
var total = document.getElementById("inner-portfolio-wrapper").childElementCount;
var currentNum = 1
var nextNum;
//Get all portfolio items add add them an event listener
var items = document.getElementById("inner-portfolio-wrapper").children
for (var i = 0; i < items.length; i++) {
items[i].addEventListener(animationEvent, function(e) {
if (e.animationName === 'fadeOut') {
this.classList.toggle('out')
this.classList.toggle('active');
document.getElementById("portfolio-item-" + nextNum).classList.toggle('active')
currentNum = nextNum
}
})
}
//When page loaded make first porfolio item active
items[0].classList.add("active");
function clickMe() {
if (total > 1) {
var currentElement = document.getElementById("portfolio-item-" + currentNum);
nextNum = (currentNum === total) ? 1 : currentNum + 1
currentElement.classList.toggle('out')
}
}
#inner-portfolio-wrapper {
position: relative;
width: 150px;
height: 50px;
}
.portfolio-item {
display: none;
}
.portfolio-item.active {
display: block;
animation: fadeIn 2s;
}
.portfolio-item.active.out {
display: block;
animation: fadeOut 2s;
}
#keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<div id="inner-portfolio-wrapper">
<div id="portfolio-item-1" class="portfolio-item">
<h2>
ITEM 1
</h2>
</div>
<div id="portfolio-item-2" class="portfolio-item">
<h2>
ITEM 2
</h2>
</div>
<div id="portfolio-item-3" class="portfolio-item">
<h2>
ITEM 3
</h2>
</div>
</div>
<button type="button" onclick="clickMe()">
Click Me
</button>

Categories

Resources