Treehouse

Create Twitter-Style Dropdowns Using jQuery

By on  
Twitter Dropdown

Twitter does some great stuff with JavaScript. What I really appreciate about what they do is that there aren't any epic JS functionalities -- they're all simple touches. One of those simple touches is the "Login" dropdown on their homepage. I've taken some time to duplicate that functionality with jQuery.

The HTML

<div id="menu1"><div class="relative">
	<a href="/demos" title="Popular MooTools Tutorials" id="dd1" class="dropdown" style="width:170px;text-decoration:none;"><span>Menu 1</span></a>
	<div id="dropdown1" class="dropdown-menu">
		<a href="/about-david-walsh" title="Learn a bit about me.">About Me</a>
		<a href="/page/1" title="The David Walsh Blog">Blog</a>
		<a href="/chat" title="#davidwalshblog IRC Chat">Chat</a>
		<a href="/contact" title="Contact David Walsh">Contact Me</a>
		<a href="/demos" title="CSS, PHP, jQuery, MooTools Demos">Demos &amp; Downloads</a>
		<a href="/js" title="ScrollSpy, Lazyload, Overlay, Context Menu">MooTools Plugins</a>
		<a href="/network" title="David Walsh Blog, Script &amp; Style, Band Website Template, Wynq">Network</a>
		<a href="/web-development-tools" title="JS, CSS Compression">Web Dev Tools</a>
	</div>
</div></div>

<div id="menu2"><div class="relative">
	<a href="/demos" title="Popular MooTools Tutorials" id="dd2" class="dropdown" rel="dropdown2" style="width:170px;text-decoration:none;"><span>Menu 2</span></a>
	<div id="dropdown2" class="dropdown-menu">
		<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>

A series of DIVS wrapping a link (the dropdown "trigger") and a DIV containing the menu items.

The CSS

/* dropdowns: general */
a.dropdown { background: #88bbd4; padding: 4px 6px 6px; text-decoration: none; font-weight: bold; color: #fff; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; }
a.dropdown:hover { background: #59b; }
a.dropdown { position: relative; margin-left: 3px; }
a.dropdown span { background-image: url(toggle_down_light.png); background-repeat: no-repeat; background-position: 100% 50%; padding: 4px 16px 6px 0; }
a.dropdown.dropdown-active { color:#59b; background-color:#ddeef6; }
a.dropdown.dropdown-active span { background:url(toggle_up_dark.png) 100% 50% no-repeat; }
.dropdown-menu	{ background:#ddeef6; padding:7px 12px; position:absolute; top:16px; right:0; display:none; z-index:5000; -moz-border-radius-topleft: 5px; -moz-border-radius-bottomleft: 5px; -moz-border-radius-bottomright: 5px; -webkit-border-top-left-radius: 5px; -webkit-border-bottom-left-radius: 5px; -webkit-border-bottom-right-radius: 5px; }
	.dropdown-menu p { font-size:11px; }
.dropdown-menu a:link, .dropdown-menu a:visited	{ font-weight:bold; color:#59b; text-decoration:none; line-height:1.7em; }
.dropdown-menu a:active, .dropdown-menu a:hover { color:#555; }

			* html .dropdown-menu { top:28px; }
			*+ html .dropdown-menu { top:28px; }

/* dropdowns: specific */
#menu1			{ float:left; margin-right:20px; }
	#dropdown1	{ width:150px; }
	#dropdown1 a	{ display:block; }
#menu2			{ float:left; }
	#dropdown2	{ width:150px; font-size:11px; }
.relative		{ position:relative; }

There's a lot of CSS involved but most of it is simple visual styling as opposed to styling for JavaScript's sake. Do, however, note where relative and absolute positioning is used. The outermost DIV may be positioned absolutely if you'd like. Also note that I'm not doing anything to accommodate for rounded corners in IE -- I recommend DD_Roundies for that.

The jQuery JavaScript

$(document).ready(function() {
	/* for keeping track of what's "open" */
	var activeClass = 'dropdown-active', showingDropdown, showingMenu, showingParent;
	/* hides the current menu */
	var hideMenu = function() {
		if(showingDropdown) {
			showingDropdown.removeClass(activeClass);
			showingMenu.hide();
		}
	};
	
	/* recurse through dropdown menus */
	$('.dropdown').each(function() {
		/* track elements: menu, parent */
		var dropdown = $(this);
		var menu = dropdown.next('div.dropdown-menu'), parent = dropdown.parent();
		/* function that shows THIS menu */
		var showMenu = function() {
			hideMenu();
			showingDropdown = dropdown.addClass('dropdown-active');
			showingMenu = menu.show();
			showingParent = parent;
		};
		/* function to show menu when clicked */
		dropdown.bind('click',function(e) {
			if(e) e.stopPropagation();
			if(e) e.preventDefault();
			showMenu();
		});
		/* function to show menu when someone tabs to the box */
		dropdown.bind('focus',function() {
			showMenu();
		});
	});
	
	/* hide when clicked outside */
	$(document.body).bind('click',function(e) {
		if(showingParent) {
			var parentElement = showingParent[0];
			if(!$.contains(parentElement,e.target) || !parentElement == e.target) {
				hideMenu();
			}
		}
	});
});

I've commented to the code to illustrate what each block does. In a nutshell:

  • I create placeholder variables which will keep track of the current menu, dropdown, and parent for the opened menu. This functionality is only included because I don't want more than one menu to be open at a time.
  • I create a function that hides the current menu -- this can be used from anywhere within the closure.
  • I cycle through each dropdown and add events to relevant elements to show and hide menus.
  • I add an event to the body to close the current menu if the user clicks outside of the menu.

That's it!

Be sure to check out the MooTools version.

ydkjs-6.png

Recent Features

  • 6 Things You Didn&#8217;t Know About Firefox&nbsp;OS

    Firefox OS is all over the tech news and for good reason:  Mozilla's finally given web developers the platform that they need to create apps the way they've been creating them for years -- with CSS, HTML, and JavaScript.  Firefox OS has been rapidly improving,...

  • Animated 3D Flipping Menu with&nbsp;CSS

    CSS animations aren't just for basic fades or sliding elements anymore -- CSS animations are capable of much more.  I've showed you how you can create an exploding logo (applied with JavaScript, but all animation is CSS), an animated Photo Stack, a sweet...

Incredible Demos

  • Create a Photo Stack Effect with Pure CSS Animations or&nbsp;MooTools

    My favorite technological piece of Google Plus is its image upload and display handling.  You can drag the images from your OS right into a browser's DIV element, the images upload right before your eyes, and the albums page displays a sexy photo deck animation...

  • MooTools Gone Wild: Element&nbsp;Flashing

    If you're like me and lay awake in bed at night, you've flipped on the TV and seen the commercials: misguided, attention-starved college girls fueled by alcohol ruining their futures by flashing lame camera-men on Spring Break. Why do they do it? Attention,...

Discussion

  1. Looks great dude :)

  2. Goes off the left edge of my screen like the Mootools version… :-(

  3. @Dwight Blubaugh: I’ll address this at the end of the day. I have a few ideas.

  4. @Dwight Blubaugh: All that’s needed is to move the absolute positioning of the menu part. Instead of right:0, you’d use left:0.

  5. @David Walsh: I was able to fix this with:

    #menu1 {
    float:left;
    margin-right:20px;
    margin-left:100px;
    }

    However, I’ve tried both the JQuery and MooTools version, but the menus will not close (if the menus are clicked) or if I click outside them. Hmm…

    Cheers,
    Mike.
    Twitter: @bsodmike

  6. @David Walsh: I also had to do this, to get the ‘outside’ detection to work:

    body {
    	height:500px;
    }
    

    If ‘body’ was not styled, it would be a tiny bar at the top that the mouse would never reach.

  7. @Miichael

    Hiding the menu works with jQuery 1.4.1, but not jQuery 1.3.2. Are you using an older version of jQuery?

    Also, thank you for this tut!

  8. @Zac: I actually abandoned the JQuery version and got the MooTools version working (see my comments in the ‘other’ article). Thanks for the tip, I’ll check my JQ version and report back :)

  9. Pao

    This is nice tutorial for beginners :D
    Thank you..

  10. Chuck Conway

    Thanks for the demo. They work great. I appreciate your coding style, it’s elegant.

  11. Peo

    I would like to second this question, as the menu emulates the drop-down element of a form – wich has this behavior.

  12. Peo

    I’m sorry, i am reffering to: mike June 10, 2010 @ 10:30 am

  13. Zef

    Thanks alot man exactly what I was after!

  14. Gfoo

    I too would like to know how to make the dropdown close when pressing the up arrow/menu1

  15. Shawn Holman

    Hi! I just remade the JS but for some reason the JS that i used it does not make the button active. I used onclicks on the menu znd here is the JS that I used:

    function showmenu1() {
        $('#dropdown1').fadeToggle();
        $('#dropdown2').fadeOut();
    }
    function showmenu2() {
        $('#dropdown2').fadeToggle();
        $('#dropdown1').fadeOut();
    }
    

    This is more simple and to exit out the box all you have to do is click on the button agian, not outside

    • Shawn Holman

      I need help to make it active please help me

  16. Mattia

    I too would like to know how to make the dropdown close when pressing the up arrow/menu1

    • Mattia

      I’ve tried modifing jquery script in this way but no way :(
      Where am I wrong?

      $(document).ready(function() {
      	$(".sign_in").click(function()
      		{
      		$("#sign_box").show();
      		return false;
      		});
      	$(".sign_in").click(function()
      		{
      		$('.sign_in').addClass('active');
      		});
      
      	$(".sign_in.active").click(function()
      		{
      		$("#sign_box").hide();
      		return false;
      		});
      	$(".sign_in.active").click(function()
      		{
      		$('.sign_in.active').removeClass('active');
      		});				
      });
      

      Thanks in advance

  17. Better way to toggle menu when parent is clicked:

    /* function to show menu when clicked */
    dropdown.bind('click',function(e) {
    if(e) e.stopPropagation();
    if(e) e.preventDefault();
    if ( dropdown.hasClass('menu-open') ) {
    hideMenu();
    } else {
    showMenu();
    }
    });

  18. gmclelland

    @Florian and David Walsh – I took your changes and made a jsfiddle at http://jsfiddle.net/gmclelland/W8PgX/

    Now the hitting the “enter” key collapses the popup correctly.

    The only problem is that clicking the document doesn’t close the popup anymore.

    Any ideas on how to fix this?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!