Nuxt js Custom cursor event listener not working after route change

Viewed 468

I'm currently building a new website for our studio but can't get the custom cursor to work properly. Here's a custom cursor built with gsap and the result was great except when I navigate to another route and back to the home page, the mouseover event stops working and I can't find a reason why.

What could be causing this and how could this be fixed?

Thank you in advance!

Here's the CustomCursor component:

    <template>
      <div class="custom-cursor">
        <div id="cursor-big" class="custom-cursor__ball custom-cursor__ball--big"></div>
        <div id="cursor-small" class="custom-cursor__ball custom-cursor__ball--small"></div>
      </div>
    </template>
    
    <script>
      import gsap from "gsap";
    
      export default {
        props: {
          hoverClass: {
            type: String,
            default: 'cursorHover'
          }
        },
        mounted () {
          const cursorBig = document.getElementById('cursor-big'),
                cursorSmall = document.getElementById('cursor-small'),
                links = document.getElementsByTagName("a"),
                withClassHover = document.getElementsByClassName(this.hoverClass),
                withHover = [...links, ...withClassHover];
    
          // Event Listeners
          document.addEventListener("mousemove", onMouseMove);
          document.addEventListener("mousedown", onMouseHover);
          document.addEventListener("mouseup", onMouseHoverOut);
          document.addEventListener("mouseenter", () => {
            cursorBig.style.opacity = 1;
            cursorSmall.style.opacity = 1;
          });
          document.addEventListener("mouseleave", () => {
            cursorBig.style.opacity = 0;
            cursorSmall.style.opacity = 0;
          });
          withHover.forEach((element) => {
            element.addEventListener("mouseover", onMouseHover);
            element.addEventListener("mouseout", onMouseHoverOut);
          })
    
          // Event Handlers
          function onMouseMove(e) {
            cursorSmall.style.opacity = 1;
            gsap.to(cursorBig, 0.4, {
              x: e.clientX - 18.5,
              y: e.clientY - 18.5
            });
            gsap.to(cursorSmall, 0.1, {
              x: e.clientX - 4,
              y: e.clientY - 4
            });
          }
          function onMouseHover() {
            gsap.to(cursorBig, 0.3, {
              scale: 3,
            });
          }
          function onMouseHoverOut() {
            gsap.to(cursorBig, 0.3, {
              scale: 1,
            });
          }
        }
      };
    </script>
    
    <style>
      @media screen and (min-width:1100px) {
        * {
          cursor: none !important;
        }
    
        .custom-cursor__ball {
          position: fixed;
          top: 0;
          left: 0;
          mix-blend-mode: difference;
          z-index: 99999;
          opacity: 0;
          pointer-events: none;
          transition: opacity 0.5s ease;
        }
    
        .custom-cursor__ball--big {
          content: "";
          width: 35px;
          height: 35px;
          background: white;
          border-radius: 50%;
        }
    
        .custom-cursor__ball--small {
          content: "";
          width: 6px;
          height: 6px;
          background: #fff;
          border-radius: 50%;
        }
      }
    </style>
2 Answers

Moved from comments:

  • Issue: Elements with cursorHover class are not on the DOM after they have been removed when you route off somewhere else. Mounted only fires once.

Fix: Handle reinitiating your events onto the dom elements, and destroy your custom event handlers as the route changes.

<template>
  <div class="custom-cursor">
    <div
      id="cursor-big"
      class="custom-cursor__ball custom-cursor__ball--big"
    ></div>
    <div
      id="cursor-small"
      class="custom-cursor__ball custom-cursor__ball--small"
    ></div>
  </div>
</template>

<script>
import gsap from "gsap";

export default {
  name: "CustomCursor",
  props: {
    hoverClass: {
      type: String,
      default: "cursorHover",
    },
  },
  data() {
    return {
      cursorBig: null,
      cursorSmall: null,
      withHover: [],
    };
  },
  watch: {
    "$route.path"() {
      console.log("route change");
      this.destroy();
      this.$nextTick(this.init);
    },
  },
  mounted() {
    console.log("mounted");
    this.$nextTick(this.init);
  },
  beforeDestroy() {
    console.log("beforeDestroy");
    this.destroy();
  },
  methods: {
    init() {
      console.log("init");
      setTimeout(() => {
        this.cursorBig = document.getElementById("cursor-big");
        this.cursorSmall = document.getElementById("cursor-small");

        this.withHover = [
          ...document.getElementsByTagName("a"),
          ...document.getElementsByClassName(this.hoverClass),
        ];

        this.withHover.forEach((element) => {
          element.addEventListener("mouseover", this.onMouseHover);
          element.addEventListener("mouseout", this.onMouseHoverOut);
        });
        document.addEventListener("mousemove", this.onMouseMove);
        document.addEventListener("mousedown", this.onMouseHover);
        document.addEventListener("mouseup", this.onMouseHoverOut);
        document.addEventListener("mouseenter", this.onMouseEnter);
        document.addEventListener("mouseleave", this.onMouseLeave);
      }, 100);
    },
    destroy() {
      console.log("destroy");
      this.withHover.forEach((element) => {
        element.removeEventListener("mouseover", this.onMouseHover);
        element.removeEventListener("mouseout", this.onMouseHoverOut);
      });
      document.removeEventListener("mousemove", this.onMouseMove);
      document.removeEventListener("mousedown", this.onMouseHover);
      document.removeEventListener("mouseup", this.onMouseHoverOut);
      document.removeEventListener("mouseenter", this.onMouseEnter);
      document.removeEventListener("mouseleave", this.onMouseLeave);
    },
    onMouseEnter() {
      this.cursorBig.style.opacity = 1;
      this.cursorSmall.style.opacity = 1;
    },
    onMouseLeave() {
      this.cursorBig.style.opacity = 0;
      this.cursorSmall.style.opacity = 0;
    },
    onMouseMove(e) {
      this.cursorSmall.style.opacity = 1;
      gsap.to(this.cursorBig, 0.4, {
        x: e.clientX - 18.5,
        y: e.clientY - 18.5,
      });
      gsap.to(this.cursorSmall, 0.1, {
        x: e.clientX - 4,
        y: e.clientY - 4,
      });
    },
    onMouseHover() {
      gsap.to(this.cursorBig, 0.3, {
        scale: 3,
      });
    },
    onMouseHoverOut() {
      gsap.to(this.cursorBig, 0.3, {
        scale: 1,
      });
    },
  },
};
</script>

<style>
@media screen and (min-width: 1100px) {
  * {
    cursor: none !important;
  }

  .custom-cursor__ball {
    position: fixed;
    top: 0;
    left: 0;
    mix-blend-mode: difference;
    z-index: 99999;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.5s ease;
  }

  .custom-cursor__ball--big {
    content: "";
    width: 35px;
    height: 35px;
    background: black;
    border-radius: 50%;
  }

  .custom-cursor__ball--small {
    content: "";
    width: 6px;
    height: 6px;
    background: #000;
    border-radius: 50%;
  }
}
</style>

i currently facing the same issue, but it solved after adding,

this.$next.tick()

so, i just attached the handler when the components is mounted :

mounted() {
    this.$nextTick(() => {
      this.transitionController()
      window.addEventListener('scroll', () => {
        this.transitionController()
      })
    })
},
Related