21

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;
}
1
  • I want my map between my 2 toolbars and i just want, when i scroll down, that the second toolbar will fix under the first. You know ? thanks for your help Commented Oct 4, 2018 at 8:09

4 Answers 4

33

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;
}
Sign up to request clarification or add additional context in comments.

4 Comments

thank you a lot for your answer :) but i don't realy understand the solution. 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. you anderstand my question
Please notice the browser support of position sticky, caniuse.com/#search=sticky
The browser support looks good. Chrome update itself for everyone. But if you insist, you can simply add a polyfill.
Sticky always corresponds to the nearest scrolling ancenstor. E.g If you have a scrolling div like table or anything with overflow, then the position of the sticky element will be according to it and not based on body scroll.
17

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

Comments

7

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

3 Comments

I would like to recommend this method. Thank you @Can K.
Spacer doesn't work because it's height is always 0px: github.com/w11k/angular-sticky-things/issues/3
I would not recommend since in my case the sticky-thing works not smooth, custom js solution works fine
3
+25

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>

2 Comments

Please have look at position: sticky - developer.mozilla.org/en-US/docs/Web/CSS/position. This way, you don't need to use JS to achieve the sticky effect.
Have you seen the browser support? caniuse.com/#search=sticky Yea, this is a new feature and it's so great and easy to use. but only a few version of chrome support this!!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.