Accordion Navigation

Yesterday at work I spent far too much time working on getting an accordion vertical navigation working in a new Magento design. In the interest of public education, here's a walkthrough of how I went about getting this to work.

Basically what was required was a simple form of an accordion menu where only the top-level categories will be visible initially but clicking on one will open up a submenu showing its children. It will also set that category as the current one and display the content for it.

Getting the basics up and running was fairly easy but I kept running into problems when I attempted to smooth out the rough edges. I ran into the old problem of Magento being very poorly documented, so trying to find out what avenues I could pursue took far longer than it should have. I also ran into a definite bug where there exist several functions for retrieving the parent of a category but they don't actually work. I believe this stems from the fact that the parent_id values for categories are not entered into the database in the first place. Odd, and it shows that Magento really does have plenty of jagged edges.

I started out with this post on the Magento forums. That set up a vertical list of top-level categories but displays the rest of the menu in much the same manner as the horizontal one in the default Magento design – as a popup nested list. Not what I was looking for.

Because I only wanted the current category to show up I added in a condition to check whether or not a category was "active" before displaying its children. Doing this works but unfortunately I was using a call to $this->getCurrentChildren() to retrieve the children of the top-level active category. This works when you click on the top-level links – the children are displayed correctly on the loaded page – but doesn't work when you navigate to one of the child categories, since obviously they are then set to the current category and will have different children to the top-level category.

I then came up with the simple idea of calling $_category->getParentCategory() and getting the children of that. This is when I ran into the aforementioned bug with the parent categories (see bug #5182) which meant it was impossible to use that way.

After thinking through (and trying out) a few crazy ideas like building my own tree of categories and querying it, or overriding the majority of the Magento catalog navigation classes, I eventually realised the answer was actually quite simple, at least for the two level system I required (I haven't tried it for more levels). All I had to do was check if each top-level category was active and had children and, if so, display the children. This works since a parent category is still active if one of it's children is active. (I assume this functionality was added to make it easier to set highlighting styles on these categories.)

Here's my modified vert_nav.phtml file:

<div class="vertical-nav-container box base-mini">
    <div class="vertical-nav">
        <div class="head">
        <h4><?php echo $this->__('Navigation') ?></h4>
        </div>
        <ul id="nav_vert">
        <?php foreach ($this->getStoreCategories() as $_category): ?>
            <?php $open = $this->isCategoryActive($_category) && $_category->hasChildren(); ?>
            <li><a href="<?=$this->getCategoryUrl($_category);?>"<?php if($open) { echo ' class="open"'; } ?>>< ?=$_category->getName();?></a>
            <?php if ($open): ?>
                <ul>
                <?php foreach ($_category->getChildren() as $child): ?>
                    <?php $childCat = Mage::getModel('catalog/category')->load($child); ?>
                    <li><a href="<?=$this->getCategoryUrl($childCat);?>">< ?=$childCat->getName();?></a></li>
                <?php endforeach; ?>
                </ul>
            <?php endif; ?>
            </li>
        <?php endforeach ?>
        </ul>
    </div>
</div>

Originally I also tried to use $_category->getAllChildren() as this sounded more like what I wanted but this returned nothing so I went with what I have above, which works. It would be nice if there was a method that directly returned the children as category objects, rather than having to load them as an extra step – if such a method exists I didn't find it so let me know if it is in there!

Everything else is roughly the same as the other vertical navigation tutorial. You'll need to tweak your CSS to achieve a nicer nesting effect. I used left margins to indent the child category links.

Here are some images from the final product:

topmenu submenu-dvd