Emphasizing hyperlinks according to the user's scrolling location

Currently, I am attempting to create a feature where links are highlighted when the user scrolls over the corresponding section on the page. However, there seems to be an issue with the functionality as Link 2 is highlighting instead of Link 1 as intended.

<nav>
  <ul>
    <li><a href="" id="link_1">Link 1</a></li>
    <li><a href="" id="link_2">Link 2</a></li>
    <li><a href="" id="link_3">Link 3</a></li>
  </ul>
   <p></p>
</nav>

<div id="sec_one" class="sections">

</div>

<div id="sec_two" class="sections">

</div>

<div id="sec_three" class="sections">

</div>

<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>

*{
  margin: 0;
  padding: 0;
}
nav{
  width: 100%;
  background-color: black;
  position: fixed;
  top: 0;
}

nav ul{
  width: 50%;
  margin: 0 auto;
  list-style-type: none;
  text-align: center;
}

nav ul li{
  display: inline;
  width: 100%;
}

nav ul li a{
  font-size: 40px;
  color: white;
  text-decoration: none;
}

.sections{
  width: 100%;
  height: 2000px;
}

#sec_one{
  background-color: blue;
}

#sec_two{
  background-color: red;
}

#sec_three{
  background-color: yellow;
}

.active{
  background-color: #666666;
}

p{
  color: white;
}

$(window).scroll(function(){
  var scrollPos = $(window).scrollTop();
  var page1Top = $("#sec_one").scrollTop();
  var page1Bot = $("#sec_one").outerHeight();

  var page2Top = $("#sec_two").scrollTop();
  var page2Bot = $("#sec_two").outerHeight();

  var page3Top = $("#sec_three").scrollTop();
  var page3Bot = $("#sec_three").outerHeight();

  if(scrollPos >= page1Top && scrollPos < page1Bot){
    $("#link_1").addClass("active");
    $("#link_2").removeClass("active");
    $("#link_3").removeClass("active");
  }else {
    $("#link_1").removeClass("active");
  }

  if(scrollPos >= page2Top && scrollPos < page2Bot){
    $("#link_2").addClass("active");
    $("#link_1").removeClass("active");
    $("#link_3").removeClass("active");
  }else {
    $("#link_2").removeClass("active");
  }

});

Answer №1

You may want to consider using the .offset() method in your code. This way, you'll be able to get the position relative to the document rather than just relative to itself, taking other elements into account.

Additionally, there is no need to check the bottom location. You can simply focus on the top location of the next section.

$(document).ready(function() {
  $(window).scroll(function() {
    var scrollPos = $(window).scrollTop();
    
    var page1Top = $("#sec_one").offset().top;
    var page2Top = $("#sec_two").offset().top;
    var page3Top = $("#sec_three").offset().top;

    if (scrollPos >= page1Top && scrollPos < page2Top) {
      $("#link_1").addClass("active");
      $("#link_2").removeClass("active");
      $("#link_3").removeClass("active");
    } else {
      $("#link_1").removeClass("active");
    }

    if (scrollPos >= page2Top && scrollPos < page3Top) {
      $("#link_2").addClass("active");
      $("#link_1").removeClass("active");
      $("#link_3").removeClass("active");
    } else {
      $("#link_2").removeClass("active");
    }
    
    if (scrollPos >= page3Top) {
      $("#link_3").addClass("active");
      $("#link_1").removeClass("active");
      $("#link_2").removeClass("active");
    } else {
      $("#link_3").removeClass("active");
    }

  });
});
* {
  margin: 0;
  padding: 0;
}

nav {
  width: 100%;
  background-color: black;
  position: fixed;
  top: 0;
}

nav ul {
  width: 50%;
  margin: 0 auto;
  list-style-type: none;
  text-align: center;
}

nav ul li {
  display: inline;
  width: 100%;
}

nav ul li a {
  font-size: 40px;
  color: white;
  text-decoration: none;
}

.nav {


.active {
  background-color: #666666;
}

p {
  color: white;
}
<nav>
  <ul>
    <li><a href="" id="link_1">Link 1</a></li>
    <li><a href="" id="link_2">Link 2</a></li>
    <li><a href="" id="link_3">Link 3</a></li>
  </ul>
  <p></p>
</nav>

<div id="sec_one" class="sections"></div>
<div id="sec_two" class="sections"></div>
<div id="sec_three" class="sections"></div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Answer №2

To prevent targeting individual elements and using rough IDs, you can assess the scrollTop and offset().top of each element to highlight the necessary item based on the section index:

$(window).scroll(function() {
  var scrollPosition = $(window).scrollTop(),
      navHeight     = $('nav').height();
  $('.sections').each(function(index){
    var offsetTop = $(this).offset().top;
    if((offsetTop-scrollPosition-navHeight) <= 0) {
      $('.active').removeClass('active')
      $('nav a').eq(index).addClass('active')
    }
  })
});
* { margin: 0; padding: 0;} nav { width: 100%; background-color: black; position: fixed; top: 0;} nav ul { width: 50%; margin: 0 auto; list-style-type: none; text-align: center;} nav ul li { display: inline; width: 100%;} nav ul li a { font-size: 40px; color: white; text-decoration: none;} .sections { width: 100%; height: 2000px;} #sec_one { background-color: blue;} #sec_two { background-color: red;} #sec_three { background-color: yellow;} .active { background-color: #666666;} p { color: white;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
  <ul>
    <li><a href="" id="link_1" class="active">Link 1</a></li>
    <li><a href="" id="link_2">Link 2</a></li>
    <li><a href="" id="link_3">Link 3</a></li>
  </ul>
  <p></p>
</nav>
<div id="sec_one" class="sections"></div>
<div id="sec_two" class="sections"></div>
<div id="sec_three" class="sections"></div>

Answer №3

Adjusted the code to streamline your JavaScript by dynamically identifying which div is in view and updating the corresponding nav element class.

var $sections = $('.sections'),
    $lis = $('nav li');

$(window).on('scroll', function(){
  var scrollPos = $(window).scrollTop(),
      navHeight = $('nav').outerHeight();
  $sections.each(function() {
    var top = $(this).offset().top,
        bottom = top + $(this).outerHeight();
    if (scrollPos > top - navHeight && scrollPos < bottom) {
      var $target = $lis.eq($(this).index() - 1);
      $lis.not($target).removeClass('active');
      $target.addClass('active');
    }
  })
});
*{
  margin: 0;
  padding: 0;
}
nav{
  width: 100%;
  background-color: black;
  position: fixed;
  top: 0;
}

nav ul{
  width: 50%;
  margin: 0 auto;
  list-style-type: none;
  text-align: center;
}

nav ul li{
  display: inline-block;
}

nav ul li a{
  font-size: 40px;
  color: white;
  text-decoration: none;
  display: inline-block;
}

.sections{
  height: 200vh; ;
}

#sec_one{
  background-color: blue;
}

#sec_two{
  background-color: red;
}

#sec_three{
  background-color: yellow;
}

.active{
  background-color: #666666;
}

p{
  color: white;
}
<nav>
  <ul>
    <li><a href="" id="link_1">Link 1</a></li>
    <li><a href="" id="link_2">Link 2</a></li>
    <li><a href="" id="link_3">Link 3</a></li>
  </ul>
  <p></p>
</nav>

<div id="sec_one" class="sections">

</div>

<div id="sec_two" class="sections">

</div>

<div id="sec_three" class="sections">

</div>

<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>

Answer №4

It seems like you're looking to create links that navigate to different sections within the same page rather than loading a new page altogether.

An easy way to achieve this is by using ScrollSpy. For detailed instructions, you can refer to the documentation available here.

Below is some example code demonstrating how you can implement this on your webpage:

Start by including the scrollspy.js file in your project. Make sure to adjust the URL based on where the file is located.

<script src="scrollspy.js"></script>

Next, within your script file for the page, you might have something similar to the following:

$('.sections').on('scrollSpy:enter', function() {
  switch($(this).attr('id')) {
    case "sec_one":
      $("#link_1").addClass("active");
      $("#link_2").removeClass("active");
      $("#link_3").removeClass("active");
      break;
    case "sec_two":
      $("#link_1").removeClass("active");
      $("#link_2").addClass("active");
      $("#link_3").removeClass("active");
      break;
    case "sec_three":
      $("#link_1").removeClass("active");
      $("#link_2").removeClass("active");
      $("#link_3").addClass("active");
      break;
  }
}

$('.sections').scrollSpy();

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

Utilizing ng-bind-html to establish Angular bindings within the HTML code

My scenario involves hitting a specific route (#/abc) and then making a POST request to the server to render the HTML received as a response. Instead of embedding this logic directly into $routeProvider.when, I came up with my own solution. This is how I ...

Ways to eliminate unnecessary re-rendering of components that remain unchanged?

I am struggling with a component that contains various other components, such as text fields. Whenever an input is made in the text field, all the components are re-rendered. My goal is to prevent this re-rendering and only update the component that has a ...

The click event is activated following the :active selector being triggered

My Angular application features a button with a slight animation - it moves down by a few pixels when clicked on: &:active { margin: 15px 0 0 0; } The button also has a (click)="myFunction()" event listener attached to it. A common issue arises w ...

What is the best way to retrieve a lengthy HTML page string parameter from a Java request?

While working with Javascript, I encountered an issue with sending HTML pages in post data. In my Java code, I used String html = request.getParameter("templateHtml"); and during debugging, I could see the HTML string in the request. However, the "html" va ...

What is the method to disable response validation for image endpoints in Swagger API?

I'm working with a Swagger YAML function that looks like this: /acitem/image: x-swagger-router-controller: image_send get: description: Returns 'image' to the caller operationId: imageSend parameters: ...

Tips for preventing harmful messages in a chat room app

As I am working on a chat room website, users are able to input any content they want into the entry field and then send it to all online users. However, I have concerns about security - what if malicious individuals try to inject harmful HTML or JavaScrip ...

Align text vertically next to an image

I am facing a challenge with aligning text vertically next to an image floated left. I have tried various solutions recommended on different platforms, but none seem to work for me. Despite following the suggestions provided in this particular solution, I ...

Transmitting data from the front end to the server in React/Shopify for updating API information using a PUT request

After successfully retrieving my API data, I am now tasked with updating it. Within my component, I have the ability to log the data using the following code snippet. In my application, there is an input field where I can enter a new name for my product an ...

Enhancing JSON data in Datatables with additional text

I'm currently looking for a way to insert some text into my data before generating a table using jQuery DataTables. As an example, if my JSON data looks like [1,5,6,12], I would like it to be displayed as [1 seconds, 5 seconds, 6 seconds, 12 seconds] ...

Modify the h:outputText value dynamically with the power of jQuery!

Is it possible to use jQuery to dynamically change the value of my OutputText component? This is the code snippet for my component: <h:outputText id="txt_pay_days" value="0" binding="#{Attendance_Calculation.txt_pay_days}"/> I would apprecia ...

Sending an array through HTML select using Jquery

Is this scenario possible: I have implemented an Ajax call to fetch results from a SQL query. The data is retrieved in an array format, which I then convert to JSON and output at the end of a PHP script that the Ajax function calls. Next, for each row in ...

NodeJS closes the previous server port before establishing a new server connection

During my development and testing process, whenever I make changes, I find myself having to exit the server, implement the updates, and then start a new server. The first time I run the command node server.js, everything works perfectly. However, when I m ...

Tips for generating a fresh array by conditionally including an extra property based on the comparison of two arrays

I am working with two different arrays: lockers: [ { locker_id: 1, label: { size: 1, label: "small" } }, { locker_id: 2, label: { size: 3, label: "medium" } }, { locker_id: 3 ...

The issue of video tags not displaying previews on iPhone across all browsers is also accompanied by problems with controls not functioning correctly

As I delve into the world of HTML5 video tags, I encountered an issue where the video wouldn't show a preview frame initially. Instead, only the Resume button would appear. Furthermore, after playing the video with the Resume button, it wouldn't ...

Ways to stop users from inputting incorrect characters into an input field

I need help with restricting user input in a text field to only allow letters, numbers, and dashes (-). For alphanumerics only - "The alphanumeric character set includes numbers from 0 to 9 and letters from A to Z. In the Perl programming language, the un ...

Prevent the ajax script from running if the previous call has not yet completed successfully

My situation involves a button that triggers an ajax function. Sometimes the server response is slow, causing users to click the button multiple times in frustration. The main ajax function in question is structured as follows: $.ajax({ type: "POS ...

Problem with switching the view using the dropdown feature of the bootstrap datepicker

I am encountering an issue with the bootstrap datepicker when I attempt to change the view of the datepicker based on the mode selected in the dropdown. Here is the HTML code : Mode : <select id="mode"> <option value="1">Day</option&g ...

The ajax method is encountering an issue when trying to perform an action: It is unable to find the necessary anti-forgery form field "__RequestVerificationToken" required for the operation

I am encountering an issue with my ajax method that triggers the action method from the controller. When I run this method, I receive an error stating: The required anti-forgery form field "__RequestVerificationToken" is not present. However, upon inspecti ...

Unresolved promise rejection on Repl.it

I decided to add a basic leaderboard feature to my game on Repl.it, so I set up a node.js backend for it. Here's the code snippet for the backend: const express = require('express'); const Client = require('@replit/database'); cons ...

Express route encountered an error with an undefined value

Here's the method declaration: exports.postRedisValue = function(req,res) { let keyRedis = req.body.key; let valueRedis = req.body.value; console.log(keyRedis); //displays as undefined if(keyRedis && valueRedis){ ...