IntersectionObserver doesn't work while using nuxt-link

Viewed 1294

I use IntersectionObserver to add a class whenever a element is visible so I can add a fade-in/out animation in my nuxt project. It work well but if I change the page with nuxt-link (or go back to the same page) IntersectionObserver doesn't add the class (so everything is opacity: 0)

Here is the Intersection observer script in my main nuxt layout (default.vue)

mounted() {

  const animate = document.querySelectorAll('.animate');

  const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('in')
      } else {
        entry.target.classList.remove('in')
      }
    })
  }, {
   rootMargin: '-15px',
  })

  animate.forEach(entry => {
    observer.observe(entry);
  });

},

I would like this code to run everytime I change page with nuxt-link

2 Answers

I just implemented IntersectionObserver on Nuxt pages, not layouts and just tested that - it worked fine.

I have a feeling that Nuxt is doing something with layouts where the component is kept-alive and not re-mounted, but the observer is being shut off for some reason.

I haven't tested this, but I'd imagine you could set up a watcher for this.$route.name within the layout.

When this watcher is triggered, you can bind the observer.

Make sure to set immediate:true so it runs on first render.

Something like this:

computed: {
    routeName() {
        return this.$route.name;
    }
},

watch: {
  routeName: {
    immediate: true,
    handler() {
      // Bind observer here
    },
  },
},

This should give you a starting point. You may need to also manually dispose of the observer to make sure there's no duplicates. Perhaps you can check somehow if the observer is set, then only initialise a new one if it isn't.

It may also be possible to do a deep watcher on purely the $route object itself.

I suggest not to use the dom directly instead, use template refs to access the dom, just in case you have the same ref for more than one element read through this discussion Link

If you try to observe the NuxtLink directly with ref the observer is not detecting the NuxtLink as an element so it will crash to fix that enclose it with div, so that it works.

<script setup>
const root = ref(null);
const target = ref(null);

onMounted(() => {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(
      (entry) => {
        entry.target.classList.toggle("show", entry.isIntersecting);
      },
      {
        threshold: 0.5,
      }
    );
  });

  target.value.forEach((card) => {
    observer.observe(card);
  });
});
</script>

<template ref="root">
  <div>
    <NuxtLink to="/blogs" class="mb-60">Got blog</NuxtLink>

    <div v-for="i in 1" class="flex flex-col">
      <div
        ref="target"
        class="card p-10 border-2 border-black self-start rounded-xl bg-red-400"
      >
        <NuxtLink to="/blogs">Go to blog</NuxtLink>
      </div>
      <div
        ref="target"
        class="card p-10 border-2 border-black self-start rounded-xl bg-red-400"
      >
        show card
      </div>
      <div
        ref="target"
        class="card p-10 border-2 border-black self-start rounded-xl bg-red-400"
      >
        Show Card
      </div>
    </div>
  </div>
</template>
Related