Recently, I was thinking about an interesting way to display a listing of services offered for a design agency. While there are a plethora of options for something so simple, I ultimately dreamed up a carousel of sorts that scrolls through the services vertically. I knew it would likely require some jQuery and wanted to keep the HTML simple enough to quickly modify or apply to a CMS-generated unordered list.
My first step, as it is with anything I need to whip up quickly in code these days, was to head over to CodePen and see if I could accomplish what I was envisioning. Spoiler: turns out I worked it out. Check out the demo on CodePen and then follow along below to recreate it yourself!
The HTML
The objective for the HTML is to keep it simple. To truly be able to reuse this codebase for future projects, the HTML must consist of a simple unordered list with a class of container and list items. The result is as follows.
HTML
<ul class="container">
<li>Branding</li>
<li>Identity Design</li>
<li>Web Development</li>
<li>Social Media Setup</li>
<li>Email Marketing</li>
<li>Brochure Development</li>
<li>Logo Design</li>
</ul>
The CSS
Because the example is a simple box of list items, the CSS is fairly simple as well. We quickly want to set an overall width, make sure the list items are displayed as block-level elements, and choose a couple appealing colors. We’ll also set the second list item in the list to stand out with different background and text colors.
As a quick note, the “:nth-child” attribute will not work in IE8 or below, but we’re going to roll with it for this example.
CSS
ul.container {
width: 20em;
padding: 0;
margin: 50px auto 0;
border: 10px solid #fff;
box-shadow: 0px 2px 3px #eaeaea;
}
ul.container li {
background: #f7f7f7;
color: #ddd;
list-style-type: none;
padding: 1em;
margin: 0;
transition: background-color 0.5s;
}
ul.container li:nth-child(2) {
color: #fff;
background: #1998c4;
}
The Good Stuff: jQuery
The jQuery is where this effect all starts to come together. Let’s break it down piece-by-piece and then you can see all the code together below.
jQuery
var x = 0,
container = $('.container'),
items = container.find('li'),
containerHeight = 0,
numberVisible = 5,
intervalSec = 3000;
The first thing we want to do is set up a few variables. We’ll want to cache the list and its items, set a baseline for the container height (we’ll set this dynamically in a minute), and decide how many items should show at once and how fast they should change. A comma separated list of variables with their values does the trick here nicely. Caching the container and the list items allows us to do our work on a stored set of HTML code, rather than combing the DOM each time we want to make a change.
jQuery
if(!container.find('li:first').hasClass("first")){
container.find('li:first').addClass("first");
}
We wanted to keep the HTML code nice and clean, so we never added any classes to the list items. That’s great, but we’ll need to rip out the first item in each cycle and append it to the end. To get started, we need to make sure that the first item has a class of first. (I originally tried using “:first-child”, but it proved to be less reliable than setting a class right up front.)
jQuery
items.each(function(){
if(x < numberVisible){
containerHeight = containerHeight + $(this).outerHeight();
x++;
}
});
container.css({ height: containerHeight, overflow: "hidden" });
The next step is to run through the list items and figure out their height. With this information, we can set the container’s height based on the list item height × the number of items to be shown.
Once we’ve figured out what the container height should be, we can apply it using the jQuery “css” function and set the overflow to “hidden”.
jQuery
function vertCycle() {
var firstItem = container.find('li.first').html();
container.append('<li>'+firstItem+'</li>');
firstItem = '';
container.find('li.first').animate({ marginTop: "-50px" }, 600, function(){ $(this).remove(); container.find('li:first').addClass("first"); });
}
At this point, we have all our setup done and can start getting the animation going. We’ll start by creating a function called “vertCycle()”.
First, we need to find the first item and store its contents so we can append it to the end of the container. We, then, reset the firstItem variable and run our animation sequence. It’s a long line of code, but here is what the last line of the function is doing:
- Find the first item in the container (by the .first class)
- Animate the top margin value by negative 50 pixels (which happens to be our <li> height) over 0.6 seconds
- Run a function as a callback that removes the first list item, finds the new first list item and adds a class of first to it.
jQuery
var init = setInterval("vertCycle()",intervalSec);
container.hover(function(){
clearInterval(init);
}, function(){
init = setInterval("vertCycle()",intervalSec);
});
Now that our animation function is in place, we need to trigger it to run at a regular interval. For this, we’ll set a variable (so we can clear it later) to run the function using the javascript “setInterval()” function. For the interval itself, we’ll paste in our intervalSec variable that we declared earlier.
The block of code for the “hover” state is optional, but this allows you to stop that animation on hover, in case you have any anchor links in the list that need to be able to be clicked.
To see the entire jQuery code, look below where I’ve included all of the HTML, CSS, and jQuery code needed for this effect!
Final Code
That’s all there is to it! It looks like a lot, but when you break it down into smaller chunks, it becomes less intimidating. Go ahead, give it a shot!
HTML
<ul class="container">
<li>Branding</li>
<li>Identity Design</li>
<li>Web Development</li>
<li>Social Media Setup</li>
<li>Email Marketing</li>
<li>Brochure Development</li>
<li>Logo Design</li>
</ul>
CSS
ul.container {
width: 20em;
padding: 0;
margin: 50px auto 0;
border: 10px solid #fff;
box-shadow: 0px 2px 3px #eaeaea;
}
ul.container li {
background: #f7f7f7;
color: #ddd;
list-style-type: none;
padding: 1em;
margin: 0;
transition: background-color 0.5s;
}
ul.container li:nth-child(2) {
color: #fff;
background: #1998c4;
}
jQuery
var x = 0,
container = $('.container'),
items = container.find('li'),
containerHeight = 0,
numberVisible = 5,
intervalSec = 3000;
if(!container.find('li:first').hasClass("first")){
container.find('li:first').addClass("first");
}
items.each(function(){
if(x < numberVisible){
containerHeight = containerHeight + $(this).outerHeight();
x++;
}
});
container.css({ height: containerHeight, overflow: "hidden" });
function vertCycle() {
var firstItem = container.find('li.first').html();
container.append('<li>'+firstItem+'</li>');
firstItem = '';
container.find('li.first').animate({ marginTop: "-50px" }, 600, function(){ $(this).remove(); container.find('li:first').addClass("first"); });
}
var init = setInterval("vertCycle()",intervalSec);
container.hover(function(){
clearInterval(init);
}, function(){
init = setInterval("vertCycle()",intervalSec);
});