How to Solve the Problem
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
The hash is the emphasised part (including the # symbol) in: http://jqueryfordesigners.com/page.html#example
In fact, our version of this slider will be better than Panic’s and the current jQuery plugins if we can meet all of the requirements.
For example, in the Panic example linking directly to the preview pane doesn’t correctly highlight the navigation.
This whole solution is going to rely heavily on Ariel Flesler’s scrollTo plugin and particularly it’s ‘adapters’ to enhance the power of the scrollTo plugin.
I’ve provided a screencast to walk through how create this functionality. Details on how and what I used can be found below.
Watch the coda slider effect screencast (alternative flash version)
QuickTime version is approx. 90Mb, flash version is streaming - I’ve also noticed the sound is slightly over to the left - I’ll fix that for next time!)
View the demo and source code used in the screencast
Plugins Required
Along with jQuery we’ll need the following plugins - I recommend using the minified versions for production, and perhaps even development (download links can be found at the bottom of the linked pages):
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 (window.location.hash) {
trigger({ id : window.location.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 (window.location.hash) {
trigger({ id : window.location.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:
// http://gsgd.co.uk/sandbox/jquery/easing/
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. Ariel has lots of other examples of how the scroll plugins can be used on his web site so do check them out.
I’ll also be posting a follow up to last month’s survey.
If anyone has any questions, suggestions or better examples, please do share by dropping a comment on the site.
You should follow me on Twitter here I tweet about jQuery amongst the usual tweet-splurges!
Related screencasts
Demo
If you find this demo doesn't work as expected, it's possibly due to the demo running from within an iframe. Try running the demo in it's own window.
Source Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Better Coda Slider</title>
<link rel="stylesheet" href="coda-slider.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script src="jquery-1.2.6.js" type="text/javascript"></script>
<script src="jquery.scrollTo-1.3.3.js" type="text/javascript"></script>
<script src="jquery.localscroll-1.2.5.js" type="text/javascript" charset="utf-8"></script>
<script src="jquery.serialScroll-1.2.1.js" type="text/javascript" charset="utf-8"></script>
<script src="coda-slider.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<div id="wrapper">
<h1>jQuery Coda Slider</h1>
<div id="intro">
<p>This technique demonstrates an accessible 'Coda'-like slider interface, but in addition, allows you to place links to the sliding content anywhere on the page and have the effect (and navigation) still work. <br /><a href="http://jqueryfordesigners.com/coda-slider-effect">Read the article, and see the screencast this demonstration relates to</a></p>
</div>
<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>
<div class="scroll">
<div class="scrollContainer">
<div class="panel" id="sites"><h2>Sites</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p></div>
<div class="panel" id="files"><h2>Files</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p></div>
<div class="panel" id="editor"><h2>Editor</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p></div>
<div class="panel" id="preview"><h2>Preview</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p></div>
<div class="panel" id="css"><h2>CSS</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p></div>
<div class="panel" id="terminal"><h2>Terminal</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. <a href="#sites">And some sites</a></p></div>
<div class="panel" id="books"><h2>Books</h2><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p></div>
</div>
</div>
<div id="shade"></div>
</div>
<p>Lorem ipsum dolor sit amet, <a href="#books">books</a> consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco <a href="#sites">sites</a> laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure <a href="#terminal">terminal</a> dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</body>
</html>

Play QuickTime version
Play Flash version

Andy On 31st March 2009 at 12:03
I have also implemented this in Jquery 1.3.2, however it is broken in IE8, Lee, your version works fine in IE8, i dont know how to get it working, browsing without JS is fine, but if JS is turned on, then nothing happens.
Andy On 31st March 2009 at 12:03
Ah sorry, just an update, it works if i set the document mode to IE8 standards,. however i cannot guarentee people will have this setting by default
Matteo On 2nd April 2009 at 08:04
Great script! But, help me :)
I have a page with 11 blocks/tabs and I need to load the page starting from the 6th block, not from the first, then I can move to the left or to the right Can you help me?
Matteo On 3rd April 2009 at 12:04
Personal add to the script to hide navigation arrow when you are in the first and the last tab.
in the selectNav function I put this:
if ($(this).hasClass(’primo’)) $(”img.left”).fadeOut(); else if ($(this).hasClass(’ultimo’)) $(”img.right”).fadeOut(); else { $(”img.left”).fadeIn(); $(”img.right”).fadeIn(); }
It’s this add right? On my Mac and Firefox works great
Matteo On 3rd April 2009 at 12:04
sorry, in my post above there is an error:
I add “primo” and “ultimo” class to first LINK and last LINK of the navigation menu
Collin On 10th April 2009 at 02:04
Hi,
I am creating a site that uses this horizontal accordion slider, floatbox and hopefully this “coda” slider. But whenever I use the coda-slider.js it has a conflict with both the horizontal accordion slider and floatbox.
I am using this horizontal accordion slider thingy: http://designreviver.com/tutorials/jquery-examples-horizontal-accordion/
And floatbox is here: http://randomous.com/tools/floatbox/
All of the scripts use jQuery, unless I am mistaken.
Has anyone had the same problem? Any help would be greatly appreciated, I am sorry to make it so vague but I hreally have no idea where to begin, aside from show you the site I am working on:
http://www.collinwillis.com/cloudsite/
then click on the “Andrew Brodhead” button on the top left of the page.
Thanks so much for any help.
Roni Joven On 10th April 2009 at 12:04
Great slider, this is what i need however, looks that the slider conflicts with lava lamp menu. Can anyone please help.
I tried different versions of jquery and still doesnt work.
Thanks for the help
Try the link, http://jqueryfordesigners.com/
The top menu uses lava lamp menu.
Thanks for your help.
Regards
Chris Van Poos On 10th April 2009 at 16:04
Hey Remy, Great Tutorial!!!
Its greatly appreciated…
I have a question for you: Is there a way to have the scroll div’s height become dynamic.
I am trying to figure out a way to have the scroll div expand depending on the amount of content that is in each of the nested div’s (sites, files, editor, preview etc…)
Any information on this would be great!
Thanks, VP
Marco On 14th April 2009 at 09:04
Awesome! This is legeeeen….dary!
Thank you so much! I love the Coda Slider effect and now that I discovered this tutorial, my week is gonna be much better.
Colin On 15th April 2009 at 02:04
Did anyone find the solution to having multiple coda sliders on the same page? Any help would be greatly appreciated!
Caleb Jones On 16th April 2009 at 16:04
I’m trying to change this so that it will go to a different panel on page load not only via the hash (#panel1) but also via a query string (?panel=panel1). Getting the value of the query string is simple, but when I try to mimic where the hash is being used to load the initial panel by adding an else if with my query string when present it just sets the navigation to that panel but it still loads the first panel.
Any thoughts?
Peter B On 16th April 2009 at 17:04
This coda slider is one of the best things on the internet. Well done.
In case anyone wants to put the navigation links on the left-hand side, use this CSS, and add a “side” class to the #slider div. The CSS contains all of Remy’s original CSS, with additional code for the .side class.
[blog author edited - code offloaded to JS Bin]
Tony On 17th April 2009 at 17:04
Great tutorial. I changed the height . scroll and .scrollContainer div.panel to auto, because my content is of varying height. The problem is, each panel is now taking on the height of the longest panel. Is there a way to have each panel change to its own actual height? (For example, the way the JQuery UI Accordion plugin works when setting autoHeight to false: http://jqueryui.com/demos/accordion/#no-auto-height - that’s essentially what I’m looking for…each panel to resize to its actual height.) Thanks…
Matt On 24th April 2009 at 13:04
Is there anyway to have the slider automatically start and loop?
Julie On 24th April 2009 at 20:04
I love the slider and am using it for a question/answer survey application. However, I have 2 questions:
1) How do you maintain the current panel/tab for when a postback occurs? When our page postbacks, the slider gets reset to the first panel/tab.
2) How do you dynamically change the navigation arrows. We would like dictate the next panel to slide to based upon a value in the current panel.
Thanks in advance for your help! Julie
Ammad Zafeer On 24th April 2009 at 21:04
Thanks for the code and the hard work but i have one small issue. Slider is working fine as it is given in the demo but when i tried to move the script that runs the slider into one file with other jQuery scripts, everything stops working. Any clues whats going on here?
filip On 26th April 2009 at 20:04
if someone could help me,please i have 6 tabs(width is 680) that scroll in line,BUT when it slides to last one it goes to next line,it makes a really big mess… THANKS
Lee On 29th April 2009 at 02:04
@Remy…you still haven’t corrected your refresh bug that Steve posted a solution for. In your example (using Firefox anyway), click a tab other than Sites and hit refresh. Steve’s code corrects this.
http://jqueryfordesigners.com/coda-slider-effect/comment-page-10/#comment-1589
Steve On 29th April 2009 at 10:04
As Wes’s comment of early March, I’m wondering about using content that makes a server-side trip upon hitting a different hash.
My particular need is to script various steps of a shopping cart where each tab is a given step of the checkout process - one tab each for:
adding item to cart; adding options; get user data; submit payment; provide order confirmation
been done? This would be a fantastic UI pattern to use thankx to the lack of page refresh and the ability to move directly back or forth to a given tab. But we gotta be able to tickle the server per transition. got some $$ for the solution.
dipan On 30th April 2009 at 07:04
Pls guide me how to enable vertical scroll in above script? pls reply me ASAP. I am stuck in this. Avidly waiting for reply
web designer On 30th April 2009 at 08:04
Just wonderful and nice slider! It look smooth even on IE6.
Jonathan Miller On 2nd May 2009 at 23:05
I don’t know if this was asked already (didn’t want to sift through 13 pages of comments) but is it possible to “slide” up to down vs. left to right? How big of a change would that be?
chrs On 3rd May 2009 at 06:05
Is there a way to put multiple instances of this slider on a page?
Chris On 5th May 2009 at 12:05
Does this work with the new localscroll js? thanks in advance
Jezz On 5th May 2009 at 14:05
Great example, pretty simple to use too! In the version I have implemented you get a quick flash of all the hidden panels on page load, can this be suppressed? Normally I’d do something like adding display: none; to the panel div, but obviously then it would be invisible from the start. I did try setting every panel but the starting panel to a new div, panel2 and adding display: none; this worked in so far as the hidden panels were invisible on page load, but navigating to any of these panels shows that they remain invisible. Is there a simpler way round this?
Cheers.