Friday, January 28, 2011

Superfish menu + Drupal Fusion + How the devil do I

Superfish is a pretty cool menu based on JQuery and CSS. If you are suspicious, view the wonder for yourself at with the nav-bar example.

Fusion based Drupal themes are currently hot on market, e.g., Acquia Prosper. You can check them out for your own at

You are yet to hear the awesomest - jQuery hoverintent plugin. Very helpful for navigating complex menus, where often, one slip of the mouse implies restarting the navigation.

Fusion themes already use Superfish for their main menu system. Of course, I madly fell in love the moment I saw the navbar. However, the sophisticated CSS and JS of Superfish nav-bar goes kaput when used in Fusion. Quoting jeremy (a TopNotchThemer at forum)
Superfish is a bit difficult to work with when you need to get the active menu sub-menu to be visible when you are on that section. I've tried a LOT of different scripts and changes to the theme but never found a solution that worked for Superfish and it was very unstabble when I got close to what I wanted.
I actually went with disabling the primary menu and Superfish all together and going with a block instead for my menu. This is basically starting all over and doesn't work with Superfish at all so you have full control over the placement and styling but will have to write it yourself.
So for this specific case I recommend going with a block instead of the default primary menu with Superfish. Best of luck!

So, I started keeping notes when I saw some early signs of success. Here is the recipe to get it (close to) perfect.

Fix Superfish nav-bar menu in Fusion-based Drupal themes

1. Enable navbar for superfish

function iiitd_preprocess_page(&$vars) {
  // Add Superfish navbar class if dropdown enabled
  if ($vars['primary_links'] && theme_get_setting('primary_menu_dropdown') == 1) {
      $vars['primary_links_tree'] = preg_replace('/^<ul class="menu sf-menu/i', '<ul class="menu sf-menu sf-navbar', $vars['primary_links_tree'], 1);
2. Ask navbar to keep showing second level

--- mysubtheme.js
Drupal.behaviors.mysubthemeSuperfish = function (context) {

$("#primary-menu ul.sf-navbar").superfish({
    hoverClass:  'sfHover',
    pathLevels:  2,
    pathClass:   'active-trail',
    delay:       800,
    animation:   {opacity:'show'}, // opacity: fade-in, height: slide-in
    speed:       'normal',
    autoArrows:  true,
    dropShadows: false,
    disableHI:   false

3. Modify fusion-core/superfish.js DIRECTLY! Superfish removed the active-trail class which fusion didn't like at all!
return this.each(function() {
   var s = this.serial = sf.o.length;
   var o = $.extend({},sf.defaults,op);
   o.$path = $('li.'+o.pathClass,this).slice(0,o.pathLevels).each(function(){
+    $(this).addClass([o.hoverClass,c.bcClass].join(' '));
+     //.filter('li:has(ul)').removeClass(o.pathClass);
-    $(this).addClass([o.hoverClass,c.bcClass].join(' '))
-     .filter('li:has(ul)').removeClass(o.pathClass);
   sf.o[s] = sf.op = o;

4. (As good as modifying superfish.js) Set the default value for Superfish directly - by the time drupal does it, the page is already rendered in an incorrect way.

--- mysubtheme.js
// Change defaults too - otherwise, second level does not show the active-trail
$.fn.superfish.defaults.pathClass = 'active-trail';
$.fn.superfish.defaults.pathLevels = 2;

5. Add space for the second level to show in the right place.
div#primary-menu ul.sf-navbar {
    height: 2.5em;
    padding-bottom: 2.5em !important;
    padding-left: 0 !important;
    position: relative;
    width: 100%;
    z-index: 97;
    line-height: 100%;

div#primary-menu ul.sf-navbar li {
  position: static;

div#primary-menu ul.sf-navbar li.sfHover ul {
  margin-top: 0;
  top: 2.5em;   /* match top ul list item height */
  left: 0;

6. Even though the theme comes with hoverintent plugin files, HI (as hoverintent is often called) doesn't actually work. Even after disableHI is set to false. The reason and the fix is ...
// Destroy default fusionSuperfish initialization.
/* Explanation: default fusionSuperfish disables HI during its initialization -
 * which, attaches a jquery hover() handler. The jquery hover handler attaches
 * mouseenter and mouseleave events to the element. The mouseenter handler first
 * actually changes the eventype to 'mouseenter' and then calls the handler.
 * Now, HI requires handlers for mouseover and mouseout. However, upon firing
 * those events, first the hover handler is called. So, by the time the HI
 * handler is called, the event type has already been changed to mouseenter/mouseleave!
 * And that is how the cookie crumbled everytime.
Drupal.behaviors.fusionSuperfish = function (context) { };
Drupal.behaviors.fusionSuperfishBlocks = function (context) { };

This post carries a timestamp of 1:30am at night. Don't even ask for warranty.