Showing particular classes on an element using intersectionObserver/Scrollspy: a step-by-step guide

Here are three different sections on my Vue page.

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

When I click on a Navbar Link, it scrolls me to the appropriate section. Here is the code for 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 the active page links.

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>

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

Answer №1

To create an intersection observer similar to the one shown in this example, you can utilize the code provided below:

<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>

This solution relies on vue-observe-visibility and requires minimal configuration.


If you're interested in a simpler scroll-spy functionality like the one demonstrated here, you can opt for the package provided at https://github.com/ibufu/vue2-scrollspy


For a more tailored example, consider the snippet of code below that applies classes based on the route or specific 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>

https://i.sstatic.net/zXETJ.png

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Personalized cursor that blinks while utilizing window.history.replaceState

While navigating between sub-pages, I utilize the window.history.replaceState method to replace URLs in my web application. However, I have noticed that my custom cursor briefly blinks (replaced by cursor: default) when the current URL is replaced with a n ...

Having trouble retrieving input field values with Angular.js

I am struggling to access the input field values in my Angular.js application. Below is the code snippet I am using: <div class="input-group bmargindiv1 col-md-12"> <span class="input-group-addon ndrftextwidth text-right" style="width:180px"& ...

Steps to add an SVG to a DIV when clicked

This script is fully functional and produces the desired results. var play = function(){ $( this ).click(function() { console.log( "Test" ); }); } I am looking to replace the console.log( "Test" ); with an SVG that will display in the cli ...

Dealing with adding up optional values from v-model in Vue.js can be a challenging task

Within this function, I need to display the remaining amount. remainingAmount: function() { return parseFloat(this.sumAmount) - (parseFloat(this.cash) + parseFloat(this.kNet) + parseFloat(this.kNetOnline)); } The three parameters cash ...

An issue is preventing the Angular 2+ http service from making the requested call to the server

I am looking to create a model class that can access services in Angular. Let's say I have the following endpoints: /book/:id /book/:id/author I want to use a service called BooksService to retrieve a list of Book instances. These instances should ...

Vue Js image loading issue

I'm having trouble referencing an image to display in my view. I keep getting this error message: "InvalidCharacterError: Failed to execute 'setAttribute' on 'Element': 'product.images[0].filename' is not a valid attribut ...

Differences Between React Prop Types and Typescript in Front-End

I'm considering incorporating TypeScript into my project. Would this render the use of prop-types in React unnecessary? With prop-types, I find myself having to manually define types, but TypeScript would eliminate this step. Am I on the right track? ...

The Vue directive allows for seamless integration of the 'on' and 'class' directives

I am looking to consolidate multiple directives within a custom directive similar to the code snippet below using model: const model = Vue.directive('model') Vue.directive('custom', { bind(el, binding, vnode, oldVnode) { / ...

What is the best way to convert a string in JavaScript to be case-insensitive?

Can anyone assist me? Challenge: Develop a function called indexOfIgnoreCase which takes in two strings and identifies the first instance of the second string within the first string. This function should be insensitive to letter case. For example, indexO ...

Guide to incorporating owl carousel into a Vue.js project

Hey there, I recently set up a new project using Vue CLI 3 and I'm having some trouble importing Owl Carousel 2. Here's a snippet from my main.js file: global.jQuery = require('jquery'); var $ = global.jQuery; window.$ = $; import "o ...

What steps should I take to address the error message "TypeError: express-validator is not a function

I am currently utilizing express-validator version 6.4.0 and encountering an error when running the server. I have attempted to implement custom validation by organizing separate files for validator, controller, and routes. Here is the primary server file ...

The testing for React and TypeScript did not succeed. It is recommended to utilize the "jsdom" test environment for better results

I'm working on a basic React/TypeScript project and diving into the world of testing. I've opted for React Testing Library and Jest to test a straightforward product page that should display the message "Welcome to our product page." Unfortunate ...

Steps to display a single entry in a Laravel/Vue implementation

My goal is to combine Laravel with Vue and eventually Nuxt in order to incorporate dynamic page transitions similar to the ones showcased on into my websites. I recently followed a tutorial on using Vue with Laravel at https://scotch.io/tutorials/build-a ...

Strategies for handling numerous node projects efficiently?

Currently, we are utilizing three distinct node projects: Project 1, Project 2, and Project 3 incorporating react and webpack. Each of these projects is stored in their individual repositories. While Project 1 and Project 2 operate independently, Project ...

The error "TypeError: b.toLowerCase is not a function in the bootstrap typeahead plugin" indicates that

Currently, I am working on implementing autocomplete search using the typeahead plugin version 3.1.1. My implementation involves PHP, MySQL, AJAX, and JavaScript/jQuery. While everything works perfectly with mysqli in terms of displaying suggestions when t ...

Issue with importing CSS/SASS into Vue Cli 3 Typescript within the <script> block

Recently, I created a new Vue app using TypeScript with Vue Cli 3. However, when attempting to import CSS or Sass into my TypeScript file, I encountered the following issue: import * as sassStyles from '@/assets/sass/my.module.scss'; This re ...

Delete auto-generated list using handlebars JS

I have successfully created a dynamic list using Handlebars.js and its template. However, I am now facing confusion on how to remove or delete items from the list using a function or specific code. As I am new to Handlebars, I would appreciate any help. ...

Make sure to concentrate on the input field when the DIV element is clicked

In my React project, I am working on focusing on an input element when specific buttons or elements are clicked. It is important for me to be able to switch focus multiple times after rendering. For instance, if a name button is clicked, the input box for ...

learning how to combine two json arrays of objects and showcase them in a react component

What is the best way to combine this data and present it in a table with a map using React? The text will be in the first column and the count in the second. const handleSubmit = async (event) => { event.preventDefault(); let URL1 = " ...

Error in AngularJS: The argument 'fn' is not a function and is undefined

I'm encountering an issue with AngularJS 1.6, and the error message is stating: Error: [ng:areq] Argument 'fn' is not a function, got undefined I suspect the problem lies within my testService, and I'm seeking assistance in identify ...