Effortless Scrollspy with a custom Navigation Component, no Bootstrap needed

In my Table of Contents, identified as #TableOfContents, each href points to either an h2 or h3 tag.

The issue I'm facing is that when the heading (h2 or h3) is observed by the intersection observer, the corresponding link in #TableOfContents is highlighted by adding the class

side-active</code. However, once the longer content (like a <code>p
paragraph) after the heading enters the viewport, the highlight for that section is removed because the heading is no longer in view.

This is problematic because I want the section (h2, h3) to remain highlighted until the next h2 or h3 crosses the midpoint of the viewport.

Any suggestions on how to solve this?

window.addEventListener('DOMContentLoaded', () => {
  const observer = new IntersectionObserver(entries => {
    entries.forEach(entry => {
    const id = entry.target.getAttribute('id');
      if (entry.intersectionRatio > 0) {
        document.querySelector(`#TableOfContents a[href="#${id}"]`).classList.add('side-active');
      } else {
        document.querySelector(`#TableOfContents a[href="#${id}"]`).classList.remove('side-active');
      }
    });
  });

  toc = document.querySelectorAll('#TableOfContents a');
    // get content so that link refer to it
  toc.forEach(function (link) {
    var id = link.getAttribute("href");
    var element = document.querySelector(id);
    observer.observe(element);
  });
});

Highlighted Text When Heading is in Viewport

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

Text Not Highlighted When Heading is Out of Viewport

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

Answer №1

Our task involved calculating containerBottom by:

  1. adding the current container's top with the next container's top

  2. Alternatively, if it is the last container,

    add the current container's top with its outerHeight()

Once that is done; if the ith container is in the scroll position, add a class to it and remove the class from all containers prior to the ith container (lines 18-21) (reverse loop)

$(function () {
  // Table of contents (`ul`) that contains `a` tag in `li` which we want to highlight  
  var sectionIds = $('#TableOfContents a'); 

    $(document).on('scroll', function(){
        sectionIds.each(function(i, e){
            var container = $(this).attr('href');
            var containerOffset = $(container).offset().top; // container's top
            var nextContainer = $(sectionIds[i+1]).attr('href')

            if (i != sectionIds.length-1) {
             // if this container isn't the last container
              var containerHeight = $(nextContainer).offset().top;
            } else {
             // the last container's height will be outerHeight
              var containerHeight = $(container).outerHeight();
            }
            var containerBottom = containerOffset + containerHeight;

            var scrollPosition = $(document).scrollTop();
            if(scrollPosition < containerBottom - 20 && scrollPosition >= containerOffset - 20){
              for (var j = i; j >= 0; j--) {
                $(sectionIds[j]).removeClass('active');
              }  
              $(sectionIds[i]).addClass('active');
            } else {
                $(sectionIds[i]).removeClass('active');
            }
        });
    });
});

Ensure that the first heading ("Line Equations" h1) remains highlighted even when it is out of the viewport until the next subheading ("Distance of a point from a line") is in view.

https://i.sstatic.net/w38KD.gif

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

Using the ES6 filter method to iterate through a double nested array of objects

Struggling with filtering a nested array of objects and specifically stuck at the filter part. Any idea on how to eliminate one of the marks? this.state = { data: [ { id: 1, name: "Main", subs: [ { id: "jay", ...

Trouble retrieving data (React-redux)

Greetings! I am currently working on a project involving the Spotify API and attempting to retrieve new releases. While the data is successfully passed down to the reducer, I encounter an issue when calling the fetch action in my App component. When I try ...

What is the best way to add query parameters to router.push without cluttering the URL?

In my current project, I am using NextJS 13 with TypeScript but not utilizing the app router. I am facing an issue while trying to pass data over router.push to a dynamically routed page in Next.js without compromising the clarity of the URL. router.push({ ...

"Troubleshooting: IE compatibility issue with jQuery's .each and .children functions

I have created an Instagram image slider feed that works well in Chrome, Safari, Firefox, and Opera. However, it fails to function in all versions of Internet Explorer without showing any error messages. To accurately determine the height of images for th ...

Exploring the Jquery functionality: $(document).ready(callback);

Hey there, I'm struggling to run a function both on window resize and document ready. The issue is that when the resize event is triggered by mobile scroll, it's essential to ensure the height hasn't changed. Oddly enough, the code works fin ...

The selection in one dropdown menu influences the options in another dropdown menu

My partner and I have teamed up to develop a React translation application that utilizes the Lexicala API. Our project includes two selector components: one for choosing the source language and another for selecting the target language. Users will first pi ...

Error: The default export is not a component compatible with React in the specified page: "/"

I'm facing an issue while building my next app. Despite using export default, I keep encountering an error that others have mentioned as well. My aim is to create a wrapper for my pages in order to incorporate elements like navigation and footer. vi ...

Is it necessary to include a request in the API route handler in Next.js when passing parameters?

In my API route handler, I have a function for handling GET requests: import { NextRequest, NextResponse } from "next/server"; export async function GET(req: NextRequest, { params }: { params: { id: string } }) { const { id } = params; try { ...

Common causes leading to my header component experiencing hydration errors in Next.js

I am currently developing a job portal and encountering an issue with user authentication using JWT in combination with local storage. The problem arises in my header component when the user is authenticated, and if the web page is reloaded, I receive a hy ...

``The Art of Handling REST API with Express and Mongoose

Running Express on my application, I have a delete route set up as shown below: router.route('/lists/:id') .delete(function(req, res){ Entry.remove({ _id: req.params.id }, function(err, list){ if(err) ...

Encountering the error message: "Function arguments could not be parsed ()=>"

Here is the code I have written: projectDependencies.forEach((val)=> { container.register(val[0],function () { return require(val[1]); }); }); When I execute the command nodemon server.js, I encounter the following error: Error: c ...

extract the text content from an object

I am trying to add an object to the shopping cart. The item contains a key/value pair as shown in the following image: https://i.stack.imgur.com/5inwR.png Instead of adding the title with its innerText using p and style, I would like to find another ...

Tips for displaying filtered items on the MongoDB terminal

I have objects categorized by cost and date of addition, each user having such categories. My goal is to display all categories for the month "08" in the console. How can I achieve this? Currently, my code retrieves the entire object with the user informat ...

show the day of the week for a specific date saved in a MongoDB database

I need to create a report showing the total number of purchases for each day in the past week, formatted like this: { "sunday":30, "monday":20, ... } Each purchase in the database is structured as follows: { _id: 603fcb ...

Reading a file with fs alters the visibility of a global array, rendering it inaccessible beyond its scope

There are two sections of code. The first one is called by the second to populate an array and write it into a file. async function timeSeries(obj) { data = [ { original_value: [] } ] //read file named as passed obj ...

Creating a two-dimensional canvas within a div element using the console

I have some code that currently generates an image in the console when I hit F12. However, I would like to display this image inside a more user-friendly area such as a div on the webpage. I attempted to create a <div class = "mylog"> to place the i ...

The line break refuses to concatenate

I've been grappling with trying to format and print a 9x9 2D JavaScript array into a grid, but for some reason I can't seem to get the line breaks right. Instead of displaying the array in a neat square grid, it's all coming out in a single ...

Is it considered poor practice to create refs outside of a component?

Currently, I am developing a library that requires exporting certain functions for users to utilize. These functions rely on having access to a component reference in order to manipulate classNames and enable auto-scroll functionalities, among other things ...

Is there a way to integrate a PHP variable into JavaScript's getTime() function?

I'm attempting to compare two dates: one is the current date, and the other is a date stored in my database (retrieved through an SQL query). I was advised to use new Date(), but I'm unsure how to include a php variable in the script. Here is the ...

Using ui-grid to show cells as drop-down menus

I have a ui-grid example where one of the columns represents gender ('Gender': male, female...). The json data binding to the grid only contains code types like (1, 2, 3...). However, I want to display the gender name such as 'male' if ...