Building a Sticky Sidebar with jQuery

I’ve got no clue as to when but at some point I reached a tipping point where I began writing more of my own JavaScript (well jQuery really) rather than taking the easy option and looking for a plugin.

There’s two reasons why I started to do this…

  1. Plugins have to deal with a lot of use-cases and so are often heavier. I need my websites to be as lightweight as possible
  2. Plugins don’t always fit to your exact requirements and so the plugin influences how you approach the problem

Stickiness

One such function I’ve been refining this week makes a sidebar element track with the scrollbar; making it appear sticky. I’ve used the basis for this function a couple of times before but each time I come to reuse it I’ve ended up scratching my head trying to figure it out again.

This time I wanted to simplify it, make it a little more reusable and also comment the code better.

Position:fixed is the simple way to fix something in relation to the browser window but often a website has headers and footers that you don’t want the fixed element overlapping. Also responsive design can add an additional complication; as it often does.

The Code

Below is the code needed to build the sidebar and get it all sticky, but for a working example go to our Google Penalty Recovery page.

<div class="twocol--outer">
   <div class="twocol--main">
      Your Content
   </div>
</div>
<div class="twocol--side">
   <div class="sidebar sticky">              
   </div>
</div>
.twocol--outer      {float:left; margin-right:-280px; width:100%;}
.twocol--main       {margin-right:280px; padding-right:60px;}
.twocol--side       {float:right; width:280px;}

.fixed      {position:fixed;}
$(document).ready(function(){

	var $stickybox = $('.sticky'); // The element that needs to move
	var $sibling = $('.twocol--main'); // The area the box needs to scroll against
	var $margin = 40;

	function sticky(){

		$stickybox.removeClass('fixed'); // Reset class
		$stickybox.removeAttr('style'); // Reset style before adding new 
		// Resets appear here so we can get the height and width as it is when not floating 

		var $stickyboxHeight = $stickybox.outerHeight(); // Height of element that needs to be sticky
		var $stickyboxWidth = $stickybox.outerWidth(); // Width of element that needs to be sticky

		if($sibling.height() > ($stickyboxHeight + $margin) && $(window).height() > ($stickyboxHeight + $margin) && $stickybox.parent().css('float') != 'none'){ 
		// If sibling is taller than box && window is taller than box && box parent is not floated (no point fixing if it's in a single column layout)

			var $leftpos = $stickybox.offset().left; // Left Coords

			var $startPoint = $sibling.offset().top - $margin; // Point it becomes fixed + default margin to top
			var $stopPoint = ($sibling.offset().top + $sibling.height()) - ($stickyboxHeight + $margin); // Point it stops and tracks bottom border of sibling = bottom of sibling + (height of box + margin)

			if($(window).scrollTop() > $startPoint){

				$stickybox.addClass('fixed'); // Add class

				$stickybox.css({
					'left' : $leftpos, // Set left position to match when not sticky 
					'width' : $stickyboxWidth // Set width incase inherits width from parent
				});

				if($(window).scrollTop() > $stopPoint){ // When sticky can't go any further
					$stickybox.css('top',($stopPoint - $(window).scrollTop() + $margin)); // top = bottom of sibling - amount scrolled + default margin
				}else{
					$stickybox.css('top',$margin + 'px'); // Default margin from the top when is sticky
				}

			}

		}

	}		

	sticky(); // Run on load
	window.onscroll = function(){
		sticky(); // Run on scroll
	}
	window.onresize = function(){
		sticky(); // Run on resize
	}
});

What’s going on?

I’m hoping the comments within the code are enough to explain what’s going on rather that covering each line in detail. But as an overview the code has to do a few things:

  1. Run on load, scroll and resize – remember we’re responsive
  2. Look to a sibling (the main content area) for guidance as to when to start being sticky and when to stop
  3. Only move if the window is larger than the height of the element we want to fix
  4. Determine if the website is in a two column layout

For the last of that list we determine a two column layout on whether or not an element is using the CSS float property. We could use Media.match to specify an exact screen size but that leads to repetition and extra overhead if changes are made to the breakpoint used at a later time.

Go Play

Feel free to go and use the code in your own website, it’s not a one size fits all solution and there’s changes you’ll have to make to suit your own requirements. For example you’ll need to tweak it if you want the element to appear fixed to the bottom of the window rather than the top. But if you’ve got a question, or an idea to improve the code add a comment below.

Right now I’m just happy it works and it’s a lot less code than similar plugins.

Image courtesy of Gabriel Toro


We'd love to hear from you!

If you think Bronco has the skills to take your business forward then what are you waiting for?

Get in Touch Today!

Discussion

Add a Comment