Create a Sidebar that Sticks Within an Element

You’ve probably seen this trick used before with social icons or a sidebar that sticks to the top of your screen as you scroll down the page.

The problem is that often they don’t stop scrolling and up either disappearing behind another element or overlapping something they shouldn’t, which looks cheap and unprofessional.

In this tutorial we’ll create a ‘sticky element’ that only scrolls until the maximum height of it’s parent element which will prevent that unsightly overflow.

Let’s start with a very basic html structure:

<style>
  body{
    border: 0;
    margin: 0 auto;
    padding: 0;
    width: 960px;
  }
  #header{
    background: #faa;
    height: 600px;
  }
  #footer{
    background: #aaf;
    height: 1000px;
  }
  #main{
    background: #afa;
    height: 1800px;
    position: relative;
  }
    #container{
       background: none repeat scroll 0 0 orange;
       height: 400px;
       position: absolute;
       right: 10px;
       top: 10px;
       width: 200px;
    }
</style>
<div id="header"></div>
<div id="main">
	<div id="container"></div>
</div>
<div id="footer"></div>

Sticky Sidebar

At this stage it leaves alot to the imagination in terms of design but it’s sufficient for the tutorial.

Our aim is to move the orange container down with the page, but to stop it before it collides with the purple container.


We’ll do this using, JQuery so be sure to include the jQuery script to your site.

jQuery(document).ready(function($){
  //customizable variables
  /* used too offset the window min and max for processing the scroll */
  var threshold_offset = 50;
  /* the element to scroll */
  var $container = $("#container");
  /* the element's container (which its to scroll within) */
  var $main = $("#main");
  //leave these ones.
  var $window = $(window);
  var window_min = 0;
  var window_max = 0;
});

The first step is to set up the basic JQuery structure and the initial variables:

The first step is to work out what the maximum and minimum scroll limits are for the orange container

i.e. how far can you scroll down or up before it has to stop.

Getting the minimum is easy, you just need to take it’s parent’s offset().top and add it to the elements CSS top.

The bottom is slightly more complex.

You need to take the parent’s offset top, add it to the parent’s height, then take away the container’s height and 2x the containers CSS top (to enable the padding in at the bottom too).

It looks like this:

Sticky Sidebar

/*
 set the container's maximum and minimum limits as well as movement thresholds
*/
function set_limits(){
  //max and min container movements
  var max_move = $main.offset().top + $main.height() - $container.height() - 2*parseInt($container.css("top") );
  var min_move = $main.offset().top;

  //save them
  $container.attr("data-min", min_move).attr("data-max",max_move);

  //window thresholds so the movement isn't called when its not needed!
  //you may wish to adjust the freshhold offset
  window_min = min_move - threshold_offset;
  window_max = max_move + $container.height() + threshold_offset;
}
//sets the limits for the first load
set_limits();

Now we have the limits set, we need to handle the page scroll.

It is a good idea to leave the scroll handling function to only run if the page is within a certain range, this is where we make use of the window_max, window_min and threshold_offset variables. The window_min requires the window to be scrolled down more than it’s value and window_max requires that the window not be scrolled down any futher than it’s value before the scroll event is even considered, this will hopefully reduce some of the load off the parser.

We need to bind the window’s scroll event to a function to check these limits:

function window_scroll(){
  //if the window is within the threshold, begin movements
  if( $window.scrollTop() >= window_min && $window.scrollTop() < window_max ){
    //window scroll is within range
    //reset the limits (optional)
    set_limits();
    //move the container
    container_move();
  }
}
$window.bind("scroll", window_scroll);

The actual moving of the container, as noted above, is done by our last function “container_move” which handles the last of the logic. This function will check to see exactly where the window is positioned in relation fo the container’s min and max amounts set in set_limits(), and then apply the appropriate margin to make the move.

/**
 * Handles moving the container if needed.
**/
function container_move(){
  var wst = $window.scrollTop();

  //if the window scroll is within the min and max (the container will be "sticky"
  if( wst >= $container.attr("data-min") && wst<= $container.attr("data-max") ){

    //work out the margin offset
    var margin_top = $window.scrollTop() - $container.attr("data-min");

    //margin it down!
    $container.css("margin-top", margin_top);

  //if the window scroll is below the minimum
  }else if( wst <= $container.attr("data-min") ){

    //fix the container to the top.
    $container.css("margin-top",0);

  //if the window scroll is above the maximum
  }else if( wst > $container.attr("data-max") ){

    //fix the container to the top
    $container.css("margin-top", $container.attr("data-max")-$container.attr("data-min")+"px" );

  }
}
//do one container move on page load
container_move();

Now if you put all the code together and preview your page, you should see an orange container that sits insider the green element all the way down the page until it is 10px away from the bottom of the purple container, at which point it sticks there.

A full working example of this tutorial can be found here: http://codepen.io/hitreach/pen/bLmrC

If you enjoyed this tutorial or would like any help please let me know in the comments below!

About the Author:

Chris Gilchrist is the MD & Founder of Hit Reach, a web design & SEO agency with it’s head offices in Dundee, Scotland. You can connect with Chris on Twitter and on .

Looking for hosting? WPEngine offers secure managed WordPress hosting. You’ll get expert WordPress support, automatic backups, and caching for fast page loads.

5 Responses

Comments are now closed on this post.

  • Dan, May 22, 2013

    I don’t think you would see any issue in this situation, but as a best practice it’s always a good idea to use a radix with parseInt:

    var max_move = $main.offset().top + $main.height() – $container.height() – 2*parseInt($container.css(“top”), 10);

    http://davidwalsh.name/parseint-radix

  • andrei, May 24, 2013

    i agree with Dan. that the right parse

  • umair, May 25, 2013

    Basic but very good.

  • peter, May 27, 2013

    Instead of doing this crazy method, why can’t you just get the offset.top of the next dom and use it as the max-stop?

  • Web Designer Help, June 4, 2013

    Simple and nice…thanks for this.

Close