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.
Related posts
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
Alex Kendrick On 19th June 2009 at 15:06
Nice tutorial. Thank you.
If anyone is having trouble with ie6, this comment was helpful
Damon On 19th June 2009 at 16:06
i got vertical sliding working by simply changing var horizontal = true; to var horizontal = false;
now i gotta figure out how to have it scroll in the opposite direction so instead of starting out on top, you’re actually on the bottom.
Henri On 21st June 2009 at 08:06
First of all, thanks for the huge effort you have put into making this plugin.
I have a question, is there a way to add a class “active” (or something else) to the panel that is currently being shown? For example let’s say I have 5 panels each with a class called “panel”, I want to change the current panel’s class to “panel active”, how can this be accomplished?
It may sound silly, but I really can’t figure this out on my own so thanks in advance for the help.
SapinTremblant On 23rd June 2009 at 15:06
Hi, this code is nice but really misses the multiple slider function. Has anyone foun how to manage 2 and more sliders ?
Colin Law On 24th June 2009 at 14:06
Has anyone ever gotten this to work with a drop-down menu as the selection tool? I’ve tried the standard drop-down that is implemented with Dreamweaver, but making a selection with it simply goes directly to the linked div. no animated sliding occurs. I’m certain this can be fixed by properly telling the menu to do something different when the “onChange” is triggered….but I cannot figure it out.
my example is at http://www.chunkyboy.com/coda-slider-drop/
Markham On 25th June 2009 at 00:06
I am implementing this on my website and am having a few issues. First off I took the navigation out of the slider div and put it in the header so I changed the javaScript around to say #header .navigation instead of #slider .navigation. Problem is IE craps the bed now. The other issue I’m having is that when you refresh in FF the “selected” class moves back to home but the slider does not reset. How do I get it to either stay on the current slide and get that slide to stay selected or how do I make sure the anytime the page is refreshed in FF that the slides all move to home.
Site: http://www.markhamrollins.com/newSite
Colin Law On 26th June 2009 at 16:06
I think there’s an error on how this slider script is setup. I noticed that it doesn’t matter what order you put your list of links in, the script treats them as if they were in numerical order in regards to sliding to the panels. The id tags need to be unique, but the <a href’=”# stuff makes no difference. Try rearranging the order of the list links…your tabs will still slide to the panels in sequence. e.g. even though your 2nd link might now point to the 4th sliding panel, it will still slide to the 2nd panel because its the 2nd link in order. I think the problem is here, but I know nothing of javascript.
// handle nav selection function selectNav() { $(this) .parents(’ul:first’) .find(’a') .removeClass(’selected’) .end() .end() .addClass(’selected’); }
Colin Law On 26th June 2009 at 17:06
yes, I am completely convinced that the <a href=”#idname”> part is meaningless. the links within the <ul> list ALWAYS go in sequential order regardless of their link hrefs. Links elsewhere on the page do however work with the proper href.
the reason I was doing this was I have several items in my <ul> list that I wanted to go to the same sliding panel. I guess this is not possible.
Adam Rivera On 28th June 2009 at 20:06
Hello there. Wonderful tutorial. I have been trying to integrate jquery.history.js into this example and can’t seem to get it right. Maybe a good addition to this tutorial?
Cheers.
Jamie On 30th June 2009 at 11:06
I’m struggling a little with having multiple animations on the same page, essentially I’ve set up the scrolling side to side, and it works, I’ve fiddled about a lot with the code (most of which I didn’t initially understand!) and now I understand it better so it works the way I want it to but if someone wouldn’t mind taking a look at http://x.udwebdesign.co.uk and hopping over to the portfolio. What I want is to have the smaller images as links to scroll down the page to the relevant portfolio, but I want to animate the scroll effect, however whenever you click the links it appears to do something to the horizontal scroll, despite the fact that they aren’t included in the ul.navigation! Is there anything I can do to separate those links from the coda slider effect so that I can have them animate page offset!? Bearing in mind I’m pretty novice, I’m at that stage where I’m learning gradually by deconstructing other people’s code and trying to understand it! Otherwise this is a fantastic effect and I really appreciate the work put into it!
Cheers, Jamie.
Jamie On 30th June 2009 at 12:06
P.S. Colin, I believe you are right with those links being meaningless as far as the javascript goes, isn’t the #idhere bit purely for correctly degrading without javascript? Forgive me if I’m wrong, like I said in my previous post, total novice!
Jamie.
Rachael Burns On 30th June 2009 at 15:06
Hi, i’m trying to change the style of the list links when hovered and active. It wont let me change the background position on active/current only on hover - any ideas?
normally i do it from body id - obviously this is staying on one page.
cheers