JavaScript step-progress bar only transitioning correctly on first click

yotube
0

Issue



const stepOne = document.querySelector(".step-1");
const stepTwo = document.querySelector(".step-2");
const stepThree = document.querySelector(".step-3");
const stepFour = document.querySelector(".step-4");
const stepFive = document.querySelector(".step-5");
const steps = document.querySelectorAll(".step");
const texts = document.querySelectorAll(".progress-text");
let active = 1;
let previous;

const updateProgress = () => {
texts.forEach((text, i) => {
text.classList.remove("leave-left", "leave-right", "enter-left", "enter-right");
});
// Toggle active attr on each step
steps.forEach((step, i) => {
if (i < active) {
step.classList.add("active");
} else {
step.classList.remove("active");
}
});
// Transition the descriptions
texts.forEach((text, i) => {
if ((i + 1) < active || (i + 1) < active) {
text.classList.remove("active");
} else if ((i + 1) > active) {
text.classList.remove("active");
} else {
text.classList.add("active");
}
if ((i + 1) === previous && previous > active) {
text.classList.add("leave-right");
texts[active - 1].classList.add("enter-left");
} else if ((i + 1) === previous && previous < active) {
text.classList.add("leave-left");
texts[active - 1].classList.add("enter-right");
}
});
};


stepOne.addEventListener("click", () => {
previous = active;
active = 1;
updateProgress();
});

stepTwo.addEventListener("click", () => {
previous = active;
active = 2
updateProgress();
});

stepThree.addEventListener("click", () => {
previous = active;
active = 3
updateProgress();
});

stepFour.addEventListener("click", () => {
previous = active;
active = 4
updateProgress();
});

stepFive.addEventListener("click", () => {
previous = active;
active = 5
updateProgress();
});

.progress-bar-container {
padding: 2em 0;
}

#progress-bar li {
list-style-type: none;
width: 20%;
float: left;
position: relative;
text-align: center;
color: #0c435a;
font-weight: bold;
}

/** Status dots **/
#progress-bar li:before {
width: 1.3em;
height: 1.3em;
content: '';
line-height: 30px;
border: 2px solid #0c435a;
background-color: #0c435a;
display: block;
text-align: center;
margin: 0 auto 1em auto;
border-radius: 50%;
transition: all .8s;
}

#progress-bar li:hover::before {
cursor: pointer;
transition: all .3s;
border: 2px solid #b78e15;
background-color: #b78e15;
}

/** Highlights active status dot **/
#progress-bar li.active:before {
border-color: #b78e15;
background-color: #b78e15;
transition: all;
}

/** Horizontal line **/
#progress-bar li:after {
width: 100%;
height: 3px;
content: '';
position: absolute;
background-color: #0c435a;
top: 10px;
left: -50%;
z-index: -1;
transition: all;
}

/** Removes line before first status dot **/
#progress-bar li:first-child:after {
content: none;
}

/** Highlights line up to active status dot **/
#progress-bar li.active:after {
background-color: #b78e15;
transition: all;
}

/** Display the relevant description**/
.progress-text-wrapper h4 {
color: #0c435a;
padding-bottom: 0.7em;
}

.progress-text-wrapper {
text-align: center;
position: relative;
width: 50%;
margin: 1em auto;
-webkit-transition: height 0.4s;
-moz-transition: height 0.4s;
transition: height 0.4s;
}

.progress-text {
list-style-type: none;
position: absolute;
z-index: 1;
width: 100%;
left: 0;
top: 0;
-webkit-transform: translateX(-100%);
-moz-transform: translateX(-100%);
-ms-transform: translateX(-100%);
-o-transform: translateX(-100%);
transform: translateX(-100%);
padding: 0 5%;
opacity: 0;
-webkit-animation-duration: 0.4s;
-moz-animation-duration: 0.4s;
animation-duration: 0.4s;
-webkit-animation-timing-function: ease-in-out;
-moz-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out;
}

.progress-text.active {
/* visible event content */
position: relative;
z-index: 2;
opacity: 1;
-webkit-transform: translateX(0);
-moz-transform: translateX(0);
-ms-transform: translateX(0);
-o-transform: translateX(0);
transform: translateX(0);
}

.progress-text.enter-right {
-webkit-animation-name: cd-enter-right;
-moz-animation-name: cd-enter-right;
animation-name: cd-enter-right;
}

.progress-text.enter-left {
-webkit-animation-name: cd-enter-left;
-moz-animation-name: cd-enter-left;
animation-name: cd-enter-left;
}


.progress-text.leave-right {
-webkit-animation-name: cd-enter-right;
-moz-animation-name: cd-enter-right;
animation-name: cd-enter-right;
-webkit-animation-direction: reverse;
-moz-animation-direction: reverse;
animation-direction: reverse;
}

.progress-text.leave-left {
-webkit-animation-name: cd-enter-left;
-moz-animation-name: cd-enter-left;
animation-name: cd-enter-left;
-webkit-animation-direction: reverse;
-moz-animation-direction: reverse;
animation-direction: reverse;
}


@-webkit-keyframes cd-enter-right {
0% {
opacity: 0;
-webkit-transform: translateX(100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
}
}

@-moz-keyframes cd-enter-right {
0% {
opacity: 0;
-moz-transform: translateX(100%);
}
100% {
opacity: 1;
-moz-transform: translateX(0%);
}
}

@keyframes cd-enter-right {
0% {
opacity: 0;
-webkit-transform: translateX(100%);
-moz-transform: translateX(100%);
-ms-transform: translateX(100%);
-o-transform: translateX(100%);
transform: translateX(100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
-moz-transform: translateX(0%);
-ms-transform: translateX(0%);
-o-transform: translateX(0%);
transform: translateX(0%);
}
}

@-webkit-keyframes cd-enter-left {
0% {
opacity: 0;
-webkit-transform: translateX(-100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
}
}

@-moz-keyframes cd-enter-left {
0% {
opacity: 0;
-moz-transform: translateX(-100%);
}
100% {
opacity: 1;
-moz-transform: translateX(0%);
}
}

@keyframes cd-enter-left {
0% {
opacity: 0;
-webkit-transform: translateX(-100%);
-moz-transform: translateX(-100%);
-ms-transform: translateX(-100%);
-o-transform: translateX(-100%);
transform: translateX(-100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
-moz-transform: translateX(0%);
-ms-transform: translateX(0%);
-o-transform: translateX(0%);
transform: translateX(0%);
}
}

<div class="text-center">
<h2 class="">Process</h2>
<p class="py-3">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore
et dolore magna aliqua.
</p>
<div class="progress-bar-container">
<div class="row">
<div class="col">
<ul id="progress-bar">
<li class="step-1 step active">One</li>
<li class="step-2 step">Two</li>
<li class="step-3 step">Three</li>
<li class="step-4 step">Four</li>
<li class="step-5 step">Five</li>
</ul>
</div>
</div>
</div>
</div>

<div class="progress-text-wrapper">
<ol>
<li class="progress-text active">
<h4>One</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
</p>
</li>
<li class="progress-text">
<h4>Two</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
</p>
</li>
<li class="progress-text">
<h4>Three</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
</p>
</li>
<li class="progress-text">
<h4>Four</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
</p>
</li>
<li class="progress-text">
<h4>Five</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
</p>
</li>
</ol>
</div>



I have the following progress bar, which has a horizontal line and a description below for each step. enter image description here

The line works correctly, and the description itself changes correctly. However I want the descriptions to 'slide' left or right 'out of' the window as the user selects a different status. The transitioning works correctly, but only the first time a status is clicked. After that, although the description still changes correctly, there is no transition; just an instant change.

What is odd is that if I call manually

texts.forEach((text, i) => {
text.classList.remove("leave-left", "leave-right", "enter-left", "enter-right");
});

after every click, then it works correctly.

Also open to overall better solutions.

Thanks


Solution

The trick with @keyframes animations is they are triggered on class addition.
If the element already has the class... Well, there is no change right?

Then... There is a timing issue.
When removing the animation classes... if you re-add one right away because of a matching condition. It is like it was never removed. A 1ms setTimeout is usefull in that case. So the class removal had time to be redrawn before it is re-added.

Now, removing the animation clas is not all. You have to make sure to add/remove the active class too. It has to be at the same moment you add/remove the animation class.

Also... I improved the way to add the eventListeners and renamed variables with meaningful names, which also removed all the +1 or -1 calculations on the index. Indexes are zero-based.... Use the zero-base. ;)



const steps = Array.from(document.querySelectorAll(".step"));
const texts = Array.from(document.querySelectorAll(".progress-text"));
let currentlyActive = 0;
let nowInactive;

const updateProgress = () => {

// Case where user click on the currently active step...
if (currentlyActive === nowInactive) {
return
}

// Toggle active attr on each step
steps.forEach((step, index) => {
step.classList.toggle("active", index <= currentlyActive);
});

// Transition the descriptions
texts.forEach((text, index) => {

// Remove all animation classes
text.classList.remove("leave-left", "leave-right", "enter-left", "enter-right");

// Animation direction for both element to be animated
if (currentlyActive < nowInactive) {
animateTexts(nowInactive, "leave-right", currentlyActive, "enter-left");
}
if (currentlyActive > nowInactive) {
animateTexts(nowInactive, "leave-left", currentlyActive, "enter-right");
}
});

}

const animateTexts = (
nowInactive,
nowInactiveDir,
currentlyActive,
currentlyActiveDir
) => {

// nowInactive stars leaving
// That needs a tiny delay to make sure any animation class removal has been redrawn
setTimeout(() => {
texts[nowInactive].classList.add(nowInactiveDir);
}, 1)

// After the leave animation is done
setTimeout(() => {

// Remove the classes on the nowInactive
texts[nowInactive].classList.remove("active", nowInactiveDir);

// currentlyActive enters
texts[currentlyActive].classList.add("active", currentlyActiveDir)
}, 400)
}

// Click event listeners
steps.forEach((step, index) => {
step.addEventListener("click", () => {

// Zero-based indexes are setted here
nowInactive = currentlyActive
currentlyActive = index;

updateProgress();
});
});

.progress-bar-container {
padding: 2em 0;
}

#progress-bar li {
list-style-type: none;
width: 20%;
float: left;
position: relative;
text-align: center;
color: #0c435a;
font-weight: bold;
}


/** Status dots **/

#progress-bar li:before {
width: 1.3em;
height: 1.3em;
content: '';
line-height: 30px;
border: 2px solid #0c435a;
background-color: #0c435a;
display: block;
text-align: center;
margin: 0 auto 1em auto;
border-radius: 50%;
transition: all .8s;
}

#progress-bar li:hover::before {
cursor: pointer;
transition: all .3s;
border: 2px solid #b78e15;
background-color: #b78e15;
}


/** Highlights active status dot **/

#progress-bar li.active:before {
border-color: #b78e15;
background-color: #b78e15;
transition: all;
}


/** Horizontal line **/

#progress-bar li:after {
width: 100%;
height: 3px;
content: '';
position: absolute;
background-color: #0c435a;
top: 10px;
left: -50%;
z-index: -1;
transition: all;
}


/** Removes line before first status dot **/

#progress-bar li:first-child:after {
content: none;
}


/** Highlights line up to active status dot **/

#progress-bar li.active:after {
background-color: #b78e15;
transition: all;
}


/** Display the relevant description**/

.progress-text-wrapper h4 {
color: #0c435a;
padding-bottom: 0.7em;
}

.progress-text-wrapper {
text-align: center;
position: relative;
width: 50%;
margin: 1em auto;
-webkit-transition: height 0.4s;
-moz-transition: height 0.4s;
transition: height 0.4s;
}

.progress-text {
list-style-type: none;
position: absolute;
z-index: 1;
width: 100%;
left: 0;
top: 0;
-webkit-transform: translateX(-100%);
-moz-transform: translateX(-100%);
-ms-transform: translateX(-100%);
-o-transform: translateX(-100%);
transform: translateX(-100%);
padding: 0 5%;
opacity: 0;
-webkit-animation-duration: 0.4s;
-moz-animation-duration: 0.4s;
animation-duration: 0.4s;
-webkit-animation-timing-function: ease-in-out;
-moz-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out;
}

.progress-text.active {
/* visible event content */
position: relative;
z-index: 2;
opacity: 1;
-webkit-transform: translateX(0);
-moz-transform: translateX(0);
-ms-transform: translateX(0);
-o-transform: translateX(0);
transform: translateX(0);
}

.progress-text.enter-right {
-webkit-animation-name: cd-enter-right;
-moz-animation-name: cd-enter-right;
animation-name: cd-enter-right;
}

.progress-text.enter-left {
-webkit-animation-name: cd-enter-left;
-moz-animation-name: cd-enter-left;
animation-name: cd-enter-left;
}

.progress-text.leave-right {
-webkit-animation-name: cd-enter-right;
-moz-animation-name: cd-enter-right;
animation-name: cd-enter-right;
-webkit-animation-direction: reverse;
-moz-animation-direction: reverse;
animation-direction: reverse;
}

.progress-text.leave-left {
-webkit-animation-name: cd-enter-left;
-moz-animation-name: cd-enter-left;
animation-name: cd-enter-left;
-webkit-animation-direction: reverse;
-moz-animation-direction: reverse;
animation-direction: reverse;
}

@-webkit-keyframes cd-enter-right {
0% {
opacity: 0;
-webkit-transform: translateX(100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
}
}

@-moz-keyframes cd-enter-right {
0% {
opacity: 0;
-moz-transform: translateX(100%);
}
100% {
opacity: 1;
-moz-transform: translateX(0%);
}
}

@keyframes cd-enter-right {
0% {
opacity: 0;
-webkit-transform: translateX(100%);
-moz-transform: translateX(100%);
-ms-transform: translateX(100%);
-o-transform: translateX(100%);
transform: translateX(100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
-moz-transform: translateX(0%);
-ms-transform: translateX(0%);
-o-transform: translateX(0%);
transform: translateX(0%);
}
}

@-webkit-keyframes cd-enter-left {
0% {
opacity: 0;
-webkit-transform: translateX(-100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
}
}

@-moz-keyframes cd-enter-left {
0% {
opacity: 0;
-moz-transform: translateX(-100%);
}
100% {
opacity: 1;
-moz-transform: translateX(0%);
}
}

@keyframes cd-enter-left {
0% {
opacity: 0;
-webkit-transform: translateX(-100%);
-moz-transform: translateX(-100%);
-ms-transform: translateX(-100%);
-o-transform: translateX(-100%);
transform: translateX(-100%);
}
100% {
opacity: 1;
-webkit-transform: translateX(0%);
-moz-transform: translateX(0%);
-ms-transform: translateX(0%);
-o-transform: translateX(0%);
transform: translateX(0%);
}
}

<div class="text-center">
<h2 class="">Process</h2>
<p class="py-3">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</p>
<div class="progress-bar-container">
<div class="row">
<div class="col">
<ul id="progress-bar">
<li class="step active">One</li>
<li class="step">Two</li>
<li class="step">Three</li>
<li class="step">Four</li>
<li class="step">Five</li>
</ul>
</div>
</div>
</div>
</div>

<div class="progress-text-wrapper">
<ol>
<li class="progress-text active">
<h4>One</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</li>
<li class="progress-text">
<h4>Two</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</li>
<li class="progress-text">
<h4>Three</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</li>
<li class="progress-text">
<h4>Four</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</li>
<li class="progress-text">
<h4>Five</h4>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</p>
</li>
</ol>
</div>





Answered By - Louys Patrice Bessette

Post a Comment

0Comments
Post a Comment (0)

#buttons=(Accept !) #days=(20)

Our website uses cookies to enhance your experience. Learn More
Accept !
To Top