Animated Text and Icon Menu with jQuery

Today we want to show you how to create a slick menu with a nice animation feature on hover. The idea is to make some elements slide out, change and animate the background color of the item and then slide the elements back in with a different color.

Today we want to show you how to create a slick menu with a nice animation feature on hover. The idea is to make some elements slide out, change and animate the background color of the item and then slide the elements back in with a different color.

The inspiration for this menu comes from the website of the Pelican Miami Beach Hotel: http://www.pelicanhotel.com/

The icons are taken from the incredible Noun Project that “collects, organizes and adds to the highly recognizable symbols that form the world’s visual language, so we may share them in a fun and meaningful way”. Visit the website of The Noun Project.

So, let’s get started!

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

The Markup

Our HTML will be an unordered list where each list item will contain an anchor element with the three elements inside that we’ll animate:

<ul id="sti-menu" class="sti-menu">
	<li data-hovercolor="#37c5e9">
		<a href="#">
			<h2 data-type="mText" class="sti-item">
				Some text
			</h2>
			<h3 data-type="sText" class="sti-item">
				Some more text
			</h3>
			<span data-type="icon" class="sti-icon sti-icon-care sti-item">
			</span>
		</a>
	</li>
	<li>...</li>
	...
</ul>

The data-hovercolor will be used to set the color of the text on hover. Also, we’ll give some data-type attribute to each heading and the icon span. We’ll use that later in our JavaScript.

Now, let’s make it stylish!

The CSS

Remember, we always reset our CSS with a reset.css that we’ll add to our main style.
First, we’ll style the unordered list and give it a fixed width so that we can center it on the page:

.sti-menu{
	width:1010px;
	position:relative;
	margin:60px auto 0 auto;
}

The list elements will be floating and a little margin:

.sti-menu li{
	float:left;
	width:200px;
	height:300px;
	margin:1px;
}

Now we’ll style the anchor. We’ll hide all the overflow because we want to animate the elements outside of the anchor:

.sti-menu li a{
	display:block;
	overflow:hidden;
	background:#fff;
	text-align:center;
	height:100%;
	width:100%;
	position:relative;
	-moz-box-shadow:1px 1px 2px #ddd;
	-webkit-box-shadow:1px 1px 2px #ddd;
	box-shadow:1px 1px 2px #ddd;
}

The headings will be positioned absolutely and we’ll define the width and the top position:

.sti-menu li a h2{
	color:#000;
	font-family: 'Wire One', arial, serif; 
	font-size:42px;
	font-weight:bold;
	text-transform:uppercase;
	position:absolute;
	padding:10px;
	width:180px;
	top:140px;
	text-shadow: 0px 1px 1px black;
}
.sti-menu li a h3{
	font-family: Baskerville, "Hoefler Text", Garamond, "Times New Roman", serif; 
	font-size:18px;
	font-style:italic;
	color: #111;
	position:absolute;
	top:248px;
	width:180px;
	padding:10px;
}

Each icon span will have the following general class and also a specific one, where we’ll add the right background image. The images will have both, the black and the colored version of the icon. We’ll change the background position in the JavaScript once we animate the icon out of the anchor element.

.sti-icon{
	width:100px;
	height:100px;
	position:absolute;
	background-position:top left;
	background-repeat:no-repeat;
	background-color:transparent;
	left:50px;
	top:30px;
}
.sti-icon-care{
	background-image:url(../images/care.png);
}
.sti-icon-alternative{
	background-image:url(../images/alternative.png);
}
.sti-icon-family{
	background-image:url(../images/family.png);
}
.sti-icon-info{
	background-image:url(../images/info.png);
}
.sti-icon-technology{
	background-image:url(../images/technology.png);
}

And that’s all the style! Let’s move to the fun part!

The JavaScript

Since we are doing a plugin out of this, let’s define the default settings first:

var settings = {
	// configuration for the mouseenter event
	animMouseenter		: {
		'mText' : {speed : 350, easing : 'easeOutExpo', delay : 140, dir : 1},
		'sText' : {speed : 350, easing : 'easeOutExpo', delay : 0, dir : 1},
		'icon'  : {speed : 350, easing : 'easeOutExpo', delay : 280, dir : 1}
	},
	// configuration for the mouseleave event
	animMouseleave		: {
		'mText' : {speed : 300, easing : 'easeInExpo', delay : 140, dir : 1},
		'sText' : {speed : 300, easing : 'easeInExpo', delay : 280, dir : 1},
		'icon'  : {speed : 300, easing : 'easeInExpo', delay : 0, dir : 1}
	},
	// speed for the item bg color animation
	boxAnimSpeed		: 300,
	// default text color (same defined in the css)
	defaultTextColor	: '#000',
	// default bg color (same defined in the css)
	defaultBgColor		: '#fff'
};

For each one of our elements we’ll have the animation speed, the easing effect, the delay time and the direction (1 is for up and 0 for down).
We also have the animation speed for the background color animation of the menu item and the text and background color which we also have in our CSS.

return this.each(function() {
	// if options exist, lets merge them with our default settings
	if ( options ) {
		$.extend( settings, options );
	}
	
	var $el 			= $(this),
		// the menu items
		$menuItems		= $el.children('li'),
		// save max delay time for mouseleave anim parameters
	maxdelay	= Math.max( settings.animMouseleave['mText'].speed + settings.animMouseleave['mText'].delay ,
							settings.animMouseleave['sText'].speed + settings.animMouseleave['sText'].delay ,
							settings.animMouseleave['icon'].speed + settings.animMouseleave['icon'].delay
						  ),
		// timeout for the mouseenter event
		// lets us move the mouse quickly over the items,
		// without triggering the mouseenter event
		t_mouseenter;
	
	// save default top values for the moving elements:
	// the elements that animate inside each menu item
	$menuItems.find('.sti-item').each(function() {
		var $el	= $(this);
		$el.data('deftop', $el.position().top);
	});
	
	// Events
	...
	
});

Let’s define the mouseenter event for each menu item:

$menuItems.bind('mouseenter', function(e) {
	
	clearTimeout(t_mouseenter);
	
	var $item		= $(this),
		$wrapper	= $item.children('a'),
		wrapper_h	= $wrapper.height(),
		// the elements that animate inside this menu item
		$movingItems= $wrapper.find('.sti-item'),
		// the color that the texts will have on hover
		hovercolor	= $item.data('hovercolor');
	
	t_mouseenter	= setTimeout(function() {
		// indicates the item is on hover state
		$item.addClass('sti-current');
		
		$movingItems.each(function(i) {
			var $item			= $(this),
				item_sti_type	= $item.data('type'),
				speed			= settings.animMouseenter[item_sti_type].speed,
				easing			= settings.animMouseenter[item_sti_type].easing,
				delay			= settings.animMouseenter[item_sti_type].delay,
				dir				= settings.animMouseenter[item_sti_type].dir,
				// if dir is 1 the item moves downwards
				// if -1 then upwards
				style			= {'top' : -dir * wrapper_h + 'px'};
			
			if( item_sti_type === 'icon' ) {
				// this sets another bg image position for the icon
				style.backgroundPosition	= 'bottom left';
			} else {
				style.color					= hovercolor;
			}
			// we hide the icon, move it up or down, and then show it
			$item.hide().css(style).show();
			clearTimeout($item.data('time_anim'));
			$item.data('time_anim',
				setTimeout(function() {
					// now animate each item to its default tops
					// each item will animate with a delay specified 
					// in the options
					$item.stop(true)
						 .animate({top : $item.data('deftop') + 'px'}, speed, easing);
				}, delay)
			);
		});
		// animate the bg color of the item
		$wrapper.stop(true).animate({
			backgroundColor: settings.defaultTextColor
		}, settings.boxAnimSpeed );
	
	}, 100);	

})

And then we define the mouseleave event which is basically everything in reverse:

// mouseleave event for each menu item
.bind('mouseleave', function(e) {
	
	clearTimeout(t_mouseenter);
	
	var $item		= $(this),
		$wrapper	= $item.children('a'),
		wrapper_h	= $wrapper.height(),
		$movingItems= $wrapper.find('.sti-item');
	
	if(!$item.hasClass('sti-current')) 
		return false;		
	
	$item.removeClass('sti-current');
	
	$movingItems.each(function(i) {
		var $item			= $(this),
			item_sti_type	= $item.data('type'),
			speed			= settings.animMouseleave[item_sti_type].speed,
			easing			= settings.animMouseleave[item_sti_type].easing,
			delay			= settings.animMouseleave[item_sti_type].delay,
			dir				= settings.animMouseleave[item_sti_type].dir;
		
		clearTimeout($item.data('time_anim'));
		
		setTimeout(function() {
			
			$item.stop(true).animate({'top' : -dir * wrapper_h + 'px'}, speed, easing, function() {
				
				if( delay + speed === maxdelay ) {
					
					$wrapper.stop(true).animate({
						backgroundColor: settings.defaultBgColor
					}, settings.boxAnimSpeed );
					
					$movingItems.each(function(i) {
						var $el				= $(this),
							style			= {'top' : $el.data('deftop') + 'px'};
						
						if( $el.data('type') === 'icon' ) {
							style.backgroundPosition	= 'top left';
						} else {
							style.color					= settings.defaultTextColor;
						}
						
						$el.hide().css(style).show();
					});
					
				}
			});
		}, delay);
	});
});

And that’s it! We hope you enjoyed this tutorial and find it useful!

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 72

Comments are closed.
  1. stop doing great things mary lou ! jajjaaa, just kidding

    thanks for share this really nice code, love to interact with your creations !

  2. I love how you are able to animate the image stats individually. I actually wasn’t sure how that could be done here. Great job Mary Lou!!

  3. Wow… Great job! Thank you for sharing! Mary Lou (Crna cura) i appreciate your work, so thank you one again!

    I’d like to ask you something. “Crna cura” em… i do understand what that means, so i wonder where do you come from? From which country? 🙂

  4. Thanks for another great tutorial, I’m sure this navigation will be finding its way into one of my projects sooner rather than later! The fact that real text has been used is even more of a bonus! Keep up the good work and I look forward to reading your next tutorial.

  5. This is fantastic! Great tutorial as well! I’m constantly amazed by the new things I find that jQuery can do! Thanks for sharing!

  6. well… i ve been learning a lot here, and this efect is very usual for the works that i m doing here. it is realy very nice. thx a lot for share… mary im your fan..

  7. Great menu! love it. Is there an easy way for one of the menu items to stay “active” for it’s particular page?

  8. A simple, very nice menu!

    I guess you should make more stuff like this 🙂

    Have fun!

  9. It´s not just your talent with JS, it´s also your feeling for design which makes your work pop out from the rest – keep up your great work, Mary Lou, I´m always curious what comes next!

  10. Very nice. Easy to follow simple and straight forward. And awesome examples in the demo. Might use this in a new project one day.

    Thanks!

  11. Serious Design Stuff, I love it. Inspires one to learn jQuery coding, for sure.

    Thanks

  12. How did you convert the icons from noun project into colored images for this? it would be helpful if you can explain that too

  13. mary lou thanks so much once again! this is awesome work!

    just a small question, i tried to make it work with greek subset of font “Open Sans Condensed” but it doesn’t display the text correctly.

    anyone can help?

    thanks!

  14. This is really great tutorial to understand how powerful jQuery and thus Javascript is! But I want to notify you that have you checked your work under Firefox’s version 3.6? It looks something scattered in it while everything just works fine for me in Firefox 5 and IE though! Please check this clone I made from this tutorial for my final year project in college under Firefox 3.6 and other latest browsers, you will easily find what I’m talking about! And hence later on, tell me the fall backs to overcome that problem in ff3.6 or in older versions.

    Thanking you. 🙂

  15. Loving the script but am having issues with placing the script in a menu. Once I put them in my menu, the animation positions change and I can’t seem to solve it!! The layers are stacked on top of each other and I suspect they can’t get the ‘top’ reference. Any help pls?

  16. This is beautifully done, and my first foray into this kind of effect. I have one question if you might be so kind:

    what if I just wanted the icon image in each to just nudge up like 7px upon hover, and then nudge back down upon exit? or even better, just expand by like 7px, then shrink back down?

    i assume it has something to do with this line in the moving items:

    // if dir is 1 the item moves downwards
    // if -1 then upwards
    style = {‘top’ : -dir * wrapper_h + ‘px’};

    any hints or points in the right direction? I’ve replicated everything in your tutorial but would love to make that one small change for our use?

    thanks!

  17. Wonderful!

    QUESTION: It always ends on white. I’d like when the black slides away to reveal the images I’ve placed in the background of the s.

    How do I get rid of the defaultBgColor?

  18. pls. i love this example. i have already incorporated it into a project am working on right now. i need ur help on how to bind click event to the menu items. pls help its urgent!!!!!!

  19. How can i add this to my wordpress site? I use Sufussion theme. I would love an answer because this menu is impressive work!

    Best Regards