How to fix a header on scroll

JqueryCssHeaderFixed

Jquery Problem Overview


I am creating a header that once scrolled to a certain amount of pixels it fixes and stays in place.

Can I do this using just css and html or do i need jquery too?

I have created a demo so you can understand. Any help would be great!

http://jsfiddle.net/gxRC9/4/

body{
     margin:0px;
     padding:0px;
}
 .clear{
     clear:both;
}
 .container{
     height:2000px;
}
 .cover-photo-container{
     width:700px;
     height: 348px;
     margin-bottom: 20px;
     background-color:red;
}
 .small-box{
     width:163px;
     height:100px;
     border:1px solid blue;
     float:left;
}
 .sticky-header{
     width:700px;
     height:50px;
     background:orange;
     postion:fixed;
}


Jquery Solutions


Solution 1 - Jquery

You need some JS to do scroll events. The best way to do this is to set a new CSS class for the fixed position that will get assigned to the relevant div when scrolling goes past a certain point.

HTML

<div class="sticky"></div>

CSS

.fixed {
    position: fixed;
    top:0; left:0;
    width: 100%; }

jQuery

$(window).scroll(function(){
  var sticky = $('.sticky'),
      scroll = $(window).scrollTop();

  if (scroll >= 100) sticky.addClass('fixed');
  else sticky.removeClass('fixed');
});

Example fiddle: http://jsfiddle.net/gxRC9/501/


EDIT: Extended example

If the trigger point is unknown but should be whenever the sticky element reaches the top of the screen, offset().top can be used.

var stickyOffset = $('.sticky').offset().top;

$(window).scroll(function(){
  var sticky = $('.sticky'),
      scroll = $(window).scrollTop();
    
  if (scroll >= stickyOffset) sticky.addClass('fixed');
  else sticky.removeClass('fixed');
});

Extended example fiddle: http://jsfiddle.net/gxRC9/502/

Solution 2 - Jquery

I have modified the Coop's answer. Please check the example FIDDLE Here's my edits:

$(window).scroll(function(){
  if ($(window).scrollTop() >= 330) {
    $('.sticky-header').addClass('fixed');
   }
   else {
    $('.sticky-header').removeClass('fixed');
   }
});

Solution 3 - Jquery

Coop's answer is excellent.
However it depends on jQuery, here is a version that has no dependencies:

HTML

<div id="sticky" class="sticky"></div>

CSS

.sticky {
  width: 100%
}

.fixed {
  position: fixed;
  top:0;
}

JS
(This uses eyelidlessness's answer for finding offsets in Vanilla JS.)

function findOffset(element) {
  var top = 0, left = 0;

  do {
    top += element.offsetTop  || 0;
    left += element.offsetLeft || 0;
    element = element.offsetParent;
  } while(element);

  return {
    top: top,
    left: left
  };
}

window.onload = function () {
  var stickyHeader = document.getElementById('sticky');
  var headerOffset = findOffset(stickyHeader);
  
  window.onscroll = function() {
    // body.scrollTop is deprecated and no longer available on Firefox
    var bodyScrollTop = document.documentElement.scrollTop || document.body.scrollTop;

    if (bodyScrollTop > headerOffset.top) {
      stickyHeader.classList.add('fixed');
    } else {
      stickyHeader.classList.remove('fixed');
    }
  };
};

Example

https://jsbin.com/walabebita/edit?html,css,js,output

Solution 4 - Jquery

I know Coop has already answered this question, but here is a version which also tracks where in the document the div is, rather than relying on a static value:

http://jsfiddle.net/gxRC9/16/

Javascript

var offset = $( ".sticky-header" ).offset();
var sticky = document.getElementById("sticky-header")

$(window).scroll(function() {
    
    if ( $('body').scrollTop() > offset.top){
        $('.sticky-header').addClass('fixed');
    } else {
         $('.sticky-header').removeClass('fixed');
    } 

});

CSS

.fixed{
     position: fixed;
    top: 0px;
}

Solution 5 - Jquery

Glorious, Pure-HTML/CSS Solution

In 2019 with CSS3 you can do this without Javascript at all. I frequently make sticky headers like this:

body {
  overflow-y: auto;
  margin: 0;
}

header {
  position: sticky; /* Allocates space for the element, but moves it with you when you scroll */
  top: 0; /* specifies the start position for the sticky behavior - 0 is pretty common */
  width: 100%;
  padding: 5px 0 5px 15px;
  color: white;
  background-color: #337AB7;
  margin: 0;
}

h1 {
  margin: 0;
}

div.big {
  width: 100%;
  min-height: 150vh;
  background-color: #1ABB9C;
  padding: 10px;
}

<body>
<header><h1>Testquest</h1></header>
<div class="big">Just something big enough to scroll on</div>
</body>

Solution 6 - Jquery

Just building on Rich's answer, which uses offset.

I modified this as follows:

  • There was no need for the var $sticky in Rich's example, it wasn't doing anything

  • I've moved the offset check into a separate function, and called it on document ready as well as on scroll so if the page refreshes with the scroll half-way down the page, it resizes straight-away without having to wait for a scroll trigger

     jQuery(document).ready(function($){
     	var offset = $( "#header" ).offset();
     	checkOffset();
    
     	$(window).scroll(function() {
     		checkOffset();
     	});
     	
     	function checkOffset() {
     		if ( $(document).scrollTop() > offset.top){
     			$('#header').addClass('fixed');
     		} else {
     			$('#header').removeClass('fixed');
     		} 
     	}
     	
     });
    

Solution 7 - Jquery

Coops answer is a good, simple solution, however, once you apply the fixed class the page becomes that much shorter and content will "jump" up, and if page is of a length where the scroll distance is less than the height of the header element, it will appear to jump and not let you see the bottom of the page.

The answer I found was to add a spacer div above the element that will become fixed (nav element in my case), and set it to the same height as the nav element, and set it to display none.

When adding the .fixed class to the nav, show the .nav-spacer div, and when removing it, hide it. Since the height of the page changes instantly I have set the duration to 0.

HTML

<header>the element above the element that will become fixed</header>
<div class="nav-spacer"></div>
<nav></nav>

CSS

nav {
    position: relative;    
    height: 100px;
}
.nav-spacer{
    position: relative;
    height: 100px;
    display: none;
}
.fixed {
	position: fixed;
	top:0;
	left:0;
	width: 100%;
    /* I like to add a shadow on to the fixed element */
	-webkit-box-shadow: 0px 5px 10px 1px rgba(0,0,0,0.25);
	-moz-box-shadow: 0px 5px 10px 1px rgba(0,0,0,0.25);
	box-shadow: 0px 5px 10px 1px rgba(0,0,0,0.25);
}

JavaScript

var stickyOffset = $('nav').offset().top;

$(window).scroll(function(){
	if ($(window).scrollTop() >= stickyOffset){
  		$('nav').addClass('fixed');
        //this makes the page length equal to what it was before fixing nav
  		$('.nav-spacer').show(0); 
  	}
  	else {
  		$('nav').removeClass('fixed');
  		$('.nav-spacer').hide(0);
	}
});

Solution 8 - Jquery

Can be done by using only CSS

.sticky-header {
  position: sticky;
  top: 0;
  background: orange;
}

body {
  margin: 0px;
  padding: 0px;
}

.clear {
  clear: both;
}

.container {
  height: 2000px;
}

.cover-photo-container {
  width: 700px;
  height: 100px;
  margin-bottom: 20px;
  background-color: red;
}

.small-box {
  width: 163px;
  height: 100px;
  border: 1px solid blue;
  float: left;
}

.sticky-header {
  position: sticky;
  top: 0;
  background: orange;
}

<div class="container">
  <div class="cover-photo-container">
    Please scroll
  </div>
  <div class="small-box"></div>
  <div class="small-box"></div>
  <div class="small-box"></div>
  <div class="small-box"></div>
  <div class="clear"></div>
  <div class="sticky-header">
    This needs to be fixed when hits top of screen
  </div>
</div>

Solution 9 - Jquery

Or just simply add a span tag with the height of the fixed header set as its height then insert it next to the sticky header:

$(function() {
  var $span_height = $('.fixed-header').height;
  var $span_tag = '<span style="display:block; height:' + $span_height + 'px"></span>';

  $('.fixed-header').after($span_tag);
});

Solution 10 - Jquery

 $(document).ready(function(){
    
    var div=$('#header');
    var start=$(div).offset().top;
    
    $.event.add(window,'scroll',function(){
    	var p=$(window).scrollTop();
    	$(div).css('position',(p>start)?'fixed':'static');
    	$(div).css('top',(p>start)?'0px':'');
    	
    });	
});

It works perfectly.

Solution 11 - Jquery

The chosen solution did not fit well in my page. So this is a more advanced version that works with bootstrap.

The javascript

var stickyOffset = $('.sticky-header').offset().top;

$(window).scroll(function () {
    var sticky = $('.sticky-header'),
        scroll = $(window).scrollTop(),
        header = $('.fixed-header-background');
    sticky.each(function() {
        var left = $(this).offset().left;
        $(this).data('left', left);//I store the left offset
    });

    if (scroll >= stickyOffset) {
        sticky.addClass('fixed');
        header.css('display', 'block');
        sticky.each(function() {
            $(this).css('left', $(this).data('left'));//I set the left offset
        });
    } else {
        sticky.removeClass('fixed');
        header.css('display', 'none');
        sticky.each(function () {
            $(this).css('left', '');//I remove the left offset
        });
    }
});

The CSS

.fixed-header-background {
    display: none;
     position: fixed;
    top: 50px;
    width: 100%;
    height: 30px;
    background-color: #fff;
    z-index: 5;
    border-bottom-style: solid;
    border-bottom-color: #dedede;
    border-bottom-width: 2px;
}

.fixed{
     position: fixed;
    top: 52px;
    z-index: 6;
}

And the HTML

    <div class="fixed-header-background"></div>
<table class="table table-hover table-condensed">
        <thead>
            <tr>
                <th></th>
                <th><span class="sticky-header">My header 1</span></th>
                <th><span class="sticky-header">My header 2</span></th>
            </tr>
        </thead>
        <tbody>
[....]

        </tbody>
    </table>

Solution 12 - Jquery

custom scroll Header Fixed in shopify:

$(window).scroll(function(){
  var sticky = $('.site-header'),
      scroll = $(window).scrollTop();

  if (scroll >= 100) sticky.addClass('fixed');
  else sticky.removeClass('fixed');
})


css:


header.site-header.border-bottom.logo--left.fixed {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 9;
}

Solution 13 - Jquery

Hopefully this one piece of an alternate solution will be as valuable to someone else as it was for me.

Situation:
In an HTML5 page I had a menu that was a nav element inside a header (not THE header but a header in another element).
I wanted the navigation to stick to the top once a user scrolled to it, but previous to this the header was absolute positioned (so I could have it overlay something else slightly).
The solutions above never triggered a change because .offsetTop was not going to change as this was an absolute positioned element. Additionally the .scrollTop property was simply the top of the top most element... that is to say 0 and always would be 0.
Any tests I performed utilizing these two (and same with getBoundingClientRect results) would not tell me if the top of the navigation bar ever scrolled to the top of the viewable page (again, as reported in console, they simply stayed the same numbers while scrolling occurred).

Solution
The solution for me was utilizing

window.visualViewport.pageTop

The value of the pageTop property reflects the viewable section of the screen, therefore allowing me to track where an element is in reference to the boundaries of the viewable area.
This allowed a simple function assigned to the scroll event of the window to detect when the top of the navigation bar intersected with the top of the viewable area and apply the styling to make it stick to the top.

Probably unnecessary to say, anytime I am dealing with scrolling I expect to use this solution to programatically respond to movement of elements being scrolled.
Hope it helps someone else.

Solution 14 - Jquery

The simplest way is: HTML:

<header>
        <h1>Website</h1>
</header>

CSS:

header{
    position: sticky;
    top: 0;
}

Solution 15 - Jquery

If you are using React I recently published a custom hook which enables you to do this. You need to supply a ref to the sticky element as well as a ref to the element you want it to stick to the top of. It handles the screen "jump" (as mentioned in other responses) for you as well.

const sticky = useRef<HTMLDivElement>(null)
const container = useRef<HTMLDivElement>(null)

useStickyScroll({
    element: sticky,
    container: container
})

Solution 16 - Jquery

My solution for show header if user scrolling document from bottom to top.

JS:

const isMobile = window.innerWidth < 768
const $header = $('#header')
let lastScrollTop = 0
if ( isMobile ) {
	$(window).scroll(function () {
		const scrollTop = window.pageYOffset || document.documentElement.scrollTop

		if ( scrollTop < lastScrollTop ) {
			$header.removeClass('header_top')
		} else {
			$header.addClass( 'header_top' )	
		}

		lastScrollTop = scrollTop <= 0 ? 0 : scrollTop
	})
}

CSS

.header {
    position: fixed;
    top: 0;
    z-index: 70;
    width: 100vw;
    transition: top .5s;
    height: 62px;
}
.header_top {
    top: -62px;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPaul DesignerView Question on Stackoverflow
Solution 1 - JqueryCaribouCodeView Answer on Stackoverflow
Solution 2 - JqueryMobeen AbdullahView Answer on Stackoverflow
Solution 3 - JqueryRubenSandwichView Answer on Stackoverflow
Solution 4 - JqueryRichView Answer on Stackoverflow
Solution 5 - Jqueryocket8888View Answer on Stackoverflow
Solution 6 - JquerycrdunstView Answer on Stackoverflow
Solution 7 - JqueryCraig JacobsView Answer on Stackoverflow
Solution 8 - JqueryGokul P PView Answer on Stackoverflow
Solution 9 - JqueryVictor OniView Answer on Stackoverflow
Solution 10 - JqueryarjunView Answer on Stackoverflow
Solution 11 - JqueryDanielView Answer on Stackoverflow
Solution 12 - Jquerysandeep kumarView Answer on Stackoverflow
Solution 13 - JqueryMERView Answer on Stackoverflow
Solution 14 - Jqueryuser11675948View Answer on Stackoverflow
Solution 15 - JquerykuerschView Answer on Stackoverflow
Solution 16 - JquerykostikovmuView Answer on Stackoverflow