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-5.png

Recent Features

Incredible Demos

  • Google-Style Element Fading Using MooTools or&nbsp;jQuery

    Google recently introduced an interesting effect to their homepage: the top left and top right navigation items don't display until you move your mouse or leave the search term box. Why? I can only speculate that they want their homepage as...

  • Multiple Background CSS&nbsp;Animations

    CSS background animation has been a hot topic for a long time, mostly because they look pretty sweet and don't require additional elements.  I was recently asked if it was possible to have multiple background animations on a given element and the answer is yes...with...

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!