Progressive And Responsive Navigation

Advertisement

Developing for the Web can be a difficult yet rewarding job. Given the number of browsers across the number of platforms, it can sometimes be a bit overwhelming. But if we start coding with a little forethought and apply the principles of progressive enhancement from the beginning and apply some responsive practices at the end, we can easily accommodate for less-capable browsers and reward those with modern browsers in both desktop and mobile environments.

screenshot

A Common Structure

Below is the HTML structure of a navigation menu created by WordPress. This unordered list is pretty common for content management systems and hand-coded websites alike. This will be the basis for our work.

Please note: Any ellipses (…) in the snippets below stand in for code that we have already covered. We have used them to shorten the code and highlight the parts that are relevant to that section.

<nav class="main-navigation">
   <ul>
      <li><a href="#home">Home</a></li>
      <li>
         <a href="#about">About Us</a>
         <ul class="children">
            <li><a href="#leadership">Leadership</a></li>
            <li><a href="#involvement">Involvement</a></li>
            <li><a href="#vision">Our Vision</a></li>
         </ul>
      </li>
      <li><a href="#clients">Our Clients</a></li>
      <li>
         <a href="#support">Support</a>
         <ul class="children">
            <li><a href="#blog">Blog</a></li>
            <li><a href="#downloads">Downloads</a></li>
            <li><a href="#faq">FAQ</a></li>
         </ul>
      </li>
      <li><a href="#contact">Contact Us</a></li>
   </ul>
</nav>

Unstyled Navigation
Our navigation, unstyled.

Our Tools

  • CSS Reset
  • HTML5 elements
  • LESS CSS
  • jQuery

CSS Reset

Resetting our CSS styles is where we’ll start. Browsers have different default styles for the elements we’ll be using, so understanding this and getting all of the elements to look the same is important. In this example, since we’re using an unordered list, there will be default left padding, top and bottom margins, and a list-style. You can either deal with these individually or, if you’re project will include more than just this navigation, use a reset to clear all of the styles and start fresh. I prefer Eric Meyer’s Reset CSS, but there are a few others to choose from, listed below. Whichever you choose, make sure it accounts for the new HTML5 elements.

HTML5 and CSS3 Elements

We’ll be wrapping the menu in HTML5’s nav element, which is one HTML5 feature that we should be using right now. If you need more good reasons to use HTML5 in your daily work, such as accessibility, then read “Top 10 Reasons to Use HTML5 Right Now” over at Codrops.

CSS3 will give our menu the progressive feel we’re looking for. We can use nifty effects such as linear gradients, text and box shadows and rounded corners, while providing a reasonable appearance for browsers that are dragging their feet. You could also consider using something like CSS3 Pie in the process. This will give those lagging browsers most of the functionality they lack to display your CSS3 properties.

LESS CSS

To make our CSS more efficient, we’ll use LESS along with a class file to ease the difficulty of dealing with all of those browser prefixes. Other options, such as Sass and Compass, do effectively the same thing and might better fit your particular development environment. If you’re interested in learning more about LESS and how it compares to Sass, check out another article of mine, “An Introduction to LESS, and Comparison to Sass.”

jQuery

To make our navigation a little friendlier in small browsers, such as those on mobile devices, we’ll use JavaScript. Essentially, we will gather all of the elements in our navigation and reorganize them into a select form element. Then, when the user selects an option from the list, they will navigate to that page. Interaction with a select element is one of the easiest and best ways to handle navigation in a small window. The practice is pretty common as well, so the learning curve for users will be gentler.

Getting Started

After applying a reset, we get something like the following. You can see that the margins, padding and list styles have been cleared.

Reset navigation
Reset navigation

Child-Level Menus

For now, the child-level menus will only get in the way. The best thing to do is remove them from the equation and add them back in when it’s time to style them. To achieve this, we will apply position: relative to all of the list elements, and move the children off screen until they are needed.

.main-navigation {
   li {
      position: relative;
   }
   .children {
      left: -999em;
      position: absolute;
   }
}

Applying left: -999em; position: absolute; will move the children to the left of the screen by a significant margin. This method is preferable to just using display: none because it is more accessible to screen readers.

Unstyled without children
Unstyled without children

Common Navigation Styles

Every navigation menu will probably have links in it. But these links are not like the links we see in the main body of the page, which are blue, underlined and distinguishable from the surrounding text. Rather, links in the navigation will stand alone, and their function will be obvious. That being said, the links in a nav element will probably have a few features of their own that distinguish them from typical anchor tags.

nav {
   a {
      color: inherit;
      display: block;
      text-decoration: none;
   }
}

Thus, a link will inherit the color of the text assigned to the parent element, in this case nav. It will be set to display as a block-level element, because we want the clickable area to be large and we do not want underlining (because that would just look funny).

Navigation with more functional links
Navigation with more functional links

Please note: color: inherit is not supported in IE 6 or 7. If you need to support those browsers, then you will need to explicitly set the color that you want.

Lining Up

Getting the menu in line calls for the use of floats. Initially, we’ll float all of the elements in the nav element to the left. Later, we’ll undo this property for the child-level menus, along with a lot of the other styles that we’ll set along the way.

.main-navigation {
   ul, li, a {
      float: left;
   }
   …
}

Inline navigation
Inline navigation

Because every element in the nav element is now floated, the element itself will collapse as though it were empty. There are a few ways to deal with this. One is to also float the nav element itself, which will expand it to wrap around its contents. If need be, you can set it to width: 100% to fill any remaining space to the right. Or you could use Nicolas Gallagher’s “micro” clearfix solution, which essentially adds clear: both just before the closing of the nav element.

/* For modern browsers */
.cf:before,
.cf:after {
    content:"";
    display:table;
}
.cf:after {
    clear:both;
}
/* For IE 6/7 (trigger hasLayout) */
.cf {
    zoom:1;
}

Because we’re using LESS for our CSS, applying the clearfix to our main-navigation class without modifying the markup is very easy.

.main-navigation {
   .cf;
   …
}

We’ll see more of this, as well as a description of how this works, in the section titled “Rounded Corners and Gradients” below.

Styling

All righty. By now, you’re probably as tired of looking at an unstyled menu as I am. To start, we’ll build what looks like a block wall, and then chisel a nice menu out of it. We won’t serve the block wall to antiquated browsers, but it’s a good start anyway.

Background Color and Borders

.main-navigation {
   font-size: 0.8em;

   ul, li, a {
      …
   }
   ul {
      background: #eee;
      border: 1px solid #ddd;
   }
   li {
      …
      border-right: 1px solid #ddd;
   }
   li:last-child {
      border-right: none;
   }
   a {
      height: 35px;
      line-height: 35px;
      margin: 3px;
      padding: 0 15px;
   }
   .children {
      …
   }
}

In the code above, the text was just too big, so we shrunk it with font-size: 0.8em. This property is set on the main-navigation class, so it applies throughout the navigation. The top-level unordered list has a border: 1px solid #ddd property to break it out from the page. Each list item element is given a border-right: 1px solid #ddd; to separate it from each other. The li:last-child selector targets the last list item element in the unordered list, removing the right border because no item follows it.

The links within the navigation are given a background color and some left and right padding to add distinction and increase their clickable area. We’re fixing the height and line-height, instead of using top and bottom padding, so that we can predict more accurately where the child-level menus will be positioned relative to their shared parent list item.

Navigation resembling a block wall
Navigation resembling a block wall

Rounded Corners and Gradients

.main-navigation {
   …
   text-shadow: 0 1px 1px #fff;

   ul {
      border: 1px solid #ddd;
      .border-radius();
      .linear-gradient();
   }
   …
}

.border-radius (@radius: 5px) {
   border-radius: @radius;
}
.linear-gradient (@start: #fff, @end: #ddd, @percent: 100%) {
   background: @start; /* Old */
   background: -moz-linear-gradient(top,  @start 0%, @end @percent); /* FF3.6+ */
   background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,@start), color-stop(@percent,@end)); /* Chrome, Safari 4+ */
   background: -webkit-linear-gradient(top,  @start 0%,@end @percent); /* Chrome 10+, Safari 5.1+ */
   background: -o-linear-gradient(top,  @start 0%,@end @percent); /* Opera 11.10+ */
   background: -ms-linear-gradient(top,  @start 0%,@end @percent); /* IE 10+ */
   background: linear-gradient(top,  @start 0%,@end @percent); /* W3C */
}

Above, we have created two new classes, border-radius and linear-gradient.

The border-radius class is actually what LESS developers refer to as a parametric mixin. Essentially, it’s like a class, but you can pass variables to it in case the default value isn’t exactly what you want. In this case, if 5 pixels isn’t what you want, you could reference the mixin as .border-radius(10px), and then it would use 10px instead of the original 5px. With the border-radius property, you could also pass it something like .border-radius(5px 0 0 5px), and it would apply the 5-pixel rounding to only the top-left and bottom-left corners. For more information and possibilities on border-radius, see “Border-Radius: Create Rounded Corners With CSS” at CSS3.info.

Another parametric mixin is linear-gradient. But with LESS, you can add classes to other selectors and it will apply the same styles—thus negating the need to modify the markup just to add another class (and, by extension, its styles) to an element. Both of the classes I’ve created cover the possibilities of browser syntax. Currently, Webkit has two different syntaxes, because for some reason the browser makers decided to ignore the specification when first implementing it and made up their own syntax. With Chrome 10 and Safari 5.1, they went back to the specification, joining the other browsers, and made things a little easier for us. However, if you still care about the previous versions, you’ll need to add their crazy syntax as well. We’ve also added a white text-shadow to the text in the navigation to give it a slightly beveled look.

Navigation with a gradient and rounded corners
With the two classes applied, you can see the slight gradient and the rounded corners.

Some browsers do not support CSS3 gradients. Yes, I’m looking at you, Internet Explorer 6, 7, 8 and 9. If you want to use something other than the filter syntax for gradients, you’ll have to wait for version 10. In the meantime, either you could use the filter syntax for IE (see the “For Internet Explorer” section of “Cross-Browser CSS Gradient”) and put them in an IE-specific style sheet, or you could use an image gradient. You could also just leave them without the gradient, but that’s not the point here.

Parent-Level Hover States

.main-navigation {
   …
   li:hover {
      a {
         .linear-gradient(#dfdfdf, #c0bebe, 100%);
      }
      .children {
         …
         a {
            background: none;
         }
      }
   }
   …
}

The code above will trigger the hover state for anchor elements when the user hovers over their parent list item, rather than hovering over the anchors themselves. This way is preferable so that the anchor element maintains its hover state when the user is mousing over the child-level menu as well. Doing it this way does, however, create the need to reset the background color of anchor elements within the child-level menus. That’s the part you see within the children selector.

Hovering over the parent-level links
Hovering over the parent-level links

Displaying the Children

Bringing the children back onto the screen is easy enough. But before we get carried away, we need to clear out a few styles that are applied to all unordered lists, list items and anchors.

.main-navigation {
   …
   .children {
      background: #fff;
      left: -999em;
      position: absolute;

      li, a {
         float: none;
      }
      li {
         border-right: none;
      }
   }
}

The code above changes the background of the child-level menu to white, instead of the light gradient that we used in the parent-level menu. The next couple of lines remove the left float from the list items and anchors. We’ve also gotten rid of the right border that separates the list items in the parent-level menu.

The Hovering Box

.main-navigation {
   …
   .children {
      background: #fff;
      .box-shadow();
      left: -999em;
      margin-left: -65px;
      position: absolute;
      top: 30px;
      width: 130px;
      …
   }
}

…
.box-shadow (@x: 0, @y: 5px, @blur: 5px, @spread: -5px, @color: #000) {
   -moz-box-shadow: @x @y @blur @spread @color;
   -webkit-box-shadow: @x @y @blur @spread @color;
   box-shadow: @x @y @blur @spread @color;
}
…

We’ve added another parametric mixin to the equation. This one produces the box shadow, with all of its parameters as variables, and with the browser prefixes. We’ve borrowed the styles from .children to make the box appear to hover over the parent menu. To center the child underneath the parent element, we’ve set the left position to 50%, and set the left margin to the negative value of half the width of the child. In this case, the child level menu is set to 130 pixels wide, so we’ve set the left margin to -65 pixels.

Navigation w/child reset to hover style
Navigation with the child reset to hover style

Child-Level Hovers

.main-navigation {
   …
   .children {
      …
      a {
         .border-radius(3px);
         height: 30px;
         line-height: 30px;
         margin: 3px;
      }
      a:hover {
         background: #dff2ff;
      }
   }
}

We’re using the parametric mixin that we created for the border-radius for the links in the children as well. Adding a 3-pixel margin and 3-pixel border radius to all of the anchor elements within the child menu will accent the 5-pixel border radius of the menu well. We’ve also adjusted the height and line height a little, because they just seemed too high. Finally, we gave the list items a nice soft-blue background color on hover.

Navigation w/child menus and their hover state
Navigation with child menus and their hover state

Responding to Mobile Browsers and Size Constraints

A lot of screen sizes and browsers are out there. The iPhone has had two resolutions. Up to the 3GS model, it was 480 × 320; since the iPhone 4, it has been 960 × 640. Android browsers run from 480 × 320 to 854 × 480. Android also has a lot of browsers to choose from. There are the usual Firefox and Opera, as well as a ton of browsers by small start-ups. You can get Opera for the iPhone, but since you can’t make it the default browser, you’re pretty much stuck with Safari. Given this variety, we’ll have to make some adjustments if we want our navigation to be useful on all devices and in all browsers.

Fitting the Content

Accomplishing this part is easy, but doing it will probably require adjusting our styles. But that’s why we’re here, isn’t it?

Currently, when we open the navigation demo in iOS, it looks like this:

Original navigation in iOS
Original navigation in iOS

This might not look too bad on a giant screen, and it might even be usable on the iPad, but you would struggle to use it on a phone. Zooming in might make it easier, but that’s not ideal. Optimizing for the device is preferable, and forcing the browser to use the available space is simple.

<meta name="viewport" content="width=device-width">

This alone makes a huge difference in the way the browser renders the page. And while the menu is not the prettiest it’s ever been, it is a lot more functional.

Navigation on iOS with the viewport adjusted
Navigation on iOS with the viewport adjusted

Media Queries

We can use media queries to adjust the styles based on the media in the browser. In this case, we’ll use the width of the page to change the look and feel of the navigation to make it more suitable to the available space. In this case, we’ll make the menu items more button-like.

@media only screen and (max-width: 640px) {
   .main-navigation {
      ul {
         border: none;
         background: none;
         .border-radius(0);
      }
      li {
         border-right: none;
      }
      a {
         border: 1px solid #ddd;
         .border-radius();
         font-size: 1.2em;
         height: auto;
         .linear-gradient();
         line-height: 1em;
         padding: 15px;
      }
   }
}

In the code above, we’ve used a media query to target situations in which the user is only looking at a screen and in which the width of the window is a maximum of 640 pixels. In this scenario, we’ve removed the border, background and border radius from the unordered list, and applied those styles to the anchors themselves. We’ve also increased the font size of the anchors, cleared the height and line height, and adjusted the padding of the links to increase the clickable area.

Navigation adjusted for mobile display
Navigation adjusted for mobile

As you can see, the links look much friendlier in a mobile browser. They are, however, only half functional, because touch devices don’t have a hover state. This means that if you have child-level menus, as we do here, you’ll have to figure out a way to display them as well. You could replace the hover state with a touch event of some kind, or expand the children out onto the page. That would greatly increase the size of the navigation, though. The following method might be best.

Replacing the Menu in Mobile Browsers With JavaScript

$(function() {
   /* Get the window's width, and check whether it is narrower than 480 pixels */
   var windowWidth = $(window).width();
   if (windowWidth <= 480) {

      /* Clone our navigation */
      var mainNavigation = $('nav.main-navigation').clone();

      /* Replace unordered list with a "select" element to be populated with options, and create a variable to select our new empty option menu */
      $('nav.main-navigation').html('<select class="menu"></select>');
      var selectMenu = $('select.menu');

      /* Navigate our nav clone for information needed to populate options */
      $(mainNavigation).children('ul').children('li').each(function() {

         /* Get top-level link and text */
         var href = $(this).children('a').attr('href');
         var text = $(this).children('a').text();

         /* Append this option to our "select" */
         $(selectMenu).append('<option value="'+href+'">'+text+'</option>');

         /* Check for "children" and navigate for more options if they exist */
         if ($(this).children('ul').length > 0) {
            $(this).children('ul').children('li').each(function() {

               /* Get child-level link and text */
               var href2 = $(this).children('a').attr('href');
               var text2 = $(this).children('a').text();

               /* Append this option to our "select" */
               $(selectMenu).append('<option value="'+href2+'">--- '+text2+'</option>');
            });
         }
      });
   }

   /* When our select menu is changed, change the window location to match the value of the selected option. */
   $(selectMenu).change(function() {
      location = this.options[this.selectedIndex].value;
   });
});

To summarize, first we’re checking whether the window is less than or equal to 480 pixels. To ensure an accurate reading on mobile devices, you can use a meta tag to scale the viewport accordingly:

<meta name="viewport" content="width=device-width">

We populate the first variable, windowWidth, with the value of the window’s width as defined by the given device. We can use this value to then check whether the width is narrower than a particular value. We’ve chosen 480 pixels here because, while we might want to use media queries to adjust the menu below 640 pixels, at a certain point the viewport would be just too small to justify the menu taking up all that space.

We then use jQuery to create a clone of our menu that we can later crawl to create our options. After we’ve done that, it’s safe to replace the unordered list with the select element that we’ll be using and then select it with jQuery.

In the largest part of the code, we’re crawling through the clone of our navigation. The selector used, $(mainNavigation).children('ul').children('li'), ensures that we go through only the uppermost list elements first. This is key to creating the nested appearance of the select menu. With it, we select the “direct” child-level unordered list elements and then their “direct” child-level list elements, and then parse through them.

Inside each of these “direct” descendants, we get the value of the href attribute and the text of the link, and we store them in variables to be inserted in their respective options. This is implemented by appending <option value="'+href+'">'+text+'&kt;/option> to our new select list.

While we’re in the top-level list item elements, we can check whether any child-level menus need to be parsed. The statement if ($(this).children('ul').length > 0) checks whether the count of the selector is greater than 0. If it is, that means child-level items need to be added. We can use that same selector, with a slight addition, to go through these elements and add them to our select list, $(this).children('ul').children('li').each().

The same parsing method applies to these elements, although they use different variables to store the values of the anchor tags, so as not to create conflicts. We have also prefixed text to the menu labels at this level, --- , to differentiate them from the other items.

Parsing through the menu in this method (nested) will create the parent-child relationship you would expect.

After the menu is created, a little more JavaScript will enable the select list to serve as navigation.

$(selectMenu).change(function() {
   location = this.options[this.selectedIndex].value;
});

When the select menu is changed, a new option is selected, and the window location is changed to reflect the value of the option. That value comes from the href of the original anchor element.

The result is like so:

screenshot
The select menu in a desktop browser

Select menu on Android and iPhone
The select menu in Android and iPhone browsers

Given the increased clickable area of the native controls, the select menu is obviously much more user-friendly on mobile.

Share Your Experience

We’d love to see and hear about some of your experiences with menus across browsers and platforms; please share below. And if you have any questions, we’ll do our best to find answers for you.

(al)

↑ Back to top

Jeremy is a Senior User Experience Developer at AREA203 DIGITAL and has a personal blog and portfolio. He is a reader of fiction, watcher of educational television, generally loud-mouthed and opinionated.

  1. 1

    James Young (@welcomebrand)

    February 13, 2012 4:01 am

    Good writeup Jeremy, for those interested there’s also the great “Touchdown” plugin on Git which also does this sort of thing: https://github.com/samuelcotterall/touchdown

    0
  2. 2

    Apart from jQuery… It’s great. Actually, I was in search of pure CSS menu :(

    0
    • 3

      Nebula Technologies

      February 6, 2014 4:29 am

      Actually, you don’t need jquery at all, and frankly I wouldn’t recommend it for this application.

      I put both the form element and the nav element in the head, and set the form display property to display:none; and the nav display:block;

      Using media queries, I change the nav element to display:none; and the form to display:block; usually when max-width is around 740px, or when ever the page navigation breaks. Also, I applaud the use here of max-width instead of max-device-width, because this works with desktops whose browser windows aren’t full screen. If you’re going to take the time to code responsively…..

      The main reason I am against the javascript is not the extra code, and not worrying about mobile browser support for javascript. Here is the buggaboo:

      The javascript code in this article will trigger (by default) as the page is loading. Unless you add an event handler, say $(window).on(“orientationchange”,function(event){ then what ever the device’s orientation at time of page load will determine the navigation presented. If the device is rotated (I’m thinking iPad or Kindle, but some newer Android phones have huge resolutions) it may make sense to use the nav element for navigation structure when in landscape mode. Using purely media queries keeps it much simpler.

      Otherwise, great article! I really like the way you begin with a focus on semantic markup.

      0
  3. 4

    lots of interesting ideas here, but i expected a bit more on different visual solutions for different screen (browser) sizes. see sm menu for instance – mindblowing

    0
  4. 7

    An alternative to the jQuery approach is to include markup for both types of navigation and use display: none; to remove one or display: block; to display one at various screen resolutions. This is the approach we are using here, at Smashing Magazine.

    0
    • 8

      This is how I’ve been doing it as well. While I love jQuery, sometimes the simplest approach is the best one.

      0
    • 9

      I was wondering how can I include select markup with WordPress? Do I have to hardcode it or is there someway to still use WP Menu system?

      0
      • 10

        Sami: Indeed there is, check out the wp_dropdown_page() function.

        0
        • 11

          Thanks Daniel, I didn’t know that function. But it’s not exactly what I was looking for.
          If I or my customer wanted to remove some pages from select menu or add some categories in there and so on, it would not be easy. That’s the beauty of WP menu system. But it’s definitely gonna be useful for some projects.

          0
    • 12

      My suggestion is to detect the browser view port dimension with JS (Client -Side) and loading mobile based template and content with PHP or ASP.net (Server-Side). Hope this will reduce the load of unwanted content and markup !!.

      -Prabahar

      0
    • 13

      @Vitaly Friedman: include markup for both? How do screen readers and search engines react to this?

      0
  5. 14

    Do you have a downloadable copy of this demo?

    0
  6. 16

    Great tutorial Jeremy! A very good example of using modern technologies with responsive approach. I will definitely use it in some future projects. Thumb up!

    0
  7. 17

    Good stuff.

    You’d be better off using first-child rather than last-child in the menu because it has better support, and I personally prefer the display: hidden option for swapping nav, but a good case study.

    0
  8. 18

    Every time you call .html() or .append() in your JS, you’re touching the DOM and causing a reflow. You could get a pretty good performance upgrade (especially on something as ubiquitous as navigation) by building all the HTML markup into a local variable (ie var newHtml = “”; newHtml += “”; etc) and then waiting until the end to make one call to:

    $(‘nav.main-navigation’).html(newHtml)

    Otherwise, this is a really good walkthrough.

    0
  9. 21

    Good write up, thanks for the effort!

    0
  10. 22

    Thanks for this very useful tutorial – source would be very useful (have to second that)!

    0
  11. 24

    You did a great job at covering all bases here, thank you.

    0
  12. 25

    A better approach would be to code for mobile first, then add a media query for larger screens. See also Luke Wroblewski’s Mobile First write-up: http://www.lukew.com/ff/entry.asp?933.

    This would work well for implementing it on an existing site, but if you’re starting off from scratch the mobile first approach is best.

    0
    • 26

      “But if you’re starting off from scratch the mobile first approach is best.” Opinion, not fact. And that’s just one of many approaches.

      0
  13. 27

    You should explain why you have to use the html5.js shim for this demo. I would imagine a number of people do not know that most versions of IE will not use the CSS for that element, therefore not displaying as expected. Use of HTML5-based elements is fine as long as people understand the pros/cons of doing so.

    Unless I missed that explanation somewhere in the article.

    0
  14. 28

    li:focus should also be included with the li:hover style. This way, the currently focused item is more apparent than just the black dotted outline, and the sub-menu is visible when focusing the parent li.

    0
  15. 29

    Cool walk through.

    A few notes for those who want to implement this:

    You probably want to cache $(this) in a local var; it’s easy to forget that calling $(this) is actually calling the jQuery function to build the object.

    Also, definitely agree that it is important to build your HTML string and append/modify the DOM once (single reflow). This is actually most efficient if you can build the string via an array join. somearray.join(” “); will join all strings in the array with a space in between.

    0
  16. 30

    im a noobie to this, it would be nice if you have the full working live demo link not the downloadable source code above, if its okay. thank you much appreciated. regards Emmanuel

    0
  17. 31

    Great writeup! I have been in the middle of converting my blog to a responsive layout and the navigation I’ve found to be the toughest overall, luckily for me I have a simple navigation structure. I am sure I’ll come back to this for reference!

    0
  18. 32

    Benjamin Christine

    February 14, 2012 1:28 am

    Really nice post, going to be very useful! thanks!

    0
  19. 33

    Another alternative for the mobile version of the navigation is to put it at the bottom of the page (as nested lists, possibly that accordion open when tapped), and just put a ‘menu’ or ‘navigation’ link at the top of the page to scroll down to it. Depending on the context, this can make it easier to scan and use than a select menu, particularly if there are a lot of navigation items.

    0
  20. 34

    poor choice using a css framework to write your css for a tutorial. very poor.

    0
    • 35

      Please, elaborate. What part of LESS do you think is detrimental to learning?

      0
      • 36

        While I don’t think it was a “very poor” choice, it would be difficult for a beginner to see your process when using a framework. Especially those unfamiliar with {less} or {sass}.

        But for those that understand them, this was an awesome write-up. Well done :)

        0
    • 37

      @nick: LESS is a css pre-processor NOT a css framework. Did it get a bit confusing for you?

      0
  21. 38

    Some pointers in the javascript. In the article, the nav-menu is overwritten using jquery’s ‘html’ and then ‘appended’ with the li children. It is important to realize that each time you use ‘append’ and ‘html’ within the parent document object you are causing a document reflow. It is best to generate the select-element nav menu using documentFragment, then append/overwrite the ul menu with the new document fragment:

    Also it pointed out to change the browsers url by assigning a value to the location object:
    location = this.options[this.selectedIndex].value

    This will most likely not play well with the browser history, ie you won’t be able use the back button in the browser to come back to the page you just left after clicking that select menu navigation link. It would be best to use the ‘assign’ method of the location object:
    var newLocation = this.options[this.selectedIndex].value;
    location.assign(newLocation);

    0
  22. 39

    Very nice read. I did have a question that is different how I normally handle things. Why did you clone your navigation before looping through it? Why not just do: var mainNavigation = $(‘nav.main-navigation’)? Is it better to clone?

    0
  23. 40

    Hey Jeremy. You did a great job with this tutorial! This is extremely helpful as I am trying to learn more about making mobile friendly navigation menus. I just had one question/comment… I’m trying to use the files that you provided, and I have tried on 3 servers, and they don’t seem to be recognizing the style.less file. When I upload it (along with the Jquery plugin), it doesn’t show. All my browsers see are an unordered list. I have tried this in both Firefox and Google Chrome. I also tried bringing the style.less into a CSS style sheet and editing it so that CSS reads it, and for the most part it looks pretty good, but I can’t get my drop down when I hover over “About Us” and “Support”. Do you have any advice/ style sheet solutions? Thanks for your help! :)

    0
    • 41

      Odd that your servers can’t find it. There is a known issue with LESS and Chrome if the file is called via “file:///” but if you’re uploading them to a LAMP server or something similar and not trying to run them locally I can imagine there being a problem. I can’t find any references through a simple search to anyone who has had a similar problem.

      0
      • 42

        What I ended up doing was putting the LESS style sheet into a regular CSS style sheet and I needed to expand it. It is not nearly as efficient as your method, however I couldn’t seem to get LESS to work. I will do more research on troubleshooting the issue of my servers not recognizing LESS. I think I am getting caught up in the installation process of it.

        If other people are having the same issue with LESS, I’m happy to send the regular CSS File over. :) Thank you so much for your help! :)

        0
  24. 43

    Is there a demo of the code put together?

    0
  25. 44

    I see a lot of comments on how this can be dramatically improved, especially in regards to how the jQuery is used.

    Instead of leaving us hanging, how about updating the article with the comments concepts so that we are not hitting the dom so much? Otherwise there are going to be a lot of people like myself wanting to use this, only to be disappointed by the lack of performance and optimization. I almost feel like I would be learning to do this wrong by following the tutorial after reading the comments.

    Any possibility of this happening?
    *or can someone point us to an article where the author does it right?

    0
    • 45

      The article illustrates how to develop a functional navigation that has been built progressively and responds to a few browser scenarios. Below that are comments from other developers on how to improve upon the original. Feel free to copy the code and modify it as you see fit. Any or all of the suggestions I’ve seen in the comments would be useful additions.

      0
      • 46

        You have actually made my point for me. The article started with a decent codebase upon which many people have commented on how to make it much better. Why not simply append the article with the additions so that everyone can see and learn how to do it even better?

        It’s one thing to not know how to improve what is written having given it your best shot, but to have numerous comments for improvements, does it not do the average user a huge disservice to leave the article as is for all those who do not read through the comments? (so far 43 comments vs. I’m guessing thousands of views?)

        The problem that even I am having is some of the suggestions while great, sound complicated, so I will always be left wondering what I could have done better…

        0
  26. 48

    I do the parent a tags with inline-block, instead of block. That way, I can set a width to the enclosing ul, text-align:center the ul, and the a tags will center instead of aligning left; basically, ul {width:800px;text-align:center}

    0
  27. 49

    Pachito Marco Calabrese

    February 14, 2012 10:19 am

    Very interesting article! like it!

    0
  28. 50

    So what happens to that <nav block if you hit a browser that doesn't support html 5? Of all the articles I've read trying to convince everyone to start using it now, I've never once read will happen.

    0
    • 51

      Its completely ignored, as though it weren’t there at all. The HTML5 Shiv will take care of creating the elements for IE browsers below version 9. The resets above will also set the new HTML5 elements to display:block, otherwise they’ll render as inline.

      0
  29. 52

    G’Day. First up I would like to say thank you for this tutorial.

    However a warning appears in the back of my mind about the final output on mobile. I would like to share this warning and get your opinion about it.

    Currently the final output on mobile is a element and I noticed (in a mobile web page with content, graphics, etc.) that the menu is not really obvious. I feel lost because the menu doesn’t appear as a main menu but more like an element of a form or a widget.

    Furthermore, it is my contention that a menu hides all the information and the user can’t really see all these information if he doesn’t click on the menu.

    I do really appreciate your solution, because your use a native solution to present these information but I am worry about the accessibility and the usability. Do you believe that your solution is safe enough to guarantee a good usability?

    I am looking forward to read you.

    0
  30. 54

    “Given the increased clickable area of the native controls, the select menu is obviously much more user-friendly on mobile.”

    Disagree.

    These native controls are super ugly, and not necessarily more usable. Not sure how this turns into a “more user-friendly” quid-pro-quo argument.

    0
  31. 56

    Very informative article. Thanks a lot.
    Those who say LESS is difficult, first use it. It will be easy…

    0
  32. 57

    I almost died when I saw the jQuery code :-) Could I please write a follow-up on why this should be optimized and how?

    0
  33. 59

    “Given the increased clickable area of the native controls, the select menu is obviously much more user-friendly on mobile.”

    ontrols are super ugly,

    0
  34. 60

    Thanks for your excellent write-up. I’d like to comment on this statement though:

    “Applying left: -999em; position: absolute; will move the children to the left of the screen by a significant margin. This method is preferable to just using display: none because it is more accessible to screen readers.”

    It’s probably true that it is more accessible to screen readers, but for someone navigating with a keyboard, it could in fact be more confusing. The focus will move to the hidden elements, but the elements remain hidden, and the user has to tab through an unknown amount of links. With display:none this will not happen.

    0
    • 61

      This is true, but screen readers users will not see it all together. The best approach would be to bring it into focus when a user tabs on to it. Or allow users to tab only top level links without JS and users press enter and the list is revealed and with JS.

      0
  35. 62

    I stopped reading after “To make our CSS more efficient, we’ll use LESS along with a class file to ease the difficulty of dealing with all of those browser prefixes.”

    I’m sorry, but I sure as hell hope I don’t start seeing other tutorials across the net using these proprietary systems bust out what should be basic CSS3 stuff.

    If you can’t remember all the vendor specific stuff. It only takes a few seconds to generate the code you need!

    I just can’t stand these preprocessors. I’m an oldschool hand coder and proud of it!

    0
    • 63

      CSS preprocessors aren’t a substitute for knowing how to hand code CSS. Even with all the vendor prefixes and the two formats for gradients in webkit. They can, however, speed things up a bit and make CSS more functional for those of us who think in if/else statements and variables. Besides, this isn’t an article about proeprocessors. You’ve given up too early.

      0
  36. 64

    Mariusz Zawistowicz

    February 16, 2012 3:57 am

    Wow this is what I need right now. Good time :) Very informative article. Thanks!

    0
  37. 65

    Great article. but it display a default tag design which definitely looks odd to the website design. I tried some cool custom design for tag by the help of this article. Thanks a lot. U Rocks Jeremy Hixon. Keep Sharing.

    0
  38. 67

    Hi Jeremy,

    Not going to rain on your parade for the choice of LESS in the article. For what it’s worth I think it’s perfectly alright to use a pre-processor in these kind of articles as long as you don’t get too wild with it.

    However I do have some questions considering your article:

    1 ) Why do you build up the menu for mobile with JavaScript?

    “Given the increased clickable area of the native controls, the select menu is obviously much more user-friendly on mobile.”

    While not disagreeing with your choice for a native control, I’d hardly say loading JavaScript to override previously defined DOM elements and CSS styling is user-friendly. For one it increases load times on mobile, second it takes up more CPU usage. Not a good idea for mobile visitors. If you really must use JavaScript (which is somewhat logical if you want to change the DOM depending on width of the document) I’d suggest you reverse it. Desktop clients are way faster than mobile browsers. Use the JavaScript there if you really want to fancy up the menu. Or simply use a display: none i the CSS and hide one menu (one selectmenu, one unsorted list).

    2 ) Why not build mobile first? Granted the CSS in the example is quite small and probably developing mobile first won’t make a big difference, in bigger projects it will. Mobile users now have to load all the CSS for the desktop version before loading their own overrides.

    3 ) Perhaps the biggest problem I spotted with this article: Why don’t you simple use option groups if you want to group content together in a select box. Right now by using the — prefix users can’t go directly to an option by using the keyboard. Okay, it’s a mobile screen and you made the select box using JavaScript so it’s not an issue for desktop users right now, but by showing this kind of techniques on the web you’re teaching readers this is okay. It’s not. More times than not this way of HTML markup will hinder usability.

    0
  39. 68

    I think in the last version of LESS, you cant use this clearfix like in this tutorial becouse pseudo-classes no longer accompany mixin classes. For me only works using something like this, using &:

    .clearfix {
    &:after {
    clear: both;
    }
    }

    Or not?

    0
  40. 69

    Beautiful post. i love to see all of them. inacft i love to use the GAMES FOR HER BY YOU in to my next site theme. i will definitely show this design to my designer and will work on it for my new website. thank you very much for such wonderful post.

    0
  41. 70

    I have only rudimentary knowledge of CSS and I’m trying to learn how to incorporate the newer methods so I don’t get left completely in the dark. The website I’m updating has a static image-based menu, meaning that I can’t add new pages without redesigning the menu. I used this tutorial and it works in most browsers but looks best in Firefox 10. But I have Opera 10 on my Mac workstation and the menu doesn’t display at all. I’m nervous about implementing the new menu into our site if I’m going to lose the users who are running Opera 10. My question is, should I even be concerned about supporting this browser and if so, how can I get the menu to work?

    0
  42. 71

    I like the tutorial and got this to work without too much trouble (in WordPress no less.) However, I notice that when I change pages, the menu defaults to the first menu item in the dropdown. To fix this, I added a new var named current, looked for the class that marked it as the current page, and then changed the var current to equal ‘selected=”selected”‘ and added this to the option line. Works like a charm!

    Here is how I modified the jQuery code:

    /* Get top-level link and text */
    var href = $(this).children(‘a’).attr(‘href’);
    var text = $(this).children(‘a’).text();
    var current = ”;
    if($(this).hasClass(‘current-menu-item’)) current = ‘selected=”selected”‘;

    /* Append this option to our “select” */
    $(selectMenu).append(”+text+”);

    0
    • 72

      I think having the select menu showing always the first is not a problem.

      What was a problem is that the first cannot be selected to change the page, so it was impossible to move back to the home page.

      You just need to another before the menu ones with “Menu” as value.

      Now you have a select menu showing an unclickable “Menu” at first.

      Here’s the code :

      /* Replace unordered list with a “select” element to be populated with options, and create a variable to select our new empty option menu */
      $(‘nav.main-navigation’).html(‘Menu’);

      You can also add extra CSS to make it different from the others :

      select.menu option:first-child{…your custom style…}

      Hope this will help

      0
  43. 73

    Updated .cf – classes for newer less-versions

    the following code worked for me….

    /* For modern browsers */
    .cf {
    &:before, &:after { content:”"; display:table; }
    &:after { clear: both; }
    }
    /* For IE 6/7 (trigger hasLayout) */
    .cf { zoom:1; }

    0
  44. 74

    Thank you so much Jeremy. Nice tutorial. If you don’t want any LESS, just compile the LESS code here : http://leafo.net/lessphp/editor.html

    0
  45. 75

    Hi Im not able to get this less.css to compile into a css file, how exactly does it work?

    My path is correct:

    but when i check it in the browser its not ready any style sheet. How does less.js work? does it automatically convert the style.less file into a stlyle .css file? why isnt it working for me? i have the correct version of less.js installed.

    0
  46. 76

    I think that for very large menus, this might be useful, but I’m not a huge fan of replacing the main navigation with a select box. Personally I feel that someone like this is more appropriate: http://bradfrostweb.com/demo/mobile-first/

    Good article though and it’s nice to see a tutorial using LESS :)

    0
  47. 77

    Hi, I have it working but the first option doesn’t work. Can the JS be changed to have a ‘Please select’ as a default first option, then my initial first link will work.

    Cheers.

    0
  48. 78

    i am not able to apply this css & jquery coding in my heml page so please illustrate it with exaple in html page

    0
  49. 79

    Nice one Jeremy!

    0
  50. 80

    A really ineresting read, I’ve been looking for something like this for ages, Unfortunately I have tried a couple of times to incorporate it into an existing site without success, I canot seem to follow the abbreviations you have made ie (…).

    I notice that you have supplied a link to the complete code, from what I can make out though some parts of it are missing, are there any instances that show the complete code?

    0
  51. 81

    Hi, excellent article btw.

    I was wondering if you or anyone knows how to switch to a different wp menu location for mobile sizes? Basically my site has a huge menu, I want to switch to a smaller menu, with only the important pages listed, when a user goes to the smaller phone sizes.

    My wp nav code is currently:

    ‘main_menu’, ‘container’ => ”, ‘items_wrap’ => ‘%3$s’) ); ?>

    I have a second menu, called ‘Mobile’

    How would I get my site to switch to the menu called ‘Mobile’ at certain screen sizes?

    Any ideas would be appreciated.

    Thanks

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top