How to display specific classes on an element with an intersectionObserver/Scrollspy?

Viewed 350

Here are three section in my page in vue.

<section id="home">Home</section>
<section id="about">About</section>
<section id="contact">Contact</section>

When I click on Navbar Link it scrolls me to the appropriate section. Here is my router-link.

<router-link to="/">Home</router-link>
<router-link to="/#about">About</router-link>
<router-link to="/#contact">Contact</router-link>

By default these classes are added to link of active page.

router-link-active router-link-exact-active

<a href="/" class="router-link-active router-link-exact-active" data-v-41458b80="" aria-current="page">Home</a>
<a href="/#about" class="router-link-active router-link-exact-active" data-v-41458b80="" aria-current="page">About</a>
<a href="/#contact" class="router-link-active router-link-exact-active" data-v-41458b80="" aria-current="page">Contact</a>

But as all the sections are on same index page these classes are added to all the navbar links. Is there a way to add a custom class when a page is at particular section?

1 Answers

If you want to make an intersection observer like in this example, you can use the following code

<template>
  <div>
    <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">
      Intersection observer example
    </a>

    <p
      v-observe-visibility="{
        callback: resetHashUrl,
        intersection: {
          threshold: 0.5,
        },
      }"
      style="background-color: hsl(0, 0%, 80%); height: 100vh"
    >
      top block (triggered at 50% of the block)
    </p>

    <p
      v-observe-visibility="{
        callback: (isVisible, entry) => pushNewHash(isVisible, entry, 'center'),
        intersection: {
          threshold: 0.7,
        },
      }"
      style="background-color: hsl(210, 17%, 40%); color: hsl(0, 0%, 100%); height: 100vh"
    >
      center block (triggered if at least 70% of the block visible aka threshold value)
    </p>

    <p
      v-observe-visibility="{
        callback: (isVisible, entry) => pushNewHash(isVisible, entry, 'end'),
        intersection: {
          threshold: 0.3,
        },
      }"
      style="background-color: hsl(210, 50%, 13%); color: hsl(0, 0%, 100%); height: 100vh"
    >
      end block (triggered if at least 30% of the block visible aka threshold value)
    </p>
  </div>
</template>

<script>
import Vue from 'vue'
import { ObserveVisibility } from 'vue-observe-visibility'

Vue.directive('observe-visibility', ObserveVisibility)

export default {
  methods: {
    resetHashUrl(isVisible, _entry) {
      if (isVisible) history?.replaceState(null, null, ' ')
    },
    pushNewHash(isVisible, _entry, newHash) {
      if (isVisible) location.hash = newHash
    },
  },
}
</script>

It's relying on vue-observe-visibility and working pretty great, not a lot of configuration needed!


If you're looking for a simpler scroll-spy like this one, you can use this package: https://github.com/ibufu/vue2-scrollspy


EDIT with a maybe more adapted example. This snippet of code if you want to have some classes applied depending of the route you are on, or for which you want to pass a parameter.

<template>
  <div>
    <button :class="{ 'custom-class': isOnSpecificPath('/test') }">Click me</button>
    <button :class="{ 'custom-class': isOnSpecificPath('/index') }">Click me</button>
  </div>
</template>

<script>
export default {
  methods: {
    isOnSpecificPath(pathToTest) {
      return this.$route.path === pathToTest
    },
  },
}
</script>

<style>
.custom-class {
  color: hsl(39, 100%, 46%);
  font-weight: 700;
}
</style>

enter image description here

Related