Article

Responsive CSS3 Multi-Level, Fly-Out Menu (with JavaScript enhancement)

Here is another responsive menu that adds a desktop layout to the earlier CSS3 Multi-Level, Off-Canvas Mobile Menu (no JavaScript).

This menu differs from the CSS-only version only in that it uses a smidge of JavaScript to close all the menus/sub-menus, and hide the off-canvas menu again, in one click (in mobile view), whereas the CSS-only version requires all opened sub-menus to be closed in turn, before the user is able to send the menu back off-canvas and bring content back in to view. Please read the Notes section for details about JavaScript enhancement.

Desktop (> 960px): Look left to see the always-visible vertical menu, and hover/tap to trigger subs...

Mobile (< 960px): Click/tap that    icon in the top-right corner to open the "off-canvas" menu...

Features

A CSS3, responsive menu that;

  • Supports unlimited sub-menus, of infinite depth *
  • Uses a nested list format
  • Uses CSS3 transitions to animate the "off-canvas" slide effect (no JavaScript)
  • Uses the "advanced checkbox hack" to activate "off-canvas" sub-menus (for Android/iOS)
  • Uses "translate3d" to force hardware acceleration in WebKit (no flicker)
  • Has a "Go-Up" option to change the vertical direction of fly-out sub-menus
  • Is only slightly enhanced with JavaScript and degrades gracefully with it turned off

* Although an infinite number of nested sub-menus is possible, you'll probably want to stop at 2 or 3 deep due to the sub-menus that progressively fly across the screen in desktop view.

Please view the source of this web page to grab the JS, CSS and HTML markup.

Go-Up (desktop view)

Long nested menus will eventually fall off the bottom of the screen. To counter this, a "go-up" option is available to change the vertical direction of sub-menus. In otherwords, to make them go upwards. The "Focus on Function > Services" menu is an example of this.

To trigger the "go-up" menu, just put a class of "go-up" on the parent <li> tag;

<li class="go-up"><a href="#">Services</a> <label for="fof-services" class="toggle-sub" onclick="">&#9658;</label>
<input type="checkbox" name="nav" id="fof-services" class="sub-nav-check"/>    
 <ul id="fof-services-sub" class="sub-nav">
    <li class="sub-heading">Services <label for="fof-services" class="toggle" onclick="" title="Back">&#9658;</label></li>
    <li><a href="#">Content Management</a></li>
    <li><a href="#">Graphic Design</a></li>
...

CSS Transforms and the "position:fixed" bug in Safari

When CSS Transforms are used (as this menu does), there is a known buggy behaviour in Safari that affects child "position:fixed" elements. What happens is that it seemingly forces them to adopt a "position:absolute" placement, while forcing their immediate parents to "position:relative". This causes some very strange (and frustrating) results. There is an easy fix here though - apply this CSS if you have any "position:fixed" elements inside ".container";

/* #### - corrects 'unfixing' bug in Safari - #### */
@media screen and (-webkit-min-device-pixel-ratio:0) { .container { -webkit-transform:none !important } }

Notes (JavaScript enhancement for mobile view)

The first change to the CSS-only version of this responsive menu is that all the checkboxes are named, e.g;

<input type="checkbox" name="nav" id="something-unique" class="sub-nav-check" />

And the bit of JavaScript below is used to remove all checks from checkboxes, thus closing all of the menus/sub-menus at once (in mobile view) instead of needing to close each in turn. I've also included a snippet that adds a class of "js" to the <html> element when JavaScript is enabled which, when coupled with the appropriate CSS, makes the menu degrade back to the CSS-only version when JavaScript is turned off;

<script>
document.documentElement.className = 'js'; // adds class="js" to <html> element
function uncheckboxes(nav){
    var navarray = document.getElementsByName(nav);
    for(var i = 0; i < navarray.length; i++){
        navarray[i].checked = false
        }     
    }
</script>

The JavaScript function is called inside an additional  ×  label, placed just above the closing #menu div, which closes the menu and sends all mobile navigation back off-canvas. Note that there is no "for" attribute in the label - for some reason, things are a bit buggy with it included;

    <label class="toggle close-all" onclick="uncheckboxes('nav')">&times;</label>
</div><!-- closing "#menu" -->

Now to make the new  ×  label look pretty, and also move the large "back"   arrows out of the way, add the following CSS to the mobile area of the stylesheet. Note the ".js" selectors that only apply when JavaScript is enabled;

#menu .close-all { display:none }
.js #menu .close-all { display:inline }
.js #menu .toggle { top:0; z-index:9999 }
.js #menu .sub-nav .toggle { left:0.15em; width:1em }

Finally, hide the  ×  label when in desktop view by adding this to the desktop CSS;

.js #menu .close-all { display:none }

Compatibility

  • Desktop: IE7+ (no fly/fade effect in IE7/8/9) / Mobile: IE9+ (no slide effect in IE9)
  • Chrome
  • Safari
  • Firefox
  • Opera
  • Android
  • iOS

Please be aware that touch screen desktop/tablet users have to tap the indicator arrows to open sub-menus, and fly-out sub-menus are likely to obscure page content until another link is clicked (see the iOS 'Sticky Hover' fix).

I haven't been able to test in other browsers/devices but feedback is always welcome. Contact me if anything goes awry in other browsers.

Related

Freebies

Looking for more freebies for your website? Grab a bunch of free PHP, CSS and JavaScript goodies, from flat file CMS' to RSS managers to responsive CSS menus, galleries and sliders.