Grow your CSS skills. Land your dream job.

Slot Machine Tabs

Published by Chris Coyier

I was looking at the features page of a web service called Fluxiom. I haven't used the product (although it looks pretty nice and might be good few a couple of our clients). It's the tabs on that page that I thought were pretty neat. As you click a different tab, the three columns of text fly upward at different rates and are replaced by new columns. It looks kinda like a slot machine. I didn't investigate too deeply how they were doing it, but as I often do, I set about recreating the effect with jQuery.

After clicking a new tab the three columns slide away and are replace with new ones at random rates, like a slot machine.

View Demo   Download Files

I thought I did OK... although it can definitely be improved. There are also enough interesting things to talk about, so let's get after it.

HTML

Just going to do a bit of a code dump here so you can see it all.

<div id="slot-machine-tabs">

	<ul class="tabs">
		<li><a href="#one">Tab One</a></li>
		<li><a href="#two">Tab Two</a></li>
		<li><a href="#three">Tab Three</a></li>
	</ul>

	<div class="box-wrapper">

		<div id="one" class="content-box">
			<div class="col-one col">
				<img src="images/evangeline.jpg" alt="" />
			</div>
			<div class="col-two col">
				<h3>Kate</h3>
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
			<div class="col-three col">
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
		</div>

		<div id="two" class="content-box">
			<div class="col-one col">
				<img src="images/elizabeth.jpg" alt="" />
			</div>
			<div class="col-two col">
				<h3>Juliet</h3>
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
			<div class="col-three col">
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
		</div>

		<div id="three" class="content-box">
			<div class="col-one col">
				<img src="images/sonya.jpg" alt="" />
			</div>
			<div class="col-two col">
				<h3>Penelope</h3>
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
			<div class="col-three col">
				<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
			</div>
		</div>

	</div> <!-- END Box Wrapper -->

</div> <!-- END Slot Machine Tabs -->

Important points:

  • The whole thing is wrapped in an div with an ID value. Should we convert this idea into a plugin, the idea would be to target this ID and do the magic. But the ID is totally unnecessary for the CSS. That means we could have multiple instances of the slot machine tabs on a single page.
  • The "navigation" (the tabs) are at the top above the content boxes. With styling turned off, this will look like a navigation list like any other. The href values for the links point to the ID's of the content boxes, so the links would jump down the page to the corresponding content. Ideal.
  • When there are a lot of closing </div>'s in a row, I like to do the thing where you add a comment afterward to explain which thing this is closing (e.g. <!-- END page wrap -->)

CSS

The tabs themselves will be like any other horizontal navigation. We'll make the list items inline and the anchors block floated left. Simple borders and backgrounds, and a special state for "current" (no border and bumped down) and we're set.

.tabs { list-style: none; overflow: hidden; padding-left: 1px; }
.tabs li { display: inline; }
.tabs li a { display: block; float: left; padding: 4px 8px; color: black; border: 1px solid #ccc; background: #eee; margin: 0 0 0 -1px; }
.tabs li a.current { background: white; border-bottom: 0; position: relative; top: 2px; z-index: 2; }

One somewhat-unsemantic thing we are using is the #box-wrapper div, but whatever it's not that bad and it helps us in a number of ways. For one, it's the the relative positioning container which limits the scope of absolute positioning inside it. We can then absolutely position each content box on top of each other inside.

My favorite part is the box-shadow CSS around the #box-wrapper div. Not only does it help the tabbed area pop up a bit, but it handles the shading on the tabs themselves. The box wrapper sits on top (literally, z-index wise) of the non-current tabs. This is exactly the illusion/visual-metaphor we are going for: the current tab is on top and connected, the non-current tabs are "behind" (shadow is cast upon them).

.box-wrapper { -moz-box-shadow: 0 0 20px black; -webkit-box-shadow: 0 0 20px black; padding: 20px; background: white; border: 1px solid #ccc; margin: -1px 0 0 0; height: 210px; position: relative; }
.content-box { overflow: hidden; position: absolute; top: 20px; left: 20px; width: 658px; height: 230px; }

Notice we also pull the box up by one pixel with a negative top margin. That makes sure the under-border of the non-current tabs don't make a 2px line but a consistent 1px line.

Another important empowering concept here is that the #box-wrapper has hidden overflow and a set height. The columns in all the content boxes that are non-current are hidden by way of pushing their top value to 350px (a value taller than the height of the box). This pushes them completely out of view because of the hidden overflow. JavaScript will later do the job of pulling up new columns and pushing the old ones out of the way when needed.

The columns:

.col { width: 30%; float: left; position: relative; top: 350px; }
.col-one, .col-two { margin-right: 3%; }

Notice we only apply right margin to the first two columns. Another way to have done that is to apply the margin to all the columns but use .col:last-child { margin-right: 0; } to remove it. That might be the best way to go if you plan on having a variable number of columns. Just be aware of the lack of pseudo selector support on IE.

jQuery JavaScript

This isn't plugin-ized yet, but it probably could/should be. There are some things I would want to fix/make less redundant before that happens, which I'll cover later. You can view the full commented JavaScript file here.

I'm not going to do a full code dump but I'll cover some interesting lines.

Right away the first tab and the first content box are declared as current. The current content columns are moved to a top position of 0 (so they are visible) rather than the default hidden value from the CSS.

$(".tabs li:first-child a, .content-box:first").addClass("current");
$(".box-wrapper .current .col").css("top", 0);

We use the delegate function for the click events on the tabs, since that's so efficient (and could handle dynamic addition of tabs if that came up):

$("#slot-machine-tabs").delegate(".tabs a", "click", function() {
   // stuff
}

When a click happens, action only takes place if the tab clicked on is not the current tab and there is no other animation taking place on the page. In this limited demo, the only animation possible is the columns. In a more "real" environment the scope of this test should probably be pared down to inside the slot machine tabs specific ID. Something like $("#slot-machine-tabs *:animated)

$el = $(this);
		
 if ( (!$el.hasClass("current")) && ($(":animated").length == 0 ) ) {
    // stuff
}

I thought it was a more engaging effect if columns didn't change at the same rate each time. I set the speeds pseudo-randomly for each animation, but with a base value of half a second. The speed for the leaving column and entering column match though, so there is no overlapping.

speedOne = Math.floor(Math.random()*1000) + 500;
speedTwo = Math.floor(Math.random()*1000) + 500;
speedThree = Math.floor(Math.random()*1000) + 500;

Notice in the demo how all new columns always slide up from the bottom. But as they leave, the slide up to the top. That is accomplished because after the slide-them-up-and-away animation is finished, we instantly move them back down to the default low-and-hidden position. Before doing that, we need to make sure that all animations are completed. I had a slightly hard time doing this. Normally to fire something after an animation, it's no big deal because you can have a callback function on an animation which only fires when the animation is complete. But we are running six different animations here and because they all take a random length of time, we don't know which one is going to end last.

My solution so far is to call the same function on callback on every single finishing animation. For example:

$(".box-wrapper .current .col-one").animate({
	"top": 0
}, speedOne, function() {
	ifReadyThenReset();
});

The ifReadyThenReset() function will only do it's thing (reset the column top positions) when it's been called for the third time:

var columnReadyCounter = 0;

function ifReadyThenReset() {
	
	columnReadyCounter++;
	
	if (columnReadyCounter == 3) {
		$(".col").not(".current .col").css("top", 350);
		columnReadyCounter = 0;
	}

};

Issues:

  • It's not a plugin.
  • Each of the columns involved in a tab-changing event is individually animated. That's six things being animated at once and all of them are "hard-coded". This makes adding or removing columns more of a chore than it should be.
  • The call-the-callback-three-times thing seems kludgey to me, and the hard coded three shares the same problem as above.

Demo & Download

View Demo   Download Files

As always, do what you will with this, including use it in a corporate project to impress your boss and use as example of why you deserve a raise.

Comments

  1. I’m not a fan of it, but it’s cool you are getting your ideas out there into the public. I appreciate that.

    • bill
      Permalink to comment#

      Same here. Seems gimmicky and while the visual effect is OK, if I had to sit through this for a site to get through a lot of content I’d leave.

    • Even though the animation speed here is pretty slow, if it’s speeded up like that site’s products page it’d really attract some attention and not be annoying at all. I bet this would be a neat effect for actual casino sites, or maybe even games sites.

      Thanks for sharing Chris, I’d probably use this for something if it was a plugin so adding columns would be easier.

  2. Nick
    Permalink to comment#

    Juliet!

    Oh.. nice tutorial too!

  3. Pretty cool, thanks for recreating. A little bit naff maybe, but could become useful. Very Flash like just without the Flash. Seems to be the way a lot of websites are going, less and less Flash and more jquery lovelyness.

  4. That’s hot. I tired implementing a slot machine style rolling navigation before and it just didn’t look believable. Now that i look at your I think it might be because with the navigation the height was a lot shorter. I think that the extra space helps to make the movement seem more inline with what we are used to seeing in real life and helps our brains make the association.

  5. That is a really neat implementation of a content slider. What are you going to do when Lost is over?

    • lol Don’t you know that once this season of LOST is over they are continuing a new series off the “slide sideways” version of reality. It’s called FOUND.

      Love the effect, Chris. I’m sure with the right client and treatment this could be very useful and interesting. bookmarked.

  6. It’s a neat idea… but would it look as good with more content on each tab?

  7. It’s pretty neat, the only thing I don’t really like though is that if you press back you have to go through all the tabs you’ve clicked on, instead of just going back a page.

  8. Chris,

    nice little tutorial. Definitely inspiration for the virtually limitless possibilities of tabbed-content areas.

    How’d you get the “infinite scroll” effect? I couldn’t find it after a quick skim through the full JS code.

    Cheers,
    Jacob

  9. sanjay
    Permalink to comment#

    great job chris

  10. Best demo evar! :)

  11. Permalink to comment#

    wow impressive! is working even in the most blamed browser IE6! niceeee :)

  12. Yes, very nice…

    And I like the Lost characters.

  13. Great effect. I don’t know how practical it is (slightly gimmicky) but it certainly looks cool. It might be nice to add a slight blur during transition (to indicate speed) and maybe some easing so it comes to a bit of a gradual stop. Then I think it would be elegant enough for normal use

  14. Great effect. and Juliet is best in 3 women.

  15. This is going in my how-to-do bookmarks section. Thanks for the post!

  16. Chris,

    I am not a coder like some of your other loyal followers though I am interested in what goes on behind those curtains. I just wanted to say you make this gobbledygook interesting!

  17. Permalink to comment#

    That’s really neat! ill try it in my own.

    Thanks Chris for the post!

  18. elundmark
    Permalink to comment#

    Very cpu intensive :P But equally cool :)

  19. I agree with @jaytillery, the effect is not to my taste but a really great walk through of the code, especialy the delegate function! Probably the most I have learned on a monday morning in a long time.

  20. I’d probably like it more if it was smoother. I’m not sure whether it’s just my machine, but the start of the animation jutters quite a lot on the latest (public) build of Chrome.

  21. Natasha
    Permalink to comment#

    A nice idea, can see it being popular for some sites. A fall-back for people without javascript would be good though.

  22. Ugly and another ridiculous use of this jQuery library that you all love.

  23. Can you please not use sultry babe images in your demos? I look at this at work and it looks cheesy and sleazy.

  24. Wow, you keep inventing and inspiring others that nothing is impossible. Great work Chris.

  25. This is really cool, and it’s original, well besides the fact that you got the idea from fluxiom, but it’s still cool none the less!

    I assume it would be possible to make it scroll horizontally, since you set it relative to ‘top’.

    Could be cool to make this into a jQuery/CSS/HTML casino slot machine game.
    Nice job

  26. Filipe
    Permalink to comment#

    Hey that’s great, thank you very much.
    I just have a question, is it possible to use it in only one page of a wordpress website? If yes, could you somehow explain that?

    Thanks

  27. Superb plugin! Thanks so much for sharing.

  28. Nice work Chris. It’s interesting to see that.
    I did run into a problem on firefox with the back button not refreshing the page content, only the url. Did anyone else have that?

  29. Absolutely a great tab concept. I used to have Adobe default tab concept in all my websites..
    I thinks this one i can use from the next project….

  30. Permalink to comment#

    Very impressive. i like the slow animation speed too a fall-back would make it perfect.

    Thanks for sharing!

  31. Duncan
    Permalink to comment#

    I can see a few uses, if done carefully or it could look a bit ‘tacky’.
    FF is a bit clunky on the animation but Opera 10.5 has even bigger woes. The first tab does not load on first load of the page, it will only appear after one of the other tabs has been clicked.
    No content visible at all without JS enabled ???
    Opera 10.5 has been going long enough now to warrant being invited to the party on CSS3 properties, you are only using the proprietary -moz and -webkit selectors, shouldn’t we be seeing the standard selectors as well (future-proofing).

  32. Permalink to comment#

    thats awesome

  33. You should add box-shadow as well as -webkit-box-shadow and -moz-box-shadow, so it works in Opera.

  34. westwind
    Permalink to comment#

    I really like this slot machine effect, but I can’t get links to work in the columns, they work in ie but nothing else, does anyone have a clue about this?
    thanks,
    west

  35. miniMAC

    I like that! But there is a metod for automatic sliding?

  36. Stuart

    Maybe i have too many scripts working on this template
    i couldn’t get the Slot Machine to work along with Magic Line
    Start Stop Slider
    always the Magic line will not respond plus i am super new at this JavaScript
    but i love it !

  37. rocky
    Permalink to comment#

    I have a problem when i make the tabs much longer to take my content every time i clicked the tabs it appears from the first line in the tab not from the top of my web page so as a result i have to scroll up after every tab clicking to see the whole page

  38. Joe
    Permalink to comment#

    Very impressive. I noticed linkage works only on the last tab body – content or am I missing something out?

    Thanks.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".