Angular 6 - CSS - STICKY Header

63,896

Solution 1

Before I answer your question, you may consider:

  • Remove static styles from your HTML.
  • Use reasonable z-index, so you won't end up with z-index of something like z-index: 100000000.
  • Use !important only when there's no other choice.

Since we can't write Angular code via StackOverflow's snippets, I wrote the code using Stackblitz - https://stackblitz.com/edit/angular-ii5tnn

To make a position sticky, well, you simply use position: sticky, with additional top or bottom rule (in this case - top). For example:

mat-toolbar {
  position: sticky;
  top: 50px
}

This way, mat-toolbar will remain at his position, until we pass it.

In my given example, I did:

  • Initialized new Angular 6 and added Material Angular.
  • Added mat-toolbar with [color="primary"] and set it to fixed via CSS.
  • Added #frugelmap with custom height just to show it.
  • Added mat-toolbar with [color="warn"] and set the sticky rules (watch below)
  • Added #add-spacing with lots of lorem ipsum just do demonstrate the sticky effect.

The following CSS rules:

mat-toolbar {
  --default-height: 50px;
}

mat-toolbar[color="primary"] {
  top: 0;
  position: fixed;
  height: var(--default-height);
}

mat-toolbar[color="warn"] {
  position: sticky;
  top: var(--default-height);
}

#frugalmap {
  height: 300px;
  background-color: #EEE;
}

Solution 2

To avoid the browser support concerns of position: sticky, you can easily achieve this by using ngClass to toggle sticky behaviour as:

component.html

<mat-toolbar color="primary" class="fixed-header" >
</mat-toolbar>

<div class="custom-popup" id="frugalmap" ></div>

<mat-toolbar
  id="secondToolbar" color="warn"
  [ngClass]="{'mat-elevation-z5' : true, 'sticky' : isSticky}">
</mat-toolbar>

usign HostListener to track scroll position as you should not use JS event handler directly in Angular:

component.ts

  isSticky: boolean = false;

  @HostListener('window:scroll', ['$event'])
  checkScroll() {
    this.isSticky = window.pageYOffset >= 250;
  }

finally adding style for our custom class sticky.

component.css

.fixed-header {
  position: fixed;
  z-index:999;
  height: 50px;
}

#frugalmap {
  height: 300px;
  width: 100%;
  top: 50px;
  position: relative;
}

.mat-elevation-z5 {
  position: relative;
}

.sticky {
  position: fixed;
  top: 50px;
}

Stackblitz Demo

Solution 3

Since the other answer mostly rely on CSS that is not available in all browsers I'll allow myself to advertise my lib angular-sticky-things.

It has a feature that is called 'boundary element' and you can see it in action in the above link. What you do is basically you slice your page in sections (what you usually already have) and then you tell an element to be sticky within the boundaries of the parent element.

Here you can see it in action:

<div #boundary style="height:1000px;">
  <div #spacer></div>
  <div stickyThing [spacer]="spacer" [boundary]="boundary">
    I am sticky but only inside #boundary!
  </div>
</div>

Just install the lib, add my code and replace the div with stickyThing with a mat-toolbar. That's basically it.

npm install @w11k/angular-sticky-things

Solution 4

I couldn't use the code of your question because I didn't have all of your code. So, I wrote you an example of what you want to do with you second toolbar.

My code is not in angular, but it has the same css styling and a Javascript event handler to add/remove a class to fix the second toolbar to the top. Just replace the elements with your own elements, classnames.

Notice

  • First of all, Take a look at this quesion CSS Sticky buttons div not working in IE 11.

  • In some use cases, your element might have a dynamic position and height and you have to get element.clientHeight to get the position of fixing your element. In this case, you have to use JS.

that you can use t

document.addEventListener("scroll", function(){
    var secondToolbar = document.querySelector('.toolbar-2');
    var map = document.querySelector('.map');

    if ((window.pageYOffset + 50) > (map.offsetTop + map.clientHeight))
      secondToolbar.classList.add('fixed');
    else
      secondToolbar.classList.remove('fixed');
});
body {
  margin: 0;
}

*{
  box-sizing: border-box;
}

.container {
  width: 300px;
  height: 1000px;
  padding-top: 50px;
}

.toolbar-1,
.toolbar-2,
.map {
  display: block;
  width: 300px;
  color: #aaa;
  text-align: center;
  padding: 15px;
  border: 1px solid #242424;
}

.toolbar-1 {
  position: fixed;
  height: 50px;
  top: 0;
  left: 0;
  background-color: #f8f8f8;
}

.toolbar-2 {
  height: 50px;
}

.toolbar-2.fixed{
  position: fixed;
  top: 50px;
  left: 0;
}

.map {
  height: 250px;
  background-color: #f8f8f8;
}
<div class="container">
  <div class="toolbar-1">First Toolbar</div>
  <div class="map">Map</div>
  <div class="toolbar-2">Second Toolbar</div>
</div>
Share:
63,896
Newbiiiie
Author by

Newbiiiie

Let's play !

Updated on July 27, 2022

Comments

  • Newbiiiie
    Newbiiiie almost 2 years

    I have 3 elements : 1 toolbar, 1 map , an other toolbar. the elements are one below the other

    I want that the second toolbar stay under the map element (at 400px of the top) but when i scroll down, my second toolbar will stop at 50px of the top and will fix under the first.

    Thanks for your help

    //Component.html

    <mat-toolbar color="primary" [ngStyle]="{'height':'50px'}"  class="fixed-header" >
    </mat-toolbar>
    
    <div class="custom-popup" id="frugalmap" ></div>
    
    <mat-toolbar color="warn" class="mat-elevation-z5">
    </mat-toolbar>
    

    //Component.css

    .fixed-header {
    position: fixed;
    z-index:999;
    }
    
    #frugalmap {
    height: 300px;
    width: 100%;
    margin-top:50px;
    }
    
    .mat-elevation-z5 {
    position: relative;
    z-index: 2;
    }