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
    Camera and Video Control with HTML5

    Client-side APIs on mobile and desktop devices are quickly providing the same APIs.  Of course our mobile devices got access to some of these APIs first, but those APIs are slowly making their way to the desktop.  One of those APIs is the getUserMedia API...

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

Incredible Demos

  • By
    WordPress-Style Comment Controls Using MooTools or jQuery

    WordPress has a nice little effect on the Admin Dashboard where it shows and hides the comment control links when you mouseover and mouseout of the record's container. Here's how to achieve that effect using MooTools or jQuery. The XHTML Notice that we place the links into...

  • By
    Highlight Table Rows, Columns, and Cells Using MooTools 1.2.3

    Row highlighting and individual cell highlighting in tables is pretty simple in every browser that supports :hover on all elements (basically everything except IE6). Column highlighting is a bit more difficult. Luckily MooTools 1.2.3 makes the process easy. The XHTML A normal table. The cells...

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!