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.
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
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
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
I agree mate - to be fair I wrote this article in 2013. Here's a more up-to-date article on navigation. Cheers
upendra
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