Simple jQuery Navigation Pull-Down Menus

A common need for websites is a “jump” menu, or a quick means to navigate between pages / views in a site.

This, of course, is not the same as the main navigation menus of the site. For example, on this site, I have a primary header navigation menu, which lets visitors move around the site based on topics.

That’s generally good enough for most sites. But sometimes, it makes sense — especially in a subsection of a website, with a lot of child content — to have a subnavigation tool that pools content into a quick means of moving around that section.

Consider the movies section of MaineToday.com, for which I originally built the solution I’m about to describe:

This is the jump menu for the movies section of MaineToday.com
This is the jump menu for the movies section of MaineToday.com

As you can see, there are two pull-down (select) menus at the top of the movies listing. One is a list of all movies for which we have theater showtimes; the other is a list of all movie theaters we cover.

By selecting either a movie, or a theater, we can quickly navigate to that item:

Notice as well, the menu reflects the currently selected page.

Doing this is mind-numbingly simple in jQuery.

Step 1: Create Select Menus

The first step to our task is to create the pull-down menus that will contain our navigation links.

<select id="mt-movies-select" class="mt-movies-jump">
	<option value="">Pick a movie ...</option>
	<option value="http://mainetoday.com/movies/american-sniper-20150108/">American Sniper</option>
	<option value="http://mainetoday.com/movies/american-sniper-the-imax-experience-20150109/">American Sniper: The IMAX Experience</option>
	<option value="http://mainetoday.com/movies/annie-20141210/">Annie</option>
	<option value="http://mainetoday.com/movies/big-eyes-20141223/">Big Eyes</option>
	<option value="http://mainetoday.com/movies/big-hero-6-20141031/">Big Hero 6</option>
	<option value="http://mainetoday.com/movies/birdman-20141031/">Birdman</option>
	<option value="http://mainetoday.com/movies/blackhat-20150109/">Blackhat</option>
	<!-- etc., etc., etc. -->
</select>

A couple quick notes about this menu:

You’ll notice that I have applied an ID and a class to this menu. I can use either to bind the jQuery that will power the menu.

On mainetoday.com, I have two jump menus, both of which are bound to the same onchange event handler. (That is, one jQuery function handles changing the URL for both jump menus.)

Since I have two menus, and they both do the same thing, I applied a common class to both, and bound the JavaScript jump menu function by selecting all elements that have the class “mt-movies-jump”.

This looks a little dirty, but it is in tune with jQuery best practices.

To be super-safe, it would seem best that I specify that I want to bind the change event for all select menus that have the class mt-movies-jump, not just everything that has the class mt-movies-jump.

But the folks behind jQuery say that when you’re not using an ID (that is, selecting a single element), your jQuery selectors should be less specific, left-to-right.

In other words, they want us to make specific classes that apply to the correct elements, rather than writing super-complicated selectors. And ultimately, I agree that makes the most sense.

Also of note is that the first option element has a blank value. That’s because I want to make sure that whatever is selected is actually a navigable URL.

I’ve previously written about breaking up long select lists with non-selectable items. This makes reading the list a lot easier, especially if it is very long. It also lets me have that “Pick a movie …” default item, so that the menu can work independently of where it appears on the site.

With the select list created, I can now create the jQuery handler.

Step 2: Creating the jQuery Event Handlers

My event handler has two parts:

  • Perform the redirect, via $(location); and
  • Ensure that the selected URL is chosen once I wind up on that new page, so the user can see he wound up at the right place.

I’m going to wire up this handler via $(document).ready(), but your implementation may want to wire this up more selectively. For example, you may only want this to work in certain circumstances on the page, or if something else has been selected or changed.

Also, this version is using jQuery in no-conflict (safe) mode. I used to not care too much about writing jQuery in safe mode, because it was the only library on my page as a rule; but in this day and age, with multiple libraries on even the simplest of pages, I’ve found it’s better safe than sorry. {*rimshot*}

Here’s the entire solution:

jQuery(document).ready(function($) {
	$('.mt-movies-jump').change(function() {
		if($(this).val() != '') {
			$(location).attr('href', $(this).val());
		}
	});

	$('.mt-movies-jump option').filter(function() {
    		return $(this).val() == $(location).attr('href'); 
	}).prop('selected', true);
});

Getting The Selected URL From The Nav Pull-Down And Navigating To That Link

The first function selects anything with the .mt-movies-jump class and binds the resulting, anonymous function to the change event.

$('.mt-movies-jump').change(function() {

In other words, it finds all the select elements with the class mt-movies-jump. Then, it tells the Web browser that any time a new option is selected from that list, it should do the tasks that are described in the anonymous function that follows.

I next examine the value of the option that was selected.

If it is not an empty string, then I assign the location of the current browser window to be whatever the selected value is. Of course, this solution assumes that the value of the selected option is a fully qualified URL or an empty string.

if($(this).val() != '') {
	$(location).attr('href', $(this).val());
}
A “fully qualified URL” is one that has a protocol (e.g., http://), a domain (e.g., www.mainetoday.com) and a path (e.g., /movies/american-sniper-20150108/).

Therefore, http://www.mainetoday.com/movies/american-sniper-20150108/ is a fully qualified URL; /movies/american-sniper-20150108/ is not.

I am being a little sloppy here because I am not examining whether the value of the selected option is a fully qualified URL, and that it will send me to a page on my site.

So, in theory, someone could come along and inject other values into this select list’s options, which would make the select-based nav menu send him someplace else. But since that would be done on a per-client basis, and be pretty pointless in the grand scheme of things, I’m simply assuming my markup will be fine.

Selecting The Current URL In The Nav Menu When It Matches A Selected Option

The second function ensures that if one of the URLs in my menu is the current location of the window, that it is the selected option for that list.

Let’s look again at one of the detail pages:

The selected item in the nav menu matches the URL of the page.
The selected item in the nav menu matches the URL of the page.

The URL of the page and the URL of the value of the selected option match, so the menu shows that the current page is the selected item.

The way we’re going to make this happen is by applying a filter to all the option elements in this select list.

Specifically, we are going to ask if the value of any one of the select list’s options matches the current location of the window (that is, the URL of the webpage we’re looking at). If so, we’ll set its selected property to true.

We can see this filtering process more clearly if I change the function a little, and remove the part that selects the option:

$('.mt-movies-jump option').filter(function() {
    	return $(this).val() == $(location).attr('href'); 
});

The selector in this case — $(‘.mt-movies-jump option’) — is choosing all the option elements that are a child of another element with the class .mt-movies-jump. In other words, I am starting by getting every single URL in the jump menu.

The jQuery filter() method allows me to narrow down specific option elements from this master list. I’m looking for any option that has a value that is exactly the same as the URL of this window:

return $(this).val() == $(location).attr('href');

It’s a little complex how this works.

The filter() jQuery method takes an anonymous function, which it applies against every single element that has been selected.

For example, suppose our nav menu has 10 URLs. The selector — $(‘.mt-movies-jump option’) — is going to get all 10 URLs from that menu.

The filter() method is going to be applied to all 10 of those items. It is going to check to see whether any of those items match the condition specified by the anonymous function that is its sole argument:

$('.mt-movies-jump option').filter(function() {
    	return $(this).val() == $(location).attr('href'); 
});

The anonymous function says, “If the value of this option — $(this).val() — is a URL that matches the URL of the window — $(location).attr(‘href’) — then this option is going to be included in the filtered results. If not, then this option will be excluded from the filtered results.”

Since our select list only contains one option with a value that is the URL of the selected page, there are only two possibilities from filtering its options: We get back either one option, or no options. (We would get back no options if the URL was not found, or if someone selected a non-selectable option { e.g., an option that has an empty string for a value}.)

When we get a match, that means that the page we’re on matches one of the URLs in our menu.

We want to tell jQuery to set that item to be the selected item in the select list, so it is obvious to the user that he’s in the right place. We do that with the prop() method, by setting the selected property of the correct option to true:

$('.mt-movies-jump option').filter(function() {
    	return $(this).val() == $(location).attr('href'); 
}).prop('selected', true);

jQuery supports method chaining. That means that I can just keep adding methods, in the order I want, on a single line of code, and jQuery will execute them in turn.

In this case, I first call the filter() method, then the prop() method; so jQuery first filters my selector, then applies prop() to the filtered results.

The function above is equivalent to:

var theOptions = $('.mt-movies-jump option');
var theURL = theOptions.filter(function() {
    	return $(this).val() == $(location).attr('href'); 
});
theURL.prop('selected', true);

But as you can see, method chaining makes things more elegant. More confusing if you’re new to jQuery, yes; but more elegant, too.

Demo, Download, etc.

Since mainetoday.com is readily available, it will serve as my demo. You can lift the code directly from there.

Also, this code is not mine to license, but it’s readily obvious to all, so use it as you would like.

All links in this post on delicious: https://delicious.com/dougvdotcom/simple-jquery-navigation-pull-down-menus

Leave a Reply

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!