How to stop js setInterval when DOM is removed in Vuejs 3?

I have a component.vue file which houses an SVG. The animations for the SVG are defined inside the tags of the file. The script code modifies the behavior of the different parts of SVG.

The problem is when the DOM is re-rendered (when I go to another router-link/Page). I start getting

Uncaught ReferenceError: <SVG's part id> is not defined

in the console. The animation code has setInterval usage. So there are infinite instances of error – unless I refresh the browser or move back to the page (which has the SVG DOM).

export default {
  name: "JackHammer",

  mounted() {
    var funcs = [
      "load",
      "addEventListener",
      "daysLeft",
      "getElementById",
      "setInterval",
      "getTime",
      "getTimezoneOffset",
      "floor",
      "textContent",
    ];

    var daysLeft;
    var frameNumber = 0;
    var endDate = new Date(2021, 1, 20, 10, 0, 0, 0);

    window.addEventListener(
      "load",
      function () {
        initJackhammer();
      },
      false
    );
    function initJackhammer() {
      daysLeft = document.getElementById(funcs[2]);
      ShowTimeLeft();
      window.setInterval(ShowTimeLeft, 1000);

      var jackhammerTip = document.getElementById("jackhammerTip");
      var jackhammer = document.getElementById("jackhammer");
      var jackBody = document.getElementById("jackBody");
      var jackArms = document.getElementById("jackArms");
      setInterval(animateJackhammer, 40);
    }

    function ShowTimeLeft() {
      // if (!document.getElementById("hoursLeft")) {
      //   return;
      // }
      var CurrTime = new Date();
      var timeDiff = (endDate - CurrTime) / 1000;
      var timeDiffInDays = Math.floor(timeDiff / 86400);
      timeDiff -= timeDiffInDays * 86400;
      var TimeDiffHours = Math.floor(timeDiff / 3600) % 24;
      timeDiff -= TimeDiffHours * 3600;
      var TimeDiffMinutes = Math.floor(timeDiff / 60) % 60;
      timeDiff -= TimeDiffMinutes * 60;
      var TimeDiffSeconds = Math.floor(timeDiff % 60);
      daysLeft.textContent = timeDiffInDays;
      hoursLeft.textContent = TimeDiffHours;
      minutesLeft.textContent = TimeDiffMinutes;
      secondsLeft.textContent = TimeDiffSeconds;
    }

    function animateJackhammer() {
      // console.log(jackhammerTip)
      if (!document.getElementById("jackhammerTip")) {
        return;
      }

      frameNumber++;
      jackhammerTip.setAttribute(
        "transform",
        "translate(0," + (frameNumber % 3) * -3 + ")"
      );

      jackhammer.setAttribute(
        "transform",
        "translate(0," + -Math.sin(frameNumber * 1.5) + ")"
      );

      var jackBodyAmount = Math.sin(frameNumber) + 2;
      jackBody.setAttribute(
        "transform",
        "translate(0," + -jackBodyAmount + ")"
      );
      jackArms.setAttribute(
        "transform",
        "translate(0," + -jackBodyAmount + ")"
      );

      if (frameNumber == 1000) frameNumber = 0;
    }
  },
};

I am new to Vuejs and JS as a whole.
I want to JS code to suspend when the DOM is removed and again start when its rendered again. What can be the most optimized way to achieve this?

Currently, I am just exiting out of the functions if the DOM element does not exist. Would appreciate a better approach.

52 thoughts on “How to stop js setInterval when DOM is removed in Vuejs 3?”

  1. export default {
      name: "JackHammer",
      data() {
         return {
            timer: null,
         }
      },
      beforeDestroy() {
         clearInterval(this.timer);
      },
      mounted() {
        var funcs = [
          "load",
          "addEventListener",
          "daysLeft",
          "getElementById",
          "setInterval",
          "getTime",
          "getTimezoneOffset",
          "floor",
          "textContent",
        ];
    
        var daysLeft;
        var frameNumber = 0;
        var endDate = new Date(2021, 1, 20, 10, 0, 0, 0);
    
        window.addEventListener(
          "load",
          initJackhammer.bind(this),
          false
        );
        function initJackhammer() {
          daysLeft = document.getElementById(funcs[2]);
          ShowTimeLeft();
          window.setInterval(ShowTimeLeft, 1000);
    
          var jackhammerTip = document.getElementById("jackhammerTip");
          var jackhammer = document.getElementById("jackhammer");
          var jackBody = document.getElementById("jackBody");
          var jackArms = document.getElementById("jackArms");
          this.timer = setInterval(animateJackhammer, 40);
        }
    
        function ShowTimeLeft() {
          // if (!document.getElementById("hoursLeft")) {
          //   return;
          // }
          var CurrTime = new Date();
          var timeDiff = (endDate - CurrTime) / 1000;
          var timeDiffInDays = Math.floor(timeDiff / 86400);
          timeDiff -= timeDiffInDays * 86400;
          var TimeDiffHours = Math.floor(timeDiff / 3600) % 24;
          timeDiff -= TimeDiffHours * 3600;
          var TimeDiffMinutes = Math.floor(timeDiff / 60) % 60;
          timeDiff -= TimeDiffMinutes * 60;
          var TimeDiffSeconds = Math.floor(timeDiff % 60);
          daysLeft.textContent = timeDiffInDays;
          hoursLeft.textContent = TimeDiffHours;
          minutesLeft.textContent = TimeDiffMinutes;
          secondsLeft.textContent = TimeDiffSeconds;
        }
    
        function animateJackhammer() {
          // console.log(jackhammerTip)
          if (!document.getElementById("jackhammerTip")) {
            return;
          }
    
          frameNumber++;
          jackhammerTip.setAttribute(
            "transform",
            "translate(0," + (frameNumber % 3) * -3 + ")"
          );
    
          jackhammer.setAttribute(
            "transform",
            "translate(0," + -Math.sin(frameNumber * 1.5) + ")"
          );
    
          var jackBodyAmount = Math.sin(frameNumber) + 2;
          jackBody.setAttribute(
            "transform",
            "translate(0," + -jackBodyAmount + ")"
          );
          jackArms.setAttribute(
            "transform",
            "translate(0," + -jackBodyAmount + ")"
          );
    
          if (frameNumber == 1000) frameNumber = 0;
        }
      },
    };
    Reply
  2. Assign your interval to a variable and then ClearInterval(variable) during the beforeUnmount step.

    Eg.:

    let myInterval
    
    [...]
    
    mounted() {
      myInterval = setInterval(animateJackhammer, 40);
    }
    
    [...]
    
    beforeUnmount(() => { 
      clearInterval(myInterval);
    })
    
    Reply

Leave a Comment