Coda Tabs Slider Effect jQuery
Recreating this effect is simple to do if you know what plugins to use. There are plugins out in the wild already, but we want our jQuery to satisfy the following requirements:
- Degrades perfectly without JavaScript enabled
- Sliding panels effect without hogging browser CPU
- Next and previous buttons added using JavaScript because they hold no use without JavaScript
- Hitting the page with a specific hash (i.e. page.html#preview) shows the right tab, and highlights the right navigation
- Any link on the page that refers back to a panel should trigger the effect and highlight the right navigation - this should happen without any extra work
Markup
Requirement 1: degrades perfectly without JavaScript enabled
To ensure this works without JavaScript, we will use the CSS overflow property to control the effect.
This way our panels will still focus in to view if a user clicks, or if the URL refers to a specific panel.
HTML
It’s worth noting that although we could scroll the individual panels inside a single nested DIV, for the JavaScript to make the effect work, it will be a nested DIV that will scroll - not the individual panels. You can see this in the markup below:
<div id="slider"> <ul class="navigation"> <li><a href="#sites">Sites</a></li> <li><a href="#files">Files</a></li> <li><a href="#editor">Editor</a></li> <li><a href="#preview">Preview</a></li> <li><a href="#css">CSS</a></li> <li><a href="#terminal">Terminal</a></li> <li><a href="#books">Books</a></li> </ul> <!-- element with overflow applied --> <div class="scroll"> <!-- the element that will be scrolled during the effect --> <div class="scrollContainer"> <!-- our individual panels --> <div class="panel" id="sites"> ... </div> <div class="panel" id="files"> ... </div> <div class="panel" id="editor"> ... </div> <div class="panel" id="preview"> ... </div> <div class="panel" id="css"> ... </div> <div class="panel" id="terminal"> ... </div> <div class="panel" id="books"> ... </div> </div> </div> </div>
That’s it. Without CSS this markup works perfectly for our content, almost exactly the way tabs work without JavaScript and CSS.
CSS
I won’t detail all the CSS like the navigation or shading effects - just the CSS required to correctly create the effect.
#slider { width: 620px; margin: 0 auto; position: relative; } .scroll { height: 250px; overflow: auto; position: relative; /* fix for IE to respect overflow */ clear: left; background: #FFFFFF url(images/content_pane-gradient.gif) repeat-x scroll left bottom; } .scrollContainer div.panel { padding: 20px; height: 210px; width: 580px; /* change to 560px if not using JS to remove rh.scroll */ }
Note also that I’m choosing to use overflow: auto;
- this way, if the user does have JavaScript disabled, there will be a visual que that the panels can be scrolled. If you want to remove the horizontal scroll in IE, I would recommend changing .scrollContainer div.panel
’s width to 560px to account for the right hand scrollbar.
I also plan to include scrolling buttons to go left and right. Although the elements will be created by the JavaScript, we’ll still need the CSS to place the buttons in the right place. Our JavaScript will put the buttons before and after the outer div.scroll
element. We have to use absolute positioning to place them, so this is why #slider
has position: relative;
applied:
.scrollButtons { position: absolute; top: 150px; cursor: pointer; } .scrollButtons.left { left: -20px; } .scrollButtons.right { right: -20px; }
jQuery
Requirement 2: Sliding panels effect without hogging browser CPU
If you try this effect using CSS, to use positioning to move the panels around it will eat up your CPU, and the browser will no likely lock up. This is why we’re using Ariel’s scrollTo pluing. The scrollTo plugin literally scrolls the element via it’s overflow (or can be used to scroll the actual window).
Required Plugins
<script src="jquery-1.2.6.min.js" type="text/javascript"></script> <script src="jquery.scrollTo-1.3.3-min.js" type="text/javascript"></script> <script src="jquery.localscroll-1.2.5-min.js" type="text/javascript"></script> <script src="jquery.serialScroll-1.2.1-min.js" type="text/javascript"></script>
Next and Previous Buttons
Requirement 3: Back and forward buttons added
This is easy vanilla jQuery, here’s the snippet from the final code:
var $scroll = $('#slider .scroll'); $scroll .before('<img class="scrollButtons left" src="images/scroll_left.png" />') .after('<img class="scrollButtons right" src="images/scroll_right.png" />');
Note the classes I’ve applied the scrollButtons
to the images so they’ll be positioned properly.
Navigation Updates Automatically
Requirement 4: Hitting the page with a specific hash highlights the right navigation
This is one of the key features that I felt was important to the slider. If I navigated to a specific panel, the navigation should match.
This is achieved using event binding. When the scroll plugin completes it’s effect, it will call our trigger function.
The trigger function will receive the ID of the element it has just shown, and search for the navigation item whose href’s hash matches the ID.
For example, if the div#sites
element is shown, the ID ’sites’ is sent to our trigger. Our trigger function will now look for links in the navigation that end with ‘#sites’: <a href="#sites">Sites</a>
.
This trigger function will be attached to the onAfter
property in the scroll options and called when the page loads for the first time with a hash in the URL.
// bind the navigation clicks to update the selected nav: $('#slider .navigation').find('a').click(selectNav); // handle nav selection - lots of nice chaining :-) function selectNav() { $(this) .parents('ul:first') // find the first UL parent .find('a') // find all the A elements .removeClass('selected') // remove from all .end() // go back to all A elements .end() // go back to 'this' element .addClass('selected'); } function trigger(data) { // within the .navigation element, find the A element // whose href ends with ID ($= is ends with) var el = $('#slider .navigation').find('a[href$="' + data.id + '"]').get(0); // we're passing the actual element, and not the jQuery instance. selectNav.call(el); }
I’ve had to separate out the selectNav
function because it also be called when the user clicks on the navigation links.
To complete this requirement, once the page is loaded, we need to check whether there is a hash on the URL, and if so, trigger the ’scrollerComplete’ function:
if ([removed].hash) { trigger({ id : [removed].hash.substr(1)}); } else { $('#slider .navigation a:first').click(); }
By default, we’re triggering a click to the first navigation item if there’s no hash on the URL.
Any Link Triggers Effect
Requirement 5: Any link on the page that refers back to a panel should trigger the effect
Since we’re all lazy developers, we don’t want to have to specifically markup arbitrary links on the page that should trigger this effect.
This is where Ariel’s localScroll plugin comes to the rescue. By just applying that plugin with our default settings any link on the page will trigger the effect. In addition, because we’re going to trigger our ’scrollerComplete’ function when the effect has finished, it will correctly select the navigation associated.
The jQuery Code
Here’s the code with all the above requirements added in. The code is commented to explain what we’re doing.
I’ve also decided to include a bonus requirement - to support both horizontal or vertical scrolling.
// when the DOM is ready... $(document).ready(function () { var $panels = $('#slider .scrollContainer > div'); var $container = $('#slider .scrollContainer'); // if false, we'll float all the panels left and fix the width // of the container var horizontal = true; // float the panels left if we're going horizontal if (horizontal) { $panels.css({ 'float' : 'left', 'position' : 'relative' // IE fix to ensure overflow is hidden }); // calculate a new width for the container (so it holds all panels) $container.css('width', $panels[0].offsetWidth * $panels.length); } // collect the scroll object, at the same time apply the hidden overflow // to remove the default scrollbars that will appear var $scroll = $('#slider .scroll').css('overflow', 'hidden'); // apply our left + right buttons $scroll .before('<img class="scrollButtons left" src="images/scroll_left.png" />') .after('<img class="scrollButtons right" src="images/scroll_right.png" />'); // handle nav selection function selectNav() { $(this) .parents('ul:first') .find('a') .removeClass('selected') .end() .end() .addClass('selected'); } $('#slider .navigation').find('a').click(selectNav); // go find the navigation link that has this target and select the nav function trigger(data) { var el = $('#slider .navigation').find('a[href$="' + data.id + '"]').get(0); selectNav.call(el); } if ([removed].hash) { trigger({ id : [removed].hash.substr(1) }); } else { $('ul.navigation a:first').click(); } // offset is used to move to *exactly* the right place, since I'm using // padding on my example, I need to subtract the amount of padding to // the offset. Try removing this to get a good idea of the effect var offset = parseInt((horizontal ? $container.css('paddingTop') : $container.css('paddingLeft')) || 0) * -1; var scrollOptions = { target: $scroll, // the element that has the overflow // can be a selector which will be relative to the target items: $panels, navigation: '.navigation a', // selectors are NOT relative to document, i.e. make sure they're unique prev: 'img.left', next: 'img.right', // allow the scroll effect to run both directions axis: 'xy', onAfter: trigger, // our final callback offset: offset, // duration of the sliding effect duration: 500, // easing - can be used with the easing plugin: easing: 'swing' }; // apply serialScroll to the slider - we chose this plugin because it // supports// the indexed next and previous scroll along with hooking // in to our navigation. $('#slider').serialScroll(scrollOptions); // now apply localScroll to hook any other arbitrary links to trigger // the effect $.localScroll(scrollOptions); // finally, if the URL has a hash, move the slider in to position, // setting the duration to 1 because I don't want it to scroll in the // very first page load. We don't always need this, but it ensures // the positioning is absolutely spot on when the pages loads. scrollOptions.duration = 1; $.localScroll.hash(scrollOptions); });
Wrap UP
That’s everything you need for the perfect slider.
You might also like
Tags
accordion accordion menu animation navigation animation navigation menu carousel checkbox inputs css3 css3 menu css3 navigation date picker dialog drag drop drop down menu drop down navigation menu elastic navigation form form validation gallery glide navigation horizontal navigation menu hover effect image gallery image hover image lightbox image scroller image slideshow multi-level navigation menus rating select dependent select list slide image slider menu stylish form table tabs text effect text scroller tooltips tree menu vertical navigation menu