Angular 6: A guide to dynamically highlighting navbar elements based on scroll position

I am currently building a single page using Angular 6. The page is quite basic, and my goal is to emphasize the navbar based on scrolling.

Below is the code snippet I am working with:

.sticky {
  position: sticky;
  top: 0;
}
#i ul {
  list-style-type: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: blue;
}

#i li {
  float: left;
}

#i li a {
  display: block;
  color: white;
  text-align: center;
  padding: 14px 16px;
  text-decoration: none;
}

#i li a:hover {
  border-radius: 0px 0px 10px 10px;
  background-color: rgb(43, 137, 226);
}

/* #i {
  margin: 0px 0px 40px 0px;
} */

#footer-id {
  background-color:blue;
}
<span id="i">
  <ul class="sticky">
    <li id="i"><a class="active" href="#">Home</a></li>
    <li><a href="#news" (click)="scrollToElement(new)">News</a></li>
    <li><a href="#contact" (click)="scrollToElement(con)">Contact</a></li>
    <li><a href="#about" (click)="scrollToElement(about)">About</a></li>

    <ul class="nav navbar-nav pull-right">
      <li class="nav"><a href="#">Contact</a></li>
    </ul>
    <ul class="nav navbar-nav pull-right">
      <li class="nav">
        <a href="#ta" (click)="scrollToElement(con)">Target</a>
      </li>
    </ul>
  </ul>
</span>

<div class="container" style="margin-bottom: 20px;">
  <div #home>
    <br />
    <br />
    <h1>Home</h1>
    <p>You can contact us here. Thank you for your greatness.</p>
    <p>
      Welcome to the Random Phrase and Idiom Generator. This tool can be very helpful for expanding your vocabulary...
    </p>
    <p>
      Welcome to the Random Phrase and Idiom Generator. This tool can be very helpful for expanding your vocabulary...
    </p>
  </div>
  <div #con>
    <br />
    <br />
    <h1>Contact</h1>
    <p>You can reach out to us here. Appreciate your support!</p>
    <p>
      Idioms are a wonderful part of the English language that add flavor and depth to communication...
    </p>
    <p>
      Idioms are a wonderful part of the English language that add flavor and depth to communication...
    </p>
  </div>

  <div #new>
    <br />
    <br />
    <h1>News</h1>
    <p>This contains our latest news updates.</p>
    <p>
      Using this tool can enhance language learning... 
    </p>
    <p>
      Using this tool can enhance language learning...
    </p>
  </div>;

  <div #about>
    <br />
    <br />
    <h1>About</h1>
    <p>This showcases more information about us.</p>
    <p>
      It offers writers an opportunity to boost creativity...
    </p>
    <p>
      It offers writers an opportunity to boost creativity...
    </p>
  </div>

</div>

<!-- Footer -->
<footer class="page-footer font-small indigo" id="footer-id">
  <div class="container text-center text-md-left">
    <div class="row">
      <div class="col-md-3 mx-auto">
        <h5 class="font-weight-bold text-uppercase mt-3 mb-4">Links</h5>
        <ul class="list-unstyled">
          <li><a href="#!">Very long link 1</a></li>
          <li><a href="#!">Very long link 2</a></li>
          <li><a href="#!">Very long link 3</a></li>
          <li><a href="#!">Very long link 4</a></li>
        </ul>
      </div>

      ...

      <div class="col-md-3 mx-auto">
        <h5 class="font-weight-bold text-uppercase mt-3 mb-4">Links</h5>
        <ul class="list-unstyled">
          <li><a href="#!">Link 1</a></li>
          ...
        </ul>
      </div>

...

</footer>

I am exploring ways to highlight the navbar elements as I scroll down the page. After researching, I discovered that using HostListener might help achieve this effect:

@HostListener('window:scroll', ['$event'])
scrollHandler(event) {
console.log('Scroll Event');
const verticalOffset =
  window.pageYOffset ||
  document.documentElement.scrollTop ||
  document.body.scrollTop ||
  0;

console.log(verticalOffset);
}

Despite implementing some solutions, I'm still struggling to connect the scrolling behavior with specific divs. Any insights or suggestions would be greatly appreciated.

Answer №1

Depending on the complexity of your project, there are different ways to approach it. To start off in a simple manner, you can use ngClass to assign the "active" class based on the currentActive variable.

<li><a href="#" [ngClass]="{'active': currentActive === 1}">Home</a></li>
<li><a href="#news" [ngClass]="{'active': currentActive === 2}" (click)="scrollToElement(new)">News</a></li>
<li><a href="#contact" [ngClass]="{'active': currentActive === 3}" (click)="scrollToElement(con)">Contact</a></li>
<li><a href="#about" [ngClass]="{'active': currentActive === 4}" (click)="scrollToElement(about)">About</a></li>

To track our scroll position (YOffset), we can simply log it using HostListener as follows:

@HostListener('window:scroll', ['$event'])
checkOffsetTop() {
  console.log(window.pageYOffset);
}

We can also use @ViewChild decorator to access various elements and their offsets:

@ViewChild('home') homeElement: ElementRef;
@ViewChild('con') conElement: ElementRef;
@ViewChild('new') newElement: ElementRef;
@ViewChild('about') aboutElement: ElementRef;

To retrieve the offsets of these elements after they have been rendered, we can utilize ngAfterViewInit:

ngAfterViewInit() {
  this.homeOffset = this.homeElement.nativeElement.offsetTop;
  this.conOffset = this.conElement.nativeElement.offsetTop;
  this.newOffset = this.newElement.nativeElement.offsetTop;
  this.aboutOffset = this.aboutElement.nativeElement.offsetTop;
}

The full code snippet is shown below for reference:

This solution provides a basic implementation for your requirements, but the complexity may vary depending on the specific needs of your application/component. Nonetheless, I hope this helps clarify some aspects for you.

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

Unable to conceal HTML5 video in mobile devices through media query

I've been struggling to find a solution for hiding a video on mobile devices. Despite going through numerous posts and attempting different methods, I haven't been successful in using @media queries to hide the video and show an image instead. He ...

Struggling to link an external JavaScript file to your HTML document?

While attempting to run a firebase app locally, I encountered an error in the chrome console: GET http://localhost:5000/behaviors/signup.js net::ERR_ABORTED 404 (Not Found) Do I need to set firebase.json source and destination under rewrites or add a rout ...

How can I extract information from an HTML table using AngleSharp?

Seeking a way to extract song data from a playlist on a music streaming website This table contains song information: <tr class="song-row " data-id="ef713e30-ea6c-377d-a1a6-bc55ef61169c" data-song-type="7" data-subscription-links="true" data-index="0" ...

What is the process for transferring a file to a different directory?

<html> <head> <title>Main Page</title> </head> <body> <h2>Main Page</h2> <form method="post" action="index.php" enctype="multipart/form-data"> <input type="file" name="filename"> <input type=" ...

Angular HTTP event progress causing Bootstrap progress bar to not update automatically

I've been working on displaying the progress of my post request using HTTP Event and a bootstrap progress bar. The progress event is functioning correctly (I can see it in the console), but for some reason, the changes are not reflected in the progres ...

Exploring Child Types in Typescript and JSX minus the React framework

It seems like there's a missing piece of the puzzle that I can't quite figure out. Despite going through the documentation on JSX in non-React settings, I'm still unable to spot my mistake. Let's examine the following code: /** @jsx pra ...

The ngx-image-cropper's roundCropper attribute is not functioning correctly as it should. An error is being displayed stating: "Type 'string' is not assignable to type 'boolean'"

<image-cropper [imageChangedEvent]="imageChangedEvent" [maintainAspectRatio]="true" [aspectRatio]="4 / 4" format="jpg" (imageCropped)="imageCropped($event)" roundCropper = "true"> </i ...

What causes a webpage to overflow when using the position:absolute property?

Despite my understanding that an element positioned absolutely should be removed from the normal content flow, why does it still cause the page to overflow? Running the code snippet below reveals a horizontal scrollbar, which I didn't expect. .rela ...

The storing of data in an HTML form input

Within my submission form, there are five distinct inputs that users can enter and submit. Currently, the entered data is being written to a .txt file successfully. Nevertheless, the objective is to organize and display the information in an easily readabl ...

The ngModel in Angular 2 does not update when a selection is made

Two select options are available, where the second one is dependent on the first: <div class="form-group"> <label class="col-xs-12 col-sm-2 control-label">Vehicle Type:</label> <div class="col-xs-12 col-sm-10"> ...

What is the best way to include a button at the bottom of a Material UI table?

I've been working with Material UI in React TypeScript and I'm having trouble adding a button at the bottom that leads to a form. Despite my attempts, I haven't been successful. Can someone please help me with this? I just need a simple butt ...

What is the best way to show the previous month along with the year?

I need help with manipulating a date in my code. I have stored the date Nov. 1, 2020 in the variable fiscalYearStart and want to output Oct. 2020. However, when I wrote a function to achieve this, I encountered an error message: ERROR TypeError: fiscalYear ...

The date selected in matDatepicker does not match the formControl in Angular 8 when using Reactive Forms

https://i.stack.imgur.com/aHSyM.pngI am facing an issue with matDatepicker in Angular. The date retrieved from the API to populate my formControl using Reactive Forms is not matching the value displayed in matDatepicker. While the matDatePicker shows "12/3 ...

Which objects can be looped through in Aurelia templating?

In the documentation for Aurelia, it mentions that repeaters can be used with arrays and other iterable data types, including objects, as well as new ES6 standards like Map and Set. Map is usually recommended, as shown in the example below: <template&g ...

Exploring AngularJS: Retrieving a list of filenames from a specified directory

In the public directory of my application, there is a folder named 'x'. I have the path name to the folder (/path/to/dir/x) but I am unsure of the names of the files within it. How can I retrieve the file names and provide URL links to them in an ...

Ways to transfer an Object from a service to a component

I'm currently working on my website and trying to implement a cart feature where users can add items. To achieve this, I have created a service that contains the cart as an object called cart. The service has functions to add items to the cart and ret ...

Erase Typescript Service

To remove a PostOffice from the array based on its ID, you can use a checkbox to select the desired element and then utilize its ID for the delete function. Here is an example: let postOffices = [ {postOfficeID: 15, postCode: '3006&ap ...

What is the method for turning off Bootstrap for viewport width below a specific threshold?

I'm currently utilizing bootstrap for my website design. However, there's a particular CSS rule causing some frustration on one specific page: @media (min-width: 576px) .col-sm-4 { -ms-flex: 0 0 33.333333%; flex: 0 0 33.333333%; max-w ...

Controller function failing to trigger

I'm new to asking questions, so if I've made a mistake, please let me know. I've searched for an answer here but couldn't find one. Any help would be greatly appreciated. I'm attempting to load a list of "Countries" using a contro ...

Issues with jQuery animate not functioning properly when triggered on scroll position

I found a solution on Stack Overflow at this link, but I'm having trouble implementing it properly. The idea is to make an element change opacity from 0 to 1 when the page is scrolled to it, but it's not working as expected. The element is locat ...