Simple responsive navigation technique

By Geoff Muskett

There are many solutions for achieving responsive navigation, which Brad Frost rounds up nicely here. Having implemented a few of these, I’ve developed a slight spin-off technique which to my mind has to be one of the front runners in terms of simplicity to execute versus functionality versus¬†user understandability. No, that is probably not a real word.

Of course one solution is not suitable for every use-case, so navigation should be crafted to suit the particular site it is being designed for. The following is useful for simple one level navigation.

The Button Toggle

The end result of this technique is a classic display:inline list on larger screens, and a button to open / close a display:block list on smaller screens. The working live example can be played with on the Newquay Handball Club website.

Responsive navigation on Newquay Handball Club website by Geoff Muskett

An extra bit of HTML

This is the only slight downside to the technique; it requires an extra fragment of HTML which won’t be used on larger screens. However, it’s only a button, nothing more. I seriously doubt it has any measurable effect on performance.

<button>Open / Close Navigation</button>

It would work if you put this anywhere, but it makes sense to put it above the navigation list in the DOM.

<nav>
<button>Open / Close Navigation</button>
<ul>
<li>first item</li>
<li>second item</li>
<li>third item</li>
<li>fourth item</li>
</nav>

Media Queries

The Button

In the CSS set up the button to display:none, then use media queries to display :block on small screens. Add your styles. (Example uses SASS)

nav button {
display:none;
@media only screen and (max-width: 720px) {
display:block;
...your styles...
}
}

The List

Now hide the unordered list items on small screens.

nav ul li {
    ...styles...
    display:inline; //or block etc
    @media only screen and (max-width: 720px) {
        display:none;
    }
}

Some simple jQuery

Toggle the visibility of the unordered list, which is super easy.

$('nav button').click(function() {
$('nav ul li').toggle();
});

We’ve now got a working button that displays on small screens. Great. Or is it; we have a big issue which is if the user has their desktop window small enough to display the button, once they’ve opened and closed the navigation then expanded their window to larger than the media query break point, the list won’t display. This is because when the button is clicked to close the list, JavaScript injects display:none css inline to the list items, which overrides the stylesheet. Never fear, there’s a solution:

jQuery resize trigger

jQuery that is only intended to run once is triggered when it is safe to do so, for which we use:

$(document).ready(function() {
...jQuery goes here...
});

Most jQuery ends up in there, however in this case we want to use another trigger:

$(window).resize(function() {
...jQuery here triggers when the window is resized...
});

We can now use the following to fix the resize display issue:

if ($(document).width() > 720) {
		$('nav ul li').css('display','inline');
	} else {
		$('nav ul li').css('display','block').hide();	}

Every time the window is resized this code runs. Essentially this makes sure the list is being displayed inline when the window is wider than 720px, and displayed block when narrower than 720px. On small screens, display:block will make the list visible, so it needs to re-hidden – until the button is pressed.

Cool, now that is fixed people can resize the window all they want. Works well in desktop browsers and on Android phones, but there’s a problem with iOS devices.

A little user agent sniffing

Honestly, I don’t know what was causing this, but on iPhone the first tap was making the navigation show and almost instantly hide. Tap it again and there was no problem. It looked like the window resize code was being triggered, but only the first time. Strange, but there’s a way round it.

function isiphone(){
    return (
        (navigator.userAgent.toLowerCase().indexOf("iphone") > -1) ||
        (navigator.userAgent.toLowerCase().indexOf("ipod") > -1)
    );
}

The above checks for iphone or ipod. We don’t need to check for iPad because even the mini in portrait mode is wider than our media query break point.

Now we can run the code triggered by resize only if the user is not viewing on an iPhone:

if (!isiphone()){
if ($(document).width() > 720) {
		$('nav ul li').css('display','inline');
	} else {
		$('nav ul li').css('display','block').hide();	}
	}

Problem solved. Well, worked around.

Finishing touches

To add some polish to the finished article, and improve the UX slightly we can use the touchstart event which makes the show/hide effect smoother:

	$('nav button').bind('touchstart',function(e){
		e.preventDefault();
		$('nav ul li').toggle();
	});

Beauty, thats it. Finished article on a live site is here. If anyone has a Windows phone or Blackberry, please let me know in the comments if it works ok!

All together now

Here’s the code organised into three useable chunks…

HTML:

<nav>
<button>Open / Close Navigation</button>
<ul>
<li>first item</li>
<li>second item</li>
<li>third item</li>
<li>fourth item</li>
</nav>

CSS, well, SaSS actually

nav button {
display:none;
@media only screen and (max-width: 720px) {
display:block;
...make it look pretty...
}
}

nav ul li {
    ...styles...
    display:inline; //or block etc
    @media only screen and (max-width: 720px) {
        display:none;
    }
}

jQuery

For neatness, I’ve put the iphone/ipod detection and the resize code in functions.

// first remap jQuery to $
(function($){})(window.jQuery);

/* trigger when page is ready */
$(document).ready(function (){

	// show/hide the menu on button click
	$('nav button').click(function() {
		$('nav ul li').toggle();
	});

	// a nicer show/hide effect on iphone
	$('nav button').bind('touchstart',function(e){
		e.preventDefault();
		$('nav ul li').toggle();
	});
}); // close doc ready

/* trigger when window is resized */
$(window).resize(function() {

//if iphone detection is true run the resize checking function
	if (!isiphone()){
		resized();
	}

}); // end trigger when window is resized

function resized() {
	if ($(document).width() > 720) {
		$('nav ul li').css('display','inline');
	} else {
		//display block then hide ready for toggle button to show nav
		$('nav ul li').css('display','block').hide();	}
}

function isiphone(){
    return (
        (navigator.userAgent.toLowerCase().indexOf("iphone") > -1) ||
        (navigator.userAgent.toLowerCase().indexOf("ipod") > -1)
    );
}

Any questions or thoughts, give me a shout in the comments. Happy responsivating. No that probably isn’t a word, either.

Comments

Edgar
April 22, 2015

Excellent article. I'm new in responsive websites and I didn't know how to display the menu again after JavaScript injects display:none and of course when the user re-size the window.

akis
July 13, 2015

Your method is flawed. If list is toggled on while on smaller screen size and you resize the screen, it toggles off again. A MUCH simpler approach and 100% functional is to just toggleClass to the list you want to hide/show by clicking the button and let css do the rest.

Geoff Muskett
July 14, 2015

I agree mate - to be fair I wrote this article in 2013. Here's a more up-to-date article on navigation. Cheers

upendra
November 2, 2015

Hello,
How can a button can be made responsive?? if i'm seeing it in my desktop it is perfect when i see it any other devices it seems to be very big..How can i manage this,i've contacted the author of the plugin but he told me to use some media queries,i've tried but there is no use... i'm using revolution slider.
thanks

Leave a Reply

Your email address will not be published.

Join my newsletter

If you’re looking for insights into web design and development, and crafting a successful presence online, then you should join the smart folk on on my newsletter. I discuss my thoughts on an issue relating to the web, then provide and teach effective solutions.