Description
I have a <button>
that, when clicked once, sets the class of a <div>
element to “fade-in” and inserts the <div>
element in the DOM. This visually fades in the <div>
element after insertion into the DOM. When the button is clicked again, the button sets the class of the <div>
element to “fade-out”, waits with setTimeout()
, and removes the <div>
element from the DOM. This visually fades out the <div>
element before removal from the DOM.
Problem
If the <div>
element is in the DOM (after clicking once), and I proceed to click twice in rapid succession, then the <div>
element fades out, then fades in, then gets removed from the DOM.
Expected Result
I would like the <div>
element to, from the first click, fade out and be removed from the DOM, then, from the second click, fade in and be reinserted into the DOM. That is, the <div>
element should both 1) be in the DOM and 2) appear visually after rapidly clicking twice when the button has already been pressed once. My assumption is that, because the second click happens before setTimeout()
ends, the container is “inserted” (it already is inserted, though) into the DOM before the code within setTimeout()
is executed (which removes the <div>
from the DOM).
How do I ensure that the code to reinsert the <div>
element (occurring in the second mouse click event) is executed after the setTimeout()
event (occurring in the first mouse click event)?
Code
Attached below is a minimal reproducible example of my problem.
const container = document.createElement("div"); let toggled = false; const button = document.querySelector("button"); button.addEventListener("click", event => { toggled = !toggled; if (toggled) { container.setAttribute("class", "fade-in"); button.parentElement.insertBefore(container, button.nextSibling); } else { container.setAttribute("class", "fade-out"); setTimeout(() => {container.remove();}, 500) } })
div { width: 100px; height: 100px; background-color: black; } .fade-in { animation: fade-in 500ms linear; } @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } .fade-out { animation: fade-out 500ms linear; } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="../../CSS/Test.css"> <script src="../../JS/Test.js" async></script> <title>Document</title> </head> <body> <button> Fade In / Fade Out </button> </body> </body> </html>
I have tried using Promises, but none of my attempts have worked, unfortunately. Because I am new to Javascript, I am not sure whether I am using it wrong, or whether my approach has flaws.
Thanks in advance!
Advertisement
Answer
I use a variable inAnimation
that is set to true
when an animation is triggered and set to false
at the end of the animation using setTimeout(f, animationDuration)
:
I also set the animation duration to 1000ms
to see the probleme more clearly.
Only need to change :
const container = document.createElement("div"); let toggled = false; let inAnimation = false; // is div currently animated const button = document.querySelector("button"); button.addEventListener("click", event => { if(inAnimation) return; // test if there is no current animation if (!toggled) { inAnimation = true; container.setAttribute("class", "fade-in"); button.parentElement.insertBefore(container, button.nextSibling); setTimeout(() => { inAnimation = false; // end of animation }, 1000) } else if(!inAnimation) { inAnimation = true; container.setAttribute("class", "fade-out"); setTimeout(() => { inAnimation = false; // end of animation container.remove(); }, 1000) } toggled = !toggled; })
div { width: 100px; height: 100px; background-color: black; } .fade-in { animation: fade-in 1000ms linear; } @keyframes fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } .fade-out { animation: fade-out 1000ms linear; } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="../../CSS/Test.css"> <script src="../../JS/Test.js" async></script> <title>Document</title> </head> <body> <button> Fade In / Fade Out </button> </body> </body> </html>