What is the best way to create a Material toolbar that changes from transparent to opaque when scrolling?

I'm in the process of familiarizing myself with Angular. I have incorporated Angular Material and I am trying to achieve a sticky and opaque material toolbar that becomes transparent with visible text when scrolling at the top of the page. Most solutions I found involved JavaScript or jQuery. How can I implement this specific functionality in Angular 8?

Below is my HTML & CSS code for reference:

<mat-toolbar color="primary">
  <a mat-button [routerLink]="['home']" >
    <h1>PETER<span class="light">CONSTRUCTION</span></h1>
  </a>
  <span class="spacer"></span>
  <a mat-button [routerLink]="['home']" routerLinkActive="active" >HOME</a>
  <a mat-button [routerLink]="['about']" routerLinkActive="active">ABOUT</a>
  <a mat-button [routerLink]="['contact']" routerLinkActive="active">CONTACT</a>
</mat-toolbar>

  mat-toolbar {
  position: absolute;
  z-index: 1;
  overflow-x: auto;
  background-color: #c3cfd2;

}

mat-toolbar-row {
  justify-content:space-between;

}

.spacer {
  flex: 1 1 auto;
}

a.active {
  background-color: rgba(0,0,0, 0.3);
}

h1 {
  margin: 0;
  color: black;
}

h1 .light {
  font-weight: 100;

}

Answer №1

To achieve this goal, there are various methods that can be utilized. However, if you are already utilizing @angular/material, you have the option to make use of @angular/cdk along with its ScrollDispatchModule (refer to the documentation).

This approach allows for a simple and efficient way to observe scroll events for registered elements, outside of NgZone, thereby minimizing performance impact.

Take a look at the sample stackblitz: https://stackblitz.com/edit/angular-npdbtp

Firstly, ensure to import the ScrollDispatchModule and register a provider for ScrollDispatcher:

import {ScrollDispatchModule, ScrollDispatcher} from '@angular/cdk/scrolling';

@NgModule({
  imports: [
    (other imports)
    ScrollDispatchModule
  ],
  providers: [ScrollDispatcher]
})
export class AppModule {}

Next, in your template, designate an html element with the cdkScrollable directive. This will automatically enlist it in the ScrollDispatcher. Additionally, you can bind the style of the component (e.g., opacity) to a property defined within your component:

<div class="scroll-wrapper" cdkScrollable>
  <mat-toolbar class="sticky-toolbar" [style.opacity]="opacity">My App</mat-toolbar>
  <div>content</div>
</div> 

To implement sticky behavior on an html element, utilize the display: sticky rule in conjunction with top: 0:

.sticky-toolbar {
  position: sticky;
  top: 0px;
}

Subsequently, inject the ScrollDispatcher and NgZone into your component while defining the opacity property:

  opacity = 1;
  constructor(
    private scrollDispatcher: ScrollDispatcher,
    private zone: NgZone
  ) {}

Then, subscribe to scrolled events emitted by the ScrollDispatcher. These events are triggered for all registered components. If necessary, also refer to the documentation for registering to scroll events of a specific element.

  ngOnInit(): void {
    this.scrollDispatcher.scrolled().subscribe((event: CdkScrollable) => {
      const scroll = event.measureScrollOffset("top");
      let newOpacity = this.opacity;

      if (scroll > 0) {
        newOpacity = 0.75;
      } else {
        newOpacity = 1;
      }

      if (newOpacity !== this.opacity) {
        this.zone.run(() => {
          this.opacity = newOpacity;
        });
      }
    });
  }

The ScrollDispatcher operates outside of NgZone, ensuring that change detection is not applied across the entire application. This results in enhanced performance, which is why we include NgZone and execute property changes within the zone to trigger proper change detection throughout the component tree.

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

How can we restrict the type without altering the original type?

import { CSSProperties } from 'react'; type StyleRulesType = Partial<CSSProperties> type StylesDefinition = { [key: string]: StyleRulesType }; const styles: StylesDefinition = { list: { position: 'relative', }, ...

Chrome browser alignment problems

Here are the lines in my code: <TD id=“avail_1” style=“display:none;availability:hidden”>UrgentAvail</TD> <TD id=“avail1_1” style=“display:none;availability:hidden”>substitutedBy</TD> When I test the application o ...

implementing a time picker feature with jQuery

As I am not familiar with jQuery, I came across this script while searching for a time picker. If anyone could guide me on how to incorporate this code into my HTML page to enable the time picker functionality, I would greatly appreciate it. /* jQuery t ...

Tips and techniques for updating the form value in Angular 4 Material while maintaining binding characteristics

import {Component,ViewChild} from '@angular/core'; import {NgForm} from '@angular/forms' @Component({ selector: 'checkbox-configurable-example', templateUrl: 'checkbox-configurable-example.html', styleUrls: [& ...

Using Typescript to create mapped types with optional parameters

I am currently working on implementing a mapped type that has some required parameters and some optional ones. While I have successfully implemented the required parameters, I am facing issues with utilizing the optional ones. type Foo = { foo: string } ...

CSS: Achieving vertical centering without specifying a fixed height using line-height. Is it possible?

Using line-height to vertically center text within an inline-element is a technique I often employ. Here's a demo I created: body, section { width: 100%; } section { background-color: lightGrey; } #wrap { margin: 30px auto; width: 100%; ...

trouble seeing images with an array input and ngFor in Angular

I am encountering issues while attempting to exhibit an array of images by their source link using ngFor. It seems like there are errors hindering the functionality! Below is the image HTML code located within my card component: <div class="Session-Pa ...

Removing nested divs using JavaScript

My website has a nested div structure which contains multiple child divs. Here is an example of the div structure: <div id="outside-one"> <div class="inside" id="1"></div> <div class="inside" id="2"></div> <div ...

Unable to open modal window in Angular 14 micro-frontend application

I've been working on a micro front end project and running into some issues with opening modal popup windows. I've tried both the Angular material and bootstrap approaches, but ran into problems with both. With Angular material, the popup window ...

Issues encountered with Angular project when deploying on NGINX across various directories

I have two angular projects that I want to deploy on my nginx server. The first project is the main website, and the second is the admin portal. I aim to make the main site accessible at www.example.com, and the admin portal at www.example.com/admin. I ha ...

Using JQuery's .mouseover and .mouseout methods to modify font colors on a webpage

Hi there, I'm new to JQuery and trying to experiment with some basic functionalities. I have a simple navigation menu created using an unordered list, and I want to change the font color of the currently hovered list item using JQuery. However, I&apos ...

I have developed a custom jQuery carousel that includes functionality to start and stop the carousel based on specific conditions

I have set up a jQuery carousel that moves to the left when a checkbox is checked, but I need it to stop moving when the checkbox is unchecked. Can someone help me achieve this? $("#checkBox").click(function(){ if($(this).prop("checked") == true){ ...

Retrieve route parameters in Angular 6 without using promises

I am currently utilizing Angular 6. When working with route parameters, I typically use the following code snippet: this.route.params.subscribe(params => { // params can now be utilized as an object variable }); However, I find myself needing to a ...

Properly arrange the positioning of floating HTML elements

I am currently using the float property to structure the layout. <style> div { float: left; } .pro { width : 100px; color : blue; } </style> <span> <div class="pro">Long property name : </div> <div>value&l ...

Understanding the integration of sass with webpack in an Angular 4 project

Is it possible to reference a sass file instead of a css file directly in the index.html? If so, how does webpack compile the sass into a css file? Additionally, what is the most effective way to bundle the sass file when building the application? The ve ...

Expanding Image with HTML and CSS: A Guide

In the process of creating my website, I encountered an issue with the logo placement. I wanted the logo to be in the top left corner, similar to the one shown in this image. Initially, everything was working fine until I made some adjustments to move the ...

a guide to effortlessly updating data in ng2-charts in real-time using Firebase

I am brand new to using angular2. My current challenge involves creating a bar chart with the ng2-charts library and connecting it to firebase through angularfire2. I have developed 2 components and a service that is responsible for sending and receiving d ...

What makes ngFor unique in Angular that allows it to not require keys like in Vue and React?

I recently delved into learning Angular a few weeks back. In Vue and React, we typically use a unique key when rendering an array of elements to optimize the rendering process, especially when there are changes in the elements' order or quantity. As a ...

Is there a way to make a picture fill the entire background?

Is there a way to make pictures expand across an entire page with different resolutions? I'm trying to achieve this effect but unsure how. Take a look at my current example: . ...

I am in the process of transitioning my JSX website to TSX and am struggling to figure out the proper placement of my type definitions

As the title suggests, I am in the process of migrating an application that I developed using the Google Maps API for rendering maps. In this app, I display information on maps and include functionality to zoom in when a user clicks on something. The erro ...