0

I have an issue on chrome in live but not in localhost (vs code live server). I'm making a countdown project with a flip animation. I have used a setInterval to add the class "active" and then after the animation I clone the node and remove the class. It works in firefox and safari in both localhost and live but not in chrome and edge for live; or at least not always.

Here there is my code:

function setEl(type) {
    let flipNode = document.querySelector(`.countdown-${type}`);
    flipNode.card = flipNode.querySelector(".card");
    flipNode.CardFaces = flipNode.querySelectorAll(".card-face");
    flipNode.digit = flipNode.querySelector(".digit");
    flipNode.cardFaceFront = flipNode.querySelector(".card-face-front");
    flipNode.cardFaceBack = flipNode.querySelector(".card-face-back");


    //timer
    let now = new Date().getTime();
    let endDate = new Date("April 16, 2022 00:00:00");
    let diff = endDate.getTime() - now;


    //get seconds, minutes, hours and days in milliseconds
    let s = 1000;
    let m = s * 60;
    let h = m * 60;
    let d = h * 24;

    //Check if the countdown is over
    if (diff < 0) {
        return

    }

    //get seconds, minutes, hours and days
    let days = Math.floor(diff / d);
    let hours = Math.floor((diff % d) / h);
    let minutes = Math.floor((diff % h) / m);
    let seconds = Math.floor((diff % m) / s);


    let curr = null;

    if (type === "seconds") {
        curr = seconds;
        if (diff > 0) {
            flipNode.card.classList.add("active");
        }
    } else if (type === "minutes") {
        curr = minutes;
        if (seconds === 0) {
            if (days !== 0 || hours !== 0) {
                flipNode.card.classList.add("active");
            }
        }
    } else if (type === "hours") {
        curr = hours;
        if (minutes === 0 && seconds === 0) {

            flipNode.card.classList.add("active");
        }
    } else if (type === "days") {
        curr = days;
        if (hours === 0 && minutes === 0 && seconds === 0) {
            flipNode.card.classList.add("active");
        }
    }

    flipNode.digit.dataset.digitBefore = curr < 10 ? "0" + curr : curr;
    flipNode.cardFaceFront.innerText = flipNode.digit.dataset.digitBefore;

    if (curr === 0) {
        if (type === "hours") {
            flipNode.digit.dataset.digitAfter = 23;
        } else {
            flipNode.digit.dataset.digitAfter = 59;
        }
        flipNode.cardFaceBack.innerText = flipNode.digit.dataset.digitAfter;
    } else {
        flipNode.digit.dataset.digitAfter = (curr - 1) < 10 ? "0" + (curr - 1) : curr - 1;
        flipNode.cardFaceBack.innerText = flipNode.digit.dataset.digitAfter;
    }

    flipNode.card.addEventListener("transitionend", function () {

        if (curr === 0) {
            if (type === "hours") {
                flipNode.digit.dataset.digitBefore = 23;
            } else {
                flipNode.digit.dataset.digitBefore = 59;
            }

        } else {
            flipNode.digit.dataset.digitBefore = (curr - 1) < 10 ? "0" + (curr - 1) : curr - 1;
        }

        flipNode.cardFaceFront.innerText = flipNode.digit.dataset.digitBefore;

        //cone the node and replace it to have a new animation
        let newFlipCard = flipNode.card.cloneNode(true);
        newFlipCard.classList.remove("active");
        flipNode.digit.replaceChild(newFlipCard, flipNode.card);
        flipNode.card = newFlipCard;


        //The var need to be reassigned or they will point out the old node and not the new one
        flipNode.cardFaces = flipNode.querySelectorAll(".card-face");
        flipNode.cardFaceFront = flipNode.cardFaces[0];
        flipNode.cardFaceBack = flipNode.cardFaces[1];


        flipNode.digit.dataset.digitAfter = (curr - 1) < 10 ? "0" + (curr - 1) : curr - 1;
        flipNode.cardFaceBack.innerText = flipNode.digit.dataset.digitAfter;

    }, { once: true });
}


function countdown() {
    setEl("seconds");
    setEl("minutes");
    setEl("hours");
    setEl("days");
}

countdown();

setInterval(() => {
    countdown();
}, 1000);
/* font import */
@import url("https://fonts.googleapis.com/css2?family=Red+Hat+Text:wght@700&display=swap");
*,
::before,
::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

ul {
  list-style-type: none;
}

body {
  font-size: 14px;
  font-family: "Red Hat Text", sans-serif;

}

.stars-background {
  width: 100%;
  height: 100%;
  position: absolute;
  overflow: hidden;
}

.app {
  width: 100%;
  height: calc(100vh - 220px);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.header-text {
  position: relative;
  top: -40px;
  width: 90%;
  font-size: 1.4rem;
  letter-spacing: 5px;
  margin-bottom: 50px;
  text-align: center;
  text-transform: uppercase;
  color: white;
}

.countdown-container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  font-size: 40px;
  line-height: 0;
}

@media (min-width: 600px) {
  .countdown-container {
    width: 100%;
  }
}

@media (min-width: 1200px) {
  .countdown-container {
    width: 50%;
  }
}

.countdown-container * {
  user-select: none;
  cursor: default;
  width: 100%;
  display: flex;
  justify-content: space-around;
}

.countdown {
  display: flex;
  flex-wrap: wrap;
  position: relative;
}

.countdown-time-text {
  font-size: 0.5rem;
  color: #8486a9;
  line-height: normal;
  padding-top: 5px;
  text-transform: uppercase;
}

@media (min-width: 600px) {
  .countdown-time-text {
    font-size: 1.2rem;
  }
}

.countdown .digit {
  position: relative;
  width: 70px;
  height: 70px;
  box-shadow: 0 5px 0px 0px black;
  border-radius: 8px 8px 8px 8px;
  perspective: 1000px;
  backface-visibility: hidden;
  color: #fb6087;
  /* remove the persepective on firefox only due to a bug in the browser rendering with perspective and preseve 3d*/
}

@supports (-moz-appearance: none) {
  .countdown .digit {
    perspective: none;
  }
}

@media (min-width: 600px) {
  .countdown .digit {
    width: 140px;
    height: 140px;
  }
}

.digit::before {
  content: attr(data-digit-before);
  bottom: 0;
  align-items: flex-start;
  background-color: #34364f;
  padding-left: 1.2px;
}

@supports (-moz-appearance: none) {
  .digit::before {
    padding-left: 0;
  }
}

.digit::after {
  content: attr(data-digit-after);
  background-color: #2c2c44;
  color: #d3506f;
  top: 0;
  align-items: flex-end;
}

.digit::before,
.digit::after {
  position: absolute;
  z-index: 0;
  display: flex;
  justify-content: center;
  width: 100%;
  height: 50%;
  overflow: hidden;
}

@media (min-width: 600px) {
  .digit::before,
  .digit::after {
    font-size: 4rem;
  }
}

.card {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 50%;
  transform-style: preserve-3d;
  transform-origin: bottom;
  transform: rotateX(0);
  transition: transform 0.7s ease-in-out;
}

.card .bullet {
  width: 100%;
}

.card::before, .card::after {
  content: "";
  position: absolute;
  top: 32.5px;
  z-index: 5;
  width: 2.2px;
  height: 5px;
  background-color: black;
}

@media (min-width: 600px) {
  .card::before, .card::after {
    top: 65px;
    width: 4.4px;
    height: 10px;
  }
}

.card::before {
  left: 0;
  border-radius: 0px 5px 5px 0px;
}

.card::after {
  content: "";
  right: 0;
  border-radius: 5px 0px 0px 5px;
}

.card-face {
  position: absolute;
  display: flex;
  justify-content: center;
  width: 100%;
  height: 100%;
  overflow: hidden;
  backface-visibility: hidden;
  will-change: transform;
  color: #fb6087;
}

@media (min-width: 600px) {
  .card-face {
    font-size: 4rem;
  }
}

.card.active {
  transform: rotateX(-180deg);
  -moz-transform: rotateX(-180deg);
  /*   box-shadow: 10px 0px 10px #000; */
}

.card-face-front {
  align-items: flex-end;
  background-color: white;
  background-color: #2c2c44;
  color: #d3506f;
}

.card-face-back {
  align-items: flex-start;
  transform: rotateX(180deg);
  -moz-transform: rotateX(-180deg);
  background-color: #34364f;
}

.digit::before,
.card-face-back {
  border-radius: 0 0 8px 8px;
}

.digit::after,
.card-face-front {
  border-radius: 8px 8px 0 0;
}

.bottom-img {
  width: 100%;
  position: relative;
  bottom: -20px;
  overflow: hidden;
}

@media (min-width: 1440px) {
  .bottom-img {
    overflow: auto;
    bottom: -30px;
  }
}

.bottom-img img {
  background-color: transparent;
  transform: translateX(-55%);
}

@media (min-width: 600px) {
  .bottom-img img {
    transform: translateX(0);
  }
}

@media (min-width: 1440px) {
  .bottom-img img {
    transform: scaleX(1.8);
    height: 200px;
  }
}
  <div class="app">
        <h1 class="header-text">We're launching soon</h1>
        <div class="countdown-container">
            <div class="countdown  countdown-days">
                <div class="digit" data-digit-before="0" data-digit-after="1">
                    <!--before-->
                    <div class="card">
                        <div class="card-face card-face-front">0</div>
                        <div class="card-face card-face-back">1</div>
                        <div class="bullet bullet-right"></div>
                    </div>
                    <!--after-->
                </div>
                <p class="countdown-time-text">days</p>
            </div>
            <div class="countdown  countdown-hours">
                <div class="digit" data-digit-before="0" data-digit-after="1">
                    <!--before-->
                    <div class="card">
                        <div class="card-face card-face-front">0</div>
                        <div class="card-face card-face-back">1</div>
                    </div>
                    <!--after-->
                </div>
                <p class="countdown-time-text">hours</p>
            </div>
            <div class="countdown  countdown-minutes">
                <div class="digit" data-digit-before="0" data-digit-after="1">
                    <!--before-->
                    <div class="card">
                        <div class="card-face card-face-front">0</div>
                        <div class="card-face card-face-back">1</div>
                    </div>
                    <!--after-->
                </div>
                <p class="countdown-time-text">minutes</p>
            </div>
            <div class="countdown  countdown-seconds">
                <div class="digit s" data-digit-before="0" data-digit-after="1">
                    <!--before-->
                    <div class="card s">
                        <div class="card-face card-face-front">0</div>
                        <div class="card-face card-face-back">1</div>
                    </div>
                    <!--after-->
                </div>
                <p class="countdown-time-text">seconds</p>
            </div>
        </div>
    </div>

You can find my project here: https://github.com/PeteC88/countdown-frontend-mentor-challenge

and the live version here :

https://petec88.github.io/countdown-frontend-mentor-challenge/

Can you please help me?

Thank you so much

  • Hi, check [this](https://stackoverflow.com/questions/41634322/transitionend-is-not-fired-when-initial-state-and-final-state-are-the-same) link. I saw your code and I think it get stucked sometimes because the transition end doesn't get fired in time and you have a "card s active" that prevents the animation to get fired again (until the "active" class is removed from the class list) I suggest to reduce the animation duration and see what happens – Margon Jan 10 '22 at 13:27
  • Hello, thanks so much for your help. I have added a setTimeout for the first time the class active is added for seconds and it seems that it has fixed it in chrome (maybe not in chrome on IOS but I'm not able to see why, on android it works). This is the code that I have added: if (diff > 0) { setTimeout(()=>{ flipNode.card.classList.add("active"); },300) } – Pietro Ciccarello Jan 11 '22 at 11:30

0 Answers0