{"id":1051,"date":"2014-04-22T09:11:05","date_gmt":"2014-04-22T09:11:05","guid":{"rendered":"https:\/\/www.bronco.co.uk\/our-ideas\/?p=1051"},"modified":"2014-04-22T15:23:13","modified_gmt":"2014-04-22T15:23:13","slug":"building-a-sticky-sidebar-with-jquery","status":"publish","type":"post","link":"https:\/\/www.bronco.co.uk\/our-ideas\/building-a-sticky-sidebar-with-jquery\/","title":{"rendered":"Building a Sticky Sidebar with jQuery"},"content":{"rendered":"<p>I&#8217;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.<\/p>\n<p>There&#8217;s two reasons why I started to do this&#8230;<\/p>\n<ol>\n<li>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<\/li>\n<li>Plugins don&#8217;t always fit to your exact requirements and so the plugin influences how you approach the problem<\/li>\n<\/ol>\n<h2>Stickiness<\/h2>\n<p>One such function I&#8217;ve been refining this week makes a sidebar element track with the scrollbar; making it appear sticky. I&#8217;ve used the basis for this function a couple of times before but each time I come to reuse it I&#8217;ve ended up scratching my head trying to figure it out again.<\/p>\n<p>This time I wanted to simplify it, make it a little more reusable and also comment the code better.<\/p>\n<p><code>Position:fixed<\/code> is the simple way to fix something in relation to the browser window but often a website has headers and footers that you don&#8217;t want the fixed element overlapping. Also responsive design can add an additional complication; as it often does.<\/p>\n<h2>The Code<\/h2>\n<p>Below is the code needed to build the sidebar and get it all sticky, but for a working example go to our <a href=\"\/what-we-do\/google-penalty-recovery.html\">Google Penalty Recovery<\/a> page.<\/p>\n<pre class=\"lang:xhtml decode:true\">&lt;div class=\"twocol--outer\"&gt;\r\n   &lt;div class=\"twocol--main\"&gt;\r\n      Your Content\r\n   &lt;\/div&gt;\r\n&lt;\/div&gt;\r\n&lt;div class=\"twocol--side\"&gt;\r\n   &lt;div class=\"sidebar sticky\"&gt;              \r\n   &lt;\/div&gt;\r\n&lt;\/div&gt;<\/pre>\n<pre class=\"lang:default decode:true\">.twocol--outer      {float:left; margin-right:-280px; width:100%;}\r\n.twocol--main       {margin-right:280px; padding-right:60px;}\r\n.twocol--side       {float:right; width:280px;}\r\n\r\n.fixed      {position:fixed;}<\/pre>\n<pre class=\"lang:js decode:true\">$(document).ready(function(){\r\n\r\n\tvar $stickybox = $('.sticky'); \/\/ The element that needs to move\r\n\tvar $sibling = $('.twocol--main'); \/\/ The area the box needs to scroll against\r\n\tvar $margin = 40;\r\n\r\n\tfunction sticky(){\r\n\r\n\t\t$stickybox.removeClass('fixed'); \/\/ Reset class\r\n\t\t$stickybox.removeAttr('style'); \/\/ Reset style before adding new \r\n\t\t\/\/ Resets appear here so we can get the height and width as it is when not floating \r\n\r\n\t\tvar $stickyboxHeight = $stickybox.outerHeight(); \/\/ Height of element that needs to be sticky\r\n\t\tvar $stickyboxWidth = $stickybox.outerWidth(); \/\/ Width of element that needs to be sticky\r\n\r\n\t\tif($sibling.height() &gt; ($stickyboxHeight + $margin) &amp;&amp; $(window).height() &gt; ($stickyboxHeight + $margin) &amp;&amp; $stickybox.parent().css('float') != 'none'){ \r\n\t\t\/\/ If sibling is taller than box &amp;&amp; window is taller than box &amp;&amp; box parent is not floated (no point fixing if it's in a single column layout)\r\n\r\n\t\t\tvar $leftpos = $stickybox.offset().left; \/\/ Left Coords\r\n\r\n\t\t\tvar $startPoint = $sibling.offset().top - $margin; \/\/ Point it becomes fixed + default margin to top\r\n\t\t\tvar $stopPoint = ($sibling.offset().top + $sibling.height()) - ($stickyboxHeight + $margin); \/\/ Point it stops and tracks bottom border of sibling = bottom of sibling + (height of box + margin)\r\n\r\n\t\t\tif($(window).scrollTop() &gt; $startPoint){\r\n\r\n\t\t\t\t$stickybox.addClass('fixed'); \/\/ Add class\r\n\r\n\t\t\t\t$stickybox.css({\r\n\t\t\t\t\t'left' : $leftpos, \/\/ Set left position to match when not sticky \r\n\t\t\t\t\t'width' : $stickyboxWidth \/\/ Set width incase inherits width from parent\r\n\t\t\t\t});\r\n\r\n\t\t\t\tif($(window).scrollTop() &gt; $stopPoint){ \/\/ When sticky can't go any further\r\n\t\t\t\t\t$stickybox.css('top',($stopPoint - $(window).scrollTop() + $margin)); \/\/ top = bottom of sibling - amount scrolled + default margin\r\n\t\t\t\t}else{\r\n\t\t\t\t\t$stickybox.css('top',$margin + 'px'); \/\/ Default margin from the top when is sticky\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t}\t\t\r\n\r\n\tsticky(); \/\/ Run on load\r\n\twindow.onscroll = function(){\r\n\t\tsticky(); \/\/ Run on scroll\r\n\t}\r\n\twindow.onresize = function(){\r\n\t\tsticky(); \/\/ Run on resize\r\n\t}\r\n});<\/pre>\n<h2>What&#8217;s going on?<\/h2>\n<p>I&#8217;m hoping the comments within the code are enough to explain what&#8217;s going on rather that covering each line in detail. But as an overview the code has to do a few things:<\/p>\n<ol>\n<li>Run on load, scroll and resize \u2013 remember we&#8217;re responsive<\/li>\n<li>Look to a sibling (the main content area) for guidance as to when to start being sticky and when to stop<\/li>\n<li>Only move if the window is larger than the height of the element we want to fix<\/li>\n<li>Determine if the website is in a two column layout<\/li>\n<\/ol>\n<p>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 <a href=\"https:\/\/github.com\/weblinc\/media-match\" rel=\"nofollow\">Media.match<\/a> 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.<\/p>\n<h2>Go Play<\/h2>\n<p>Feel free to go and use the code in your own website, it&#8217;s not a one size fits all solution and there&#8217;s changes you&#8217;ll have to make to suit your own requirements. For example you&#8217;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&#8217;ve got a question, or an idea to improve the code add a comment below.<\/p>\n<p>Right now I&#8217;m just happy it works and it&#8217;s a lot less code than similar plugins.<\/p>\n<p>Image courtesy of <a href=\"https:\/\/www.flickr.com\/photos\/gabotoc\/\" rel=\"nofollow\">Gabriel Toro<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;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&#8217;s two reasons why I started to do this&#8230; Plugins have to deal with a lot [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1058,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"class_list":["post-1051","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-and-ux"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/posts\/1051","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/comments?post=1051"}],"version-history":[{"count":0,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/posts\/1051\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/media\/1058"}],"wp:attachment":[{"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/media?parent=1051"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/categories?post=1051"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}