O'Reilly

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.

Track.js Error Reporting

Upcoming Events

Recent Features

  • Serving Fonts from CDN

    For maximum performance, we all know we must put our assets on CDN (another domain).  Along with those assets are custom web fonts.  Unfortunately custom web fonts via CDN (or any cross-domain font request) don't work in Firefox or Internet Explorer (correctly so, by spec) though...

  • 5 Awesome New Mozilla Technologies You&#8217;ve Never Heard Of

    My trip to Mozilla Summit 2013 was incredible.  I've spent so much time focusing on my project that I had lost sight of all of the great work Mozillians were putting out.  MozSummit provided the perfect reminder of how brilliant my colleagues are and how much...

Incredible Demos

  • CSS Filters

    CSS filter support recently landed within WebKit nightlies. CSS filters provide a method for modifying the rendering of a basic DOM element, image, or video. CSS filters allow for blurring, warping, and modifying the color intensity of elements. Let's have...

  • Chris Coyier&#8217;s Favorite CodePen Demos

    David asked me if I'd be up for a guest post picking out some of my favorite Pens from CodePen. A daunting task! There are so many! I managed to pick a few though that have blown me away over the past few months. If you...

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!

Recently on David Walsh Blog

  • OâReilly Velocity Conference â New York

    My favorite front-end conference has always been O'Reilly's Velocity Conference because the conference series has focused on one of the most undervalued parts of client side coding:  speed.  So often we're so excited that our JavaScript works that we forget that speed, efficiency, and performance are just as important. The next Velocity...

  • Free Download: Font Bundle Featuring 17 Incredible Typefaces

    The only thing we love more than a good font, is a good free font. So we’ve combed the Web for some of our favorite free fonts, and gathered them here in a single download. You’ll find a variety of useful typefaces, from highly geometric designs...

  • OâReilly Velocity Conference â Amsterdam

    My favorite front-end conference has always been O'Reilly's Velocity Conference because the conference series has focused on one of the most undervalued parts of client side coding:  speed.  So often we're so excited that our JavaScript works that we forget that speed, efficiency, and performance are just as important. The next Velocity...

  • CanIUse Command Line

    Every front-end developer should be well acquainted with CanIUse, the website that lets you view browser support for browser features.  When people criticize my blog posts for not detailing browser support for features within the post, I tell them to check CanIUse:  always up to date, unlike...

  • Generating Alternative Stylesheets for Browsers Without @media

    If your CSS code is built with a mobile-first approach, it probably contains all the rules that make up the "desktop" view inside @media statements. That's great, but browsers that don't support media queries (IE 8 and below) will simply ignore them, ending up getting the...