Drupal: Turn your main menu into a Superfish menu (and learn some basics along the way)

There are a few of us control freaks DIY types who prefer to tinker with things at the source, even if it takes a little more time and effort. But since most people will likely opt to install the excellent Superfish Drupal module, which will essentially have you set up in a couple of clicks, this post is more of an excuse to illustrate a few theming basics; for instance:

  • how to use a theme function to override Drupal's default HTML output,
  • how to alter rendered output using a preprocessor function,
  • how to load javascript and css files into your theme.

We'll use the plugin's "Basic" menu in this example. The fine tuning is up to you. 

Versions

Drupal 7.x uses jQuery v1.4.4 by default. In order to use the latest version of Superfish (v1.7.3 as of this writing) you will need to download the jQuery Update module which will allow you to configure jQuery to a version >= 1.7. The Superfish Drupal module requires this, as well. Or, you could cheat and just download Superfish v1.5.0 which will work fine with the default version of jQuery. (But be prepared to have to upgrade soon, anyway.) 

Steps

Drupal 7.x:

  1. Bring in plugin files. Once all plugin files are downloaded, grab those you need and put them into your theme directory. The structure should look something like this: 

    mytheme/
       css/
         [file] superfish.css
       js/
         [file] hoverIntent.js
         [file] superfish.js
       images/
         .. whatever images you need here.

    (Of course the file names will vary depending on plugin version, and whether the file is minified.)

  2. Load stylesheet. Open your theme's .info file and add a new 'stylesheets' key. It should be placed above your main stylesheet.

    stylesheets[all][] = css/superfish.css
  3. Load scripts. Also in your .info file, add two new 'scripts' keys. 

    scripts[] = js/hoverIntent.js
    scripts[] = js/superfish.js
  4. Add a CSS class to the main menu. Open template.php in your theme folder and add a theme function as shown below. Replace [THEME NAME] with the name of your theme.

    function [THEME NAME]_menu_tree__main_menu($variables) {
      return '<ul class="menu sf-menu">' . $variables['tree'] . '</ul>';
    }

    A little bit about what we're doing here:

    Drupal has a theme function called theme_menu_tree() which outputs the list wrapper element for all Drupal menus by default. All Drupal theme functions begin with 'theme_'. By replacing 'theme' with your theme name you are giving preference to your function. If we stopped here Drupal would use this function for all system menus. But because we referenced a specific menu by appending '__main_menu' - a suggestion in Drupal-speak - we have told Drupal to only give preference to this function for the main menu.

  5. Call the plugin. Below is the inline script we need to call the plugin.

    jQuery(document).ready(function ($) {
        $("ul.sf-menu").superfish();
    });

    By pointing to the selector 'ul.sf-menu', the list wrapper element altered with our theme function above becomes a jQuery object on which we can call the Superfish plugin. But how to implement it in a Drupal theme?  Two recommended methods:

    Preprocessor function

    Add a preprocessor function to template.php. Replace [THEME NAME] with the name of your theme.

    function [THEME NAME]_preprocess_page(&$vars, $hook) {
      drupal_add_js('
    jQuery(document).ready(function ($) {
        $("ul.sf-menu").superfish();
    });
      ', array('type' => 'inline'));
    }

    Preprocessor functions are a precursor to template files and allow us to alter the data before it is rendered as HTML. The template name is appended to the function name. In this case, we can tell by the function name that we are preprocessing the output which will ultimately rendered using the page.tpl.php template. See template_preprocess_page() in the API docs for more info.

    New javascript file

    The second method is to paste the jQuery snippet above directly into an empty file, save it as somefile.js, then include it by adding yet another 'scripts' key to your .info file.

    Voila.

    The difference between these two methods is that the first cannot be aggregated (if using the "Aggregate Javascript files" option for better performance), and the second requires an extra HTTP request. IMHO, unless you're super sticky it's OK to just choose whichever is easiest. 

  6. Clear all caches. 

When you reload the page you should now have a Superfish menu and all of the happy feelings that come along with it. If not, consider the following:

  • Are the names of your files typed correctly?
  • Do the paths to your files match their locations?
  • Do you need to enable and configure jQuery Update?
  • Did you clear the caches? (Not sure how? More info here.)
  • Is the CSS and JS optimization disabled? It should be.
  • Did you populate your menu? (I know, I know, but we all forget things once in awhile.)

I hope this was helpful. It's pretty cool how sometimes you can learn a ton just by doing a simple thing. :)