Friday, June 13, 2014

CSS Only Dropdown Menu that Works on Mobile Devices

Not only is it a full moon today, but it's also Friday the 13th. So it's only fitting that I tackle a scary topic: CSS only dropdown menus that work on mobile devices!

Ok, so really it's not that scary and I'll try to make it as painless as possible... don't mind the machete.

First off, it's nice to see what we're trying to accomplish. Because this example is geared for mobile type devices only you should be viewing this example on such a platform, or if you are using a desktop computer, pretend that you can only "tap":

The actual presentation of the example is not critical; you can create your own design in pretty much whatever manner you wish. The focus here is that on a mobile device one can tap a menu to see the dropdown list that will then stay open until the user taps another menu item or the "Close Menu" option.

Secondly, I have not bothered to create media queries to target small screen sizes, but if you are attempting to do this for yourself, then the CSS code should be targeted to the mobile realm, with a desktop version for those who have larger screens and mice to surf with (so in the desktop version you could do the typical open on hover actions). Now let's get started.

I generally like to start off with the HTML structure of the page, so let's examine that first:

<div id="wrapper">
            <li><a href="#">HOME</a></li>
            <li><a href="#about">ABOUT US</a>
                <ul id="about">
                    <li><a href="#">History</a></li>
                    <li><a href="#">Corporate Info</a></li>
                    <li><a href="#">Meet the Team</a></li>
                    <li>va href="#">Close Menu</a></li>
            <li><a href="#products">PRODUCTS</a>
                <ul id="products">
                    <li><a href="#">Bicycles</a></li>
                    <li><a href="#">Canoes</a></li>
                    <li><a href="#">Longboards</a></li>
                    <li><a href="#">Close Menu</a></li>
            <li><a href="#">CONTACT</a></li>

Overall there is nothing out of the ordinary. The div and nav elements are there to position the menu and would be more useful for a desktop version of the site. The "HOME" and "CONTACT" links are not dropdown menus and instead of a "#" for the href, one would normally place a link there like home.php or contact.html.

But the "ABOUT US" and "PRODUCTS" links have dropdown menus and their hrefs are important, "#about" and "#products", respectively. Notice how the unordered list right below each one has an id (without the #) that matches. This is where the CSS will come into play. As for the menu items that are part of the dropdown list, those should be actual links to real pages (since of course I have not made any additional pages, I've kept them the traditional "#").

So without further ado, let's work our way through the CSS.

* {
        margin: 0;
        padding: 0;
        border: 0;
    #wrapper {
        width: 100%;
    nav {
        /* background-color: grey;
        overflow: hidden; */

Nothing too special here. There's a quick reset in the universal selector, a little sizing for the wrapper div (overall this wouldn't do much as block elements are 100% in width anyway, but imagine if there was a desktop version to this CSS where the width was set to 80%; we need to override that value so we can use up the whole width on the mobile device), and lastly the nav, which like the div would have a more significant part to play when targeted for desktops (hence why the properties are commented out).

ul li {
        min-width: 50%;
        list-style-type: none;
        float: left;

This bit decorates the first level list items. The min-width set to 50% gives us the two column look and the list-style-type set to none removes the bullets. The float is required or else the list would be vertical.

ul li a {
        display: block;
        background-color: blue;
        color: white;
        text-decoration: none;
        text-align: center;
        font-weight: bold;
        font-family: sans-serif;
        padding: .25em 2em;
    ul li a:hover {
        background-color: darkblue;

Although these parts look like a lot, all they do is provide the look and feel for the links. The anchors are set to the block display type and when the first-level links are hovered over (at least on a desktop) they will appear dark blue in colour.

ul li ul {
        display: none;
        position: absolute;
        border: 1px solid black;
        min-width: 49%;

This next section is quite significant in this example. If you follow the descendant selector, you'll notice that we are working with the dropdown menus. We don't want these to appear unless the user clicks on a top-level link, so the display is set to none. To ensure the menu opens up in a nice position, the typical setting for position is set to absolute. Since I added a border, min-width has to be a bit smaller than 50% or else the menu will look a little odd (try it). On a quick side note, I could have used the CSS calc function for the width (e.g. calc(49% - 2px), but it does not work for every browser equally and also causes the CSS validation to fail.

ul li ul li {
        float: none;

This next little bit overrides the float for the list items in the dropdown menu. If you don't do this, the menu will appear horizontal.

ul li ul li a {
        margin: 0;
        padding: .25em;
        box-shadow: 2px 2px 3px black;
        text-align: left;
    ul li ul li a:hover {
        background-color: purple;

These two rules simply decorate the links in the dropdown menu. Lastly, here the magical part:

#about:target {
        display: block;
    #products:target {
        display: block;

Remember how we gave the dropdown lists (specifically the ul elements) an id? Here they are with the target pseudo-classs. The only property adjusted is display being set to block, which as can be guessed displays them on the click.

The way the target pseudo-class works is like a toggle. When someone clicks a link with the same name as the id (like "PRODUCTS" has an href of "#products"), CSS looks looks for the element with that id and executes the declarations in the related rules with the target pseudo-class. In our case, all that happens is that the display is set to block to show the dropdown list. Clicking other links will simply revert that element back to the non-target rule that applies to it, hence why when you click another menu item or the "Close Menu" link (with an href of "#"), the dropdown disappears. Read more about the target pseudo-class here:

Awesome stuff. I think one of the biggest pros to this method is that we don't have to use JavaScript. Not that I'm normally against it, and ultimately it wouldn't necessarily be wrong to use it, but for non-programmers and to reduce the number of requests made to a server, this is definitely a plus. The tap-to-open a menu is also going to be familiar to almost all mobile / tablet type device users too, so we're not sacrificing much in regard to the design of our website; it still pretty much looks and works like it would on a desktop system without having to completely redesign the navigation in some manner.

But as with almost everything there are some downsides. For one, since we haven't employed any JavaScript, the menus will stay open indefinitely unless they are manually shut by the user (hence the addition of the "Close Menu" item). In addition, on desktop computers where a mouse can be used, the menu does not open when the cursor is hovered over a link. Although it can be done, the menu starts acting goofy. For example, they'll open on hover but won't close when another menu opens, amongst some other things (there was another example of this where the menu had to be clicked first and then the hover would click in). Then again, the only time this would be an issue on a desktop is if media queries where not used.

So my suggestion is not to look for an all-encompassing method for dropdrop menus if you're going to use a CSS only technique. As responsive design is becoming the norm, create media queries and CSS files targeting various screen sizes and platforms and then it becomes a little more straightforward to design not only the look of your website, but it's behaviour in various cases.

So that's it for now. I have plenty more tutorials but if you have suggestions, please leave a comment. L8r!

No comments:

Post a Comment

You may comment, but if it's spam or advertising, I'll be deleting your comment and blocking your arse!