Create Twitter-Style Dropdowns Using MooTools

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 MooTools.

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; }

/* 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 MooTools JavaScript

window.addEvent('domready',function() {
	(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.setStyle('display','none');
			}
		};
		/* recurse through dropdown menus */
		$$('.dropdown').each(function(dropdown) {
			/* track elements: menu, parent */
			var menu = dropdown.getNext('div.dropdown-menu'), parent = dropdown.getParent('div');
			/* function that shows THIS menu */
			var showMenu = function() {
				hideMenu();
				showingDropdown = dropdown.addClass('dropdown-active');
				showingMenu = menu.setStyle('display','block');
				showingParent = parent;
			};
			/* function to show menu when clicked */
			dropdown.addEvent('click',function(e) {
				if(e) e.stop();
				showMenu();
			});
			/* function to show menu when someone tabs to the box */
			dropdown.addEvent('focus',function() {
				showMenu();
			});
		});
		/* hide when clicked outside */
		$(document.body).addEvent('click',function(e) {
			if(showingParent && !e.target || !$(e.target).getParents().contains(showingParent)) { 
				hideMenu();
			}
		});
	})(document.id);
});

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!

Look forward to a MooTools Class version soon. Also look forward to jQuery and Dojo versions!

Recent Features

  • By
    Responsive Images: The Ultimate Guide

    Chances are that any Web designers using our Ghostlab browser testing app, which allows seamless testing across all devices simultaneously, will have worked with responsive design in some shape or form. And as today's websites and devices become ever more varied, a plethora of responsive images...

  • By
    CSS vs. JS Animation: Which is Faster?

    How is it possible that JavaScript-based animation has secretly always been as fast — or faster — than CSS transitions? And, how is it possible that Adobe and Google consistently release media-rich mobile sites that rival the performance of native apps? This article serves as a point-by-point...

Incredible Demos

  • By
    background-size Matters

    It's something that makes all men live in fear, and are often uncertain of. It's never spoken, but the curiosity is always there. Nine out of ten women agree in the affirmative. Advertisers do their best to make us feel inadequate but...

  • By
    Redacted Font

    Back when I created client websites, one of the many things that frustrated me was the initial design handoff.  It would always go like this: Work hard to incorporate client's ideas, dream up awesome design. Create said design, using Lorem Ipsum text Send initial design concept to the client...

Discussion

  1. Simple, yet effective. I also really like the Twitter login form… takes no space from the UI, and have everything a forms would need…

    Thanks for the tut.

  2. Ahmed

    How about an option to manipulate where the sub-menu will appear, top or bottom?

  3. This is great. I was just recently wondering how to make a menu close if someone clicks off of it.

    Thanks.

  4. Nick

    Cool.
    Though the dropdown should also close when clicking on the Menu1 ‘button’, as indicated by the arrow change. (Firefox 3.6)

  5. @Nick: Ah yes, I shall update the arrows.

  6. I have a problem with the menu pull down going off screen. I have monitor setup as 900×1440. I don’t have any way to scroll the screen over to even read any of the menu. It works fine if I use monitor in 1440×900 mode and I like the concept.

    Shouldn’t there be a way for the browser to know that you’ve pasted something into the DOM that is now potentially off screen?

    I wouldn’t use this because the usability hit for folks.

  7. would love to see a jquery version eventually =)

  8. Your implementation is nice – but I have to say I *hate* that feature of twitter.

    For some reason their “remember me” setting doesn’t work, so every time I go to the site I have to log in. They compound the problem by adding an extra click on the page in order to access the login. I actually wrote a greasemonkey script to undo it (you can also go to twitter.com/login and get the same affect – but for some reason it didn’t occur to me check for that before I wrote it)

  9. Elizalde P. Castillo

    @Ryan Rampersad: i have a thesis project and were using php and mysql programming language. actually i have little bit background for this software and its hard to build transaction system with this bec im just only a beginner.. i want to explore more using php and please help me with my problem? and wanting also to learn mootools later on. i just want to learn those pop-up messages and icon, those amazing effects. i hope you consider me to help. thank you and God Bless!

  10. Nice one david…
    Liked the trick the hide menu
    “showingParent && !e.target || !$(e.target).getParents().contains(showingParent)”

    Will definitely use it in some of my project

  11. One more thing. It would have been better if the menu could adjust to right or left depending upon the screen size.

    On lower screen resolution Menu 1 goes off the screen.

  12. @Nitin, @Dwight Blubaugh: Can you provide a screenshot?

  13. @David Walsh: Emailed it to you.

  14. @David Walsh: I’d like to see how we can get the menus to go back up when they are clicked again. I tried to use dropdown.toggle(); but the event would cause the page to go to /demo.

    Hmm…..

  15. Seems I got it working, probably not the best way to go about it. Created a global variable called ‘tmp’ and had it initialized to false.

    /* function to show menu when clicked */
    dropdown.addEvent('click', function(e) {
    	if(e) e.stop();
    	if(tmp == false){
    		showMenu();
    		tmp = true;	
    	}else{
    		hideMenu();
    		tmp = false;
    	}
    });
    

    This does the trick though; menus are collapsible now. To keep it all in sync, this needs to be added too:

    /* hide when clicked outside */
    $(document.body).addEvent('click',function(e) {
    	if(showingParent && !e.target || !$(e.target).getParents().contains(showingParent)) { 
    		hideMenu();
    		tmp = false;
    	}
    });
    
    • Your solution works great as long as there is only 1 single dropdown involved. In my case I need to display 3 dropdowns.

      That happens: I open menu1 with a click. When clicking on menu2, menu 1 closes. I have to click menu2 again to open it. It is absolutely logical, but sadly not the behavior I was looking for ^^

      Any other ideas?

    • Alright, here is the solution: http://jsfiddle.net/PkbeZ/

      With a little help from wowenkho (http://www.mooforum.net/member/wowenkho/).

  16. m4co

    How would you do this with ‘mouseover’ instead of click?

    Mouseover seems complicated.. when the user goes to the child the list will just disappear! obviously because the you just left the ‘mouseover’ area

  17. I know that one year has passed since this guide has been posted, but I will use this drop down technique on the next update of my website.

    This drop down still works fantastic with mootools 1.3.

    Thank you for sharing this! You made my day :)

  18. bobthebob

    Again, a great tut from David. what a nice resource website it is!

    I would also like to see a mouseover event. When changing from click to mouceover or mouseenter, it works to open the menu.
    But i can not manage to have it close on mouseout.

    any idea?

  19. SkyGuy

    I tried copying all the code to make sure everything worked, so that after I can implement it in my own style, But for some reason mine won’t close when you click outside the menu. Although If I add a div like around the menu, then I can close the menu by clicking inside that div, but not by clicking anywhere on the body. Do you have any idea why that is?

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