Skip to content
Advertisement

How to Fade In and Insert Element then Fade Out and Remove Element?

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>
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement