How can I make a div stick to the top of the screen once it's been scrolled to?

JavascriptJqueryCssScrollPositioning

Javascript Problem Overview


I would like to create a div, that is situated beneath a block of content but that once the page has been scrolled enough to contact its top boundary, becomes fixed in place and scrolls with the page.

Javascript Solutions


Solution 1 - Javascript

You could use simply css, positioning your element as fixed:

.fixedElement {
    background-color: #c0c0c0;
    position:fixed;
    top:0;
    width:100%;
    z-index:100;
}

Edit: You should have the element with position absolute, once the scroll offset has reached the element, it should be changed to fixed, and the top position should be set to zero.

You can detect the top scroll offset of the document with the scrollTop function:

$(window).scroll(function(e){ 
  var $el = $('.fixedElement'); 
  var isPositionFixed = ($el.css('position') == 'fixed');
  if ($(this).scrollTop() > 200 && !isPositionFixed){ 
    $el.css({'position': 'fixed', 'top': '0px'}); 
  }
  if ($(this).scrollTop() < 200 && isPositionFixed){
    $el.css({'position': 'static', 'top': '0px'}); 
  } 
});

When the scroll offset reached 200, the element will stick to the top of the browser window, because is placed as fixed.

Solution 2 - Javascript

You've seen this example on Google Code's issue page and (only recently) on Stack Overflow's edit page.

CMS's answer doesn't revert the positioning when you scroll back up. Here's the shamelessly stolen code from Stack Overflow:

function moveScroller() {
	var $anchor = $("#scroller-anchor");
	var $scroller = $('#scroller');
	
    var move = function() {
        var st = $(window).scrollTop();
        var ot = $anchor.offset().top;
        if(st > ot) {
            $scroller.css({
                position: "fixed",
                top: "0px"
            });
        } else {
            $scroller.css({
                position: "relative",
                top: ""
            });
        }
    };
    $(window).scroll(move);
    move();
}

<div id="sidebar" style="width:270px;"> 
  <div id="scroller-anchor"></div> 
  <div id="scroller" style="margin-top:10px; width:270px"> 
    Scroller Scroller Scroller
  </div>
</div>

<script type="text/javascript"> 
  $(function() {
    moveScroller();
  });
</script> 

And a simple live demo.

A nascent, script-free alternative is position: sticky, which is supported in Chrome, Firefox, and Safari. See the article on HTML5Rocks and demo, and Mozilla docs.

Solution 3 - Javascript

As of January 2017 and the release of Chrome 56, most browsers in common use support the position: sticky property in CSS.

#thing_to_stick {
  position: sticky;
  top: 0px;
}

does the trick for me in Firefox and Chrome.

In Safari you still need to use position: -webkit-sticky.

Polyfills are available for Internet Explorer and Edge; https://github.com/wilddeer/stickyfill seems to be a good one.

Solution 4 - Javascript

I had the same problem as you and ended up making a jQuery plugin to take care of it. It actually solves all the problems people have listed here, plus it adds a couple of optional features too.

Options

stickyPanelSettings = {
    // Use this to set the top margin of the detached panel.
    topPadding: 0,

    // This class is applied when the panel detaches.
    afterDetachCSSClass: "",

    // When set to true the space where the panel was is kept open.
    savePanelSpace: false,

    // Event fires when panel is detached
    // function(detachedPanel, panelSpacer){....}
    onDetached: null,

    // Event fires when panel is reattached
    // function(detachedPanel){....}
    onReAttached: null,

    // Set this using any valid jquery selector to 
    // set the parent of the sticky panel.
    // If set to null then the window object will be used.
    parentSelector: null
};

https://github.com/donnyv/sticky-panel

demo: http://htmlpreview.github.io/?https://github.com/donnyv/sticky-panel/blob/master/jquery.stickyPanel/Main.htm

Solution 5 - Javascript

And here's how without jquery (UPDATE: see other answers where you can now do this with CSS only)

var startProductBarPos=-1;
window.onscroll=function(){
  var bar = document.getElementById('nav');
  if(startProductBarPos<0)startProductBarPos=findPosY(bar);

  if(pageYOffset>startProductBarPos){
    bar.style.position='fixed';
    bar.style.top=0;
  }else{
    bar.style.position='relative';
  }

};

function findPosY(obj) {
  var curtop = 0;
  if (typeof (obj.offsetParent) != 'undefined' && obj.offsetParent) {
    while (obj.offsetParent) {
      curtop += obj.offsetTop;
      obj = obj.offsetParent;
    }
    curtop += obj.offsetTop;
  }
  else if (obj.y)
    curtop += obj.y;
  return curtop;
}

* {margin:0;padding:0;}
.nav {
  border: 1px red dashed;
  background: #00ffff;
  text-align:center;
  padding: 21px 0;

  margin: 0 auto;
  z-index:10; 
  width:100%;
  left:0;
  right:0;
}

.header {
  text-align:center;
  padding: 65px 0;
  border: 1px red dashed;
}

.content {
  padding: 500px 0;
  text-align:center;
  border: 1px red dashed;
}
.footer {
  padding: 100px 0;
  text-align:center;
  background: #777;
  border: 1px red dashed;
}

<header class="header">This is a Header</header>
<div id="nav" class="nav">Main Navigation</div>
<div class="content">Hello World!</div>
<footer class="footer">This is a Footer</footer>

Solution 6 - Javascript

This is how i did it with jquery. This was all cobbled together from various answers on stack overflow. This solution caches the selectors for faster performance and also solves the "jumping" issue when the sticky div becomes sticky.

Check it out on jsfiddle: http://jsfiddle.net/HQS8s/

CSS:

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

JS:

$(document).ready(function() {
    // Cache selectors for faster performance.
    var $window = $(window),
        $mainMenuBar = $('#mainMenuBar'),
        $mainMenuBarAnchor = $('#mainMenuBarAnchor');

    // Run this on scroll events.
    $window.scroll(function() {
        var window_top = $window.scrollTop();
        var div_top = $mainMenuBarAnchor.offset().top;
        if (window_top > div_top) {
            // Make the div sticky.
            $mainMenuBar.addClass('stick');
            $mainMenuBarAnchor.height($mainMenuBar.height());
        }
        else {
            // Unstick the div.
            $mainMenuBar.removeClass('stick');
            $mainMenuBarAnchor.height(0);
        }
    });
});

Solution 7 - Javascript

Here is another option:

JAVASCRIPT

var initTopPosition= $('#myElementToStick').offset().top;	
$(window).scroll(function(){
	if($(window).scrollTop() > initTopPosition)
		$('#myElementToStick').css({'position':'fixed','top':'0px'});
	else
		$('#myElementToStick').css({'position':'absolute','top':initTopPosition+'px'});
});

Your #myElementToStick should start with position:absolute CSS property.

Solution 8 - Javascript

As https://stackoverflow.com/users/19750/josh-lee">Josh Lee and https://stackoverflow.com/users/391445/colin-t-hart">Colin 't Hart have said, you could optionally just use position: sticky; top: 0; applying to the div that you want the scrolling at...

Plus, the only thing you will have to do is copy this into the top of your page or format it to fit into an external CSS sheet:

<style>
#sticky_div's_name_here { position: sticky; top: 0; }
</style>

Just replace #sticky_div's_name_here with the name of your div, i.e. if your div was <div id="example"> you would put #example { position: sticky; top: 0; }.

Solution 9 - Javascript

The simplest solution (without js) : demo

.container {
  position: relative;
}
.sticky-div {
    position: sticky;
    top: 0;
}
<div class="container">
  <h1>
    relative container & sticky div
  </h1>
  <div class="sticky-div"> this row is sticky</div>
  <div>
    content
  </div>
</div>

Solution 10 - Javascript

My solution is a little verbose, but it handles variable positioning from the left edge for centered layouts.

// Ensurs that a element (usually a div) stays on the screen
//   aElementToStick   = The jQuery selector for the element to keep visible
global.makeSticky = function (aElementToStick) {
    var $elementToStick = $(aElementToStick);
    var top = $elementToStick.offset().top;
    var origPosition = $elementToStick.css('position');
    
    function positionFloater(a$Win) {
        // Set the original position to allow the browser to adjust the horizontal position
        $elementToStick.css('position', origPosition);
        
        // Test how far down the page is scrolled
        var scrollTop = a$Win.scrollTop();
        // If the page is scrolled passed the top of the element make it stick to the top of the screen
        if (top < scrollTop) {
            // Get the horizontal position
            var left = $elementToStick.offset().left;
            // Set the positioning as fixed to hold it's position
            $elementToStick.css('position', 'fixed');
            // Reuse the horizontal positioning
            $elementToStick.css('left', left);
            // Hold the element at the top of the screen
            $elementToStick.css('top', 0);
        }
    }

    // Perform initial positioning
    positionFloater($(window));

    // Reposition when the window resizes
    $(window).resize(function (e) {
        positionFloater($(this));
    });

    // Reposition when the window scrolls
    $(window).scroll(function (e) {
        positionFloater($(this));
    });
};

Solution 11 - Javascript

Here's one more version to try for those having issues with the others. It pulls together the techniques discussed in this duplicate question, and generates the required helper DIVs dynamically so no extra HTML is required.

CSS:

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

JQuery:

function make_sticky(id) {
	var e = $(id);
	var w = $(window);
	$('<div/>').insertBefore(id);
	$('<div/>').hide().css('height',e.outerHeight()).insertAfter(id);
	var n = e.next();
	var p = e.prev();
	function sticky_relocate() {
	  var window_top = w.scrollTop();
	  var div_top = p.offset().top;
	  if (window_top > div_top) {
		e.addClass('sticky');
		n.show();
	  } else {
		e.removeClass('sticky');
		n.hide();
	  }
	}
	w.scroll(sticky_relocate);
	sticky_relocate();
}

To make an element sticky, do:

make_sticky('#sticky-elem-id');

When the element becomes sticky, the code manages the position of the remaining content to keep it from jumping into the gap left by the sticky element. It also returns the sticky element to its original non-sticky position when scrolling back above it.

Solution 12 - Javascript

Here is an extended version to Josh Lee's answer. If you want the div to be on sidebar to the right, and float within a range (i.e., you need to specify top and bottom anchor positions). It also fixes a bug when you view this on mobile devices (you need to check left scroll position otherwise the div will move off screen).

function moveScroller() {
    var move = function() {
        var st = $(window).scrollTop();
        var sl = $(window).scrollLeft();
        var ot = $("#scroller-anchor-top").offset().top;
        var ol = $("#scroller-anchor-top").offset().left;
        var bt = $("#scroller-anchor-bottom").offset().top;
        var s = $("#scroller");
        if(st > ot) {
        	if (st < bt - 280) //280px is the approx. height for the sticky div
        	{
                s.css({
                    position: "fixed",
                    top: "0px",
                    left: ol-sl
                });	
        	}
        	else
        	{
                s.css({
                    position: "fixed",
                    top: bt-st-280,
                    left: ol-sl
                });	
        	}
        } else {
            s.css({
                position: "relative",
                top: "",
                left: ""
            });
 
        }
    };
    $(window).scroll(move);
    move();
}

Solution 13 - Javascript

I came across this when searching for the same thing. I know it's an old question but I thought I'd offer a more recent answer.

Scrollorama has a 'pin it' feature which is just what I was looking for.

http://johnpolacek.github.io/scrollorama/

Solution 14 - Javascript

The info provided to answer this other question may be of help to you, Evan:

https://stackoverflow.com/questions/487073/jquery-check-if-element-is-visible-after-scroling

You basically want to modify the style of the element to set it to fixed only after having verified that the document.body.scrollTop value is equal to or greater than the top of your element.

Solution 15 - Javascript

The accepted answer works but doesn't move back to previous position if you scroll above it. It is always stuck to the top after being placed there.

  $(window).scroll(function(e) {
	$el = $('.fixedElement');
	if ($(this).scrollTop() > 42 && $el.css('position') != 'fixed') {
	  $('.fixedElement').css( 'position': 'fixed', 'top': '0px');
	
	} else if ($(this).scrollTop() < 42 && $el.css('position') != 'relative') {
	  $('.fixedElement').css( 'relative': 'fixed', 'top': '42px');
//this was just my previous position/formating
	}
  });

jleedev's response whould work, but I wasn't able to get it to work. His example page also didn't work (for me).

Solution 16 - Javascript

You can add 3 extra rows so when the user scroll back to the top, the div will stick on its old place:

Here is the code:

if ($(this).scrollTop() < 200 && $el.css('position') == 'fixed'){
    $('.fixedElement').css({'position': 'relative', 'top': '200px'});
}

Solution 17 - Javascript

I have links setup in a div so it is a vertical list of letter and number links.

#links {
	float:left;
	font-size:9pt;
	margin-left:0.5em;
	margin-right:1em;
	position:fixed;
	text-align:center;
	width:0.8em;
}

I then setup this handy jQuery function to save the loaded position and then change the position to fixed when scrolling beyond that position.

NOTE: this only works if the links are visible on page load!!

var listposition=false;
jQuery(function(){
     try{
        ///// stick the list links to top of page when scrolling
        listposition = jQuery('#links').css({'position': 'static', 'top': '0px'}).position();
        console.log(listposition);
        $(window).scroll(function(e){
            $top = $(this).scrollTop();
            $el = jQuery('#links');
            //if(typeof(console)!='undefined'){
            //    console.log(listposition.top,$top);
            //}
            if ($top > listposition.top && $el.css('position') != 'fixed'){
                $el.css({'position': 'fixed', 'top': '0px'});
            }
            else if ($top < listposition.top && $el.css('position') == 'fixed'){
                $el.css({'position': 'static'});
            }
        });

    } catch(e) {
        alert('Please vendor [email protected] (Myvendor JavaScript Issue)');
    }
});

Solution 18 - Javascript

I used some of the work above to create this tech. I improved it a bit and thought I would share my work. Hope this helps.

jsfiddle Code

function scrollErrorMessageToTop() {
    var flash_error = jQuery('#flash_error');
    var flash_position = flash_error.position();

    function lockErrorMessageToTop() {
        var place_holder = jQuery("#place_holder");
        if (jQuery(this).scrollTop() > flash_position.top && flash_error.attr("position") != "fixed") {
            flash_error.css({
                'position': 'fixed',
                'top': "0px",
                "width": flash_error.width(),
                "z-index": "1"
            });
            place_holder.css("display", "");
        } else {
            flash_error.css('position', '');
            place_holder.css("display", "none");
        }

    }
    if (flash_error.length > 0) {
        lockErrorMessageToTop();

        jQuery("#flash_error").after(jQuery("<div id='place_holder'>"));
        var place_holder = jQuery("#place_holder");
        place_holder.css({
            "height": flash_error.height(),
            "display": "none"
        });
        jQuery(window).scroll(function(e) {
            lockErrorMessageToTop();
        });
    }
}
scrollErrorMessageToTop();​

This is a little bit more dynamic of a way to do the scroll. It does need some work and I will at some point turn this into a pluging but but this is what I came up with after hour of work.

Solution 19 - Javascript

In javascript you can do:

var element = document.getElementById("myid");
element.style.position = "fixed";
element.style.top = "0%";

Solution 20 - Javascript

Not an exact solution but a great alternative to consider

this CSS ONLY Top of screen scroll bar. Solved all the problem with ONLY CSS, NO JavaScript, NO JQuery, No Brain work (lol).

Enjoy my fiddle :D all the codes are included in there :)

CSS

#menu {
position: fixed;
height: 60px;
width: 100%;
top: 0;
left: 0;
border-top: 5px solid #a1cb2f;
background: #fff;
-moz-box-shadow: 0 2px 3px 0px rgba(0, 0, 0, 0.16);
-webkit-box-shadow: 0 2px 3px 0px rgba(0, 0, 0, 0.16);
box-shadow: 0 2px 3px 0px rgba(0, 0, 0, 0.16);
z-index: 999999;
}
 
.w {
    width: 900px;
    margin: 0 auto;
    margin-bottom: 40px;
}<br type="_moz">

Put the content long enough so you can see the effect here :) Oh, and the reference is in there as well, for the fact he deserve his credit

CSS ONLY Top of screen scroll bar

Solution 21 - Javascript

Here's an example that uses jquery-visible plugin: http://jsfiddle.net/711p4em4/.

HTML:

<div class = "wrapper">
    <header>Header</header>
    <main>
        <nav>Stick to top</nav>
        Content
    </main>
    <footer>Footer</footer>
</div>

CSS:

* {
    margin: 0;
    padding: 0;
}

body {
    background-color: #e2e2e2;
}

.wrapper > header,
.wrapper > footer {
    font: 20px/2 Sans-Serif;
    text-align: center;
    background-color: #0040FF;
    color: #fff;
}

.wrapper > main {
    position: relative;
    height: 500px;
    background-color: #5e5e5e;
    font: 20px/500px Sans-Serif;
    color: #fff;
    text-align: center;
    padding-top: 40px;
}

.wrapper > main > nav {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    font: 20px/2 Sans-Serif;
    color: #fff;
    text-align: center;
    background-color: #FFBF00;
}

.wrapper > main > nav.fixed {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
}

JS (include jquery-visible plugin):

(function($){

    /**
     * Copyright 2012, Digital Fusion
     * Licensed under the MIT license.
     * http://teamdf.com/jquery-plugins/license/
     *
     * @author Sam Sehnert
     * @desc A small plugin that checks whether elements are within
     *       the user visible viewport of a web browser.
     *       only accounts for vertical position, not horizontal.
     */
    var $w = $(window);
    $.fn.visible = function(partial,hidden,direction){

        if (this.length < 1)
            return;

        var $t        = this.length > 1 ? this.eq(0) : this,
            t         = $t.get(0),
            vpWidth   = $w.width(),
            vpHeight  = $w.height(),
            direction = (direction) ? direction : 'both',
            clientSize = hidden === true ? t.offsetWidth * t.offsetHeight : true;

        if (typeof t.getBoundingClientRect === 'function'){

            // Use this native browser method, if available.
            var rec = t.getBoundingClientRect(),
                tViz = rec.top    >= 0 && rec.top    <  vpHeight,
                bViz = rec.bottom >  0 && rec.bottom <= vpHeight,
                lViz = rec.left   >= 0 && rec.left   <  vpWidth,
                rViz = rec.right  >  0 && rec.right  <= vpWidth,
                vVisible   = partial ? tViz || bViz : tViz && bViz,
                hVisible   = partial ? lViz || rViz : lViz && rViz;

            if(direction === 'both')
                return clientSize && vVisible && hVisible;
            else if(direction === 'vertical')
                return clientSize && vVisible;
            else if(direction === 'horizontal')
                return clientSize && hVisible;
        } else {

            var viewTop         = $w.scrollTop(),
                viewBottom      = viewTop + vpHeight,
                viewLeft        = $w.scrollLeft(),
                viewRight       = viewLeft + vpWidth,
                offset          = $t.offset(),
                _top            = offset.top,
                _bottom         = _top + $t.height(),
                _left           = offset.left,
                _right          = _left + $t.width(),
                compareTop      = partial === true ? _bottom : _top,
                compareBottom   = partial === true ? _top : _bottom,
                compareLeft     = partial === true ? _right : _left,
                compareRight    = partial === true ? _left : _right;

            if(direction === 'both')
                return !!clientSize && ((compareBottom <= viewBottom) && (compareTop >= viewTop)) && ((compareRight <= viewRight) && (compareLeft >= viewLeft));
            else if(direction === 'vertical')
                return !!clientSize && ((compareBottom <= viewBottom) && (compareTop >= viewTop));
            else if(direction === 'horizontal')
                return !!clientSize && ((compareRight <= viewRight) && (compareLeft >= viewLeft));
        }
    };

})(jQuery);

$(function() {
    $(window).scroll(function() {
        $(".wrapper > header").visible(true) ?
            $(".wrapper > main > nav").removeClass("fixed") :
            $(".wrapper > main > nav").addClass("fixed");
    });
});

Solution 22 - Javascript

sticky till the footer hits the div:

function stickyCostSummary() {
    var stickySummary = $('.sticky-cost-summary');
    var scrollCostSummaryDivPosition = $(window).scrollTop();
    var footerHeight = $('#footer').height();
    var documentHeight = $(document).height();
    var costSummaryHeight = stickySummary.height();
    var headerHeight = 83;
    var footerMargin = 10;
    var scrollHeight = 252;
    var footerPosition = $('#footer').offset().top;
    
    if (scrollCostSummaryDivPosition > scrollHeight && scrollCostSummaryDivPosition <= (documentHeight - footerHeight - costSummaryHeight - headerHeight - footerMargin)) {
        stickySummary.removeAttr('style');
        stickySummary.addClass('fixed');
        
    } else if (scrollCostSummaryDivPosition > (documentHeight - footerHeight - costSummaryHeight - headerHeight - footerMargin)) {
        stickySummary.removeClass('fixed');
        stickySummary.css({
          "position" : "absolute",
          "top" : (documentHeight - footerHeight - costSummaryHeight - headerHeight - footerMargin - scrollHeight) + "px"
      });
    } else {
        stickySummary.removeClass('fixed');
        stickySummary.css({
            "position" : "absolute",
            "top" : "0"
        });
    }
}

$window.scroll(stickyCostSummary);

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
QuestionevanrView Question on Stackoverflow
Solution 1 - JavascriptChristian C. SalvadóView Answer on Stackoverflow
Solution 2 - JavascriptJosh LeeView Answer on Stackoverflow
Solution 3 - JavascriptColin 't HartView Answer on Stackoverflow
Solution 4 - JavascriptDonny V.View Answer on Stackoverflow
Solution 5 - JavascriptJim W says reinstate MonicaView Answer on Stackoverflow
Solution 6 - JavascriptJohnBView Answer on Stackoverflow
Solution 7 - JavascriptkundeView Answer on Stackoverflow
Solution 8 - JavascriptKalamariKingView Answer on Stackoverflow
Solution 9 - JavascriptMarzieh MousaviView Answer on Stackoverflow
Solution 10 - JavascriptJosh RussoView Answer on Stackoverflow
Solution 11 - JavascriptDanView Answer on Stackoverflow
Solution 12 - JavascriptrealwecanView Answer on Stackoverflow
Solution 13 - JavascriptpbrydView Answer on Stackoverflow
Solution 14 - JavascriptAmberView Answer on Stackoverflow
Solution 15 - JavascriptForgeView Answer on Stackoverflow
Solution 16 - JavascriptAlex RashkovView Answer on Stackoverflow
Solution 17 - JavascriptArtistanView Answer on Stackoverflow
Solution 18 - Javascriptnewdark-itView Answer on Stackoverflow
Solution 19 - JavascriptAsherView Answer on Stackoverflow
Solution 20 - Javascriptweia designView Answer on Stackoverflow
Solution 21 - JavascriptDRDView Answer on Stackoverflow
Solution 22 - JavascriptOttensView Answer on Stackoverflow