An automatic navigation for ExpressionEngine

Posted in Web Design on Wednesday, 2nd June 2010 at 3:04PM

An automatic navigation for ExpressionEngine

Update October 2010: Since writing this post I have had a chance to try out three modules which make my solution pretty much redundant.  Navee by Booyant does a great job of making Expressionengine menus really easy to create and manage from the control panel. Structure goes several steps further by organising the entire structure of the site including all the menus and making it really easy to manage for the end user.  Finally, the relative newcomer is Taxonomy which is something of a halfway house.  All three are brilliant and suit different types of site depending on what is needed.  Give them a go!

Having experimented with several different content management systems, one feature I really value is the ability to dynamically create a navigation menu based on the content of the site.  When I started using ExpressionEngine, I liked it’s user interface, flexibility and ease of implementation but I missed the automatic navigation builder’s that are included with some open source packages such as CMS Made Simple.

These examples are built for ExpressionEngine 2 but the same techniques can equally be applied to ExpressionEngine 1 by switching ‘channel’ for ‘weblog’.

A simple one level menu

The following will use the standard channels module to iterate over all items in a chosen channel and use their titles as the navigation’s list item text.  I have set dynamic = “off” to ensure all menu items are shown on every page.

<ul>
{exp:channel:entries channel="inner_pages" dynamic="off" sort="asc"}
<li><a  href='{title_permalink="/TemplateGroup/TemplateName"}'>{title}</a></li>
 {/exp:channel:entries}
 </ul>

A two level navigation

More often than not, some sort of split level navigation is needed in my sites which needed a bit more work to implement. In this example I have a main navigation and a sub-navigation.

For the main navigation I created several categories that each acted as a parent item for my sub-menu items.  ExpressionEngine was then able to  iterate over all the categories in the specified channel and create a list of all the parent items, using ‘path=’ to link the item to the top page in each category.

<ul id="mainNav">
{exp:channel:categories channel="inner_pages}
<li><a href='{path='TemplateGroup/TemplateName/index'}'>{category_name}</a></li>
{/exp:channel:categories}
</ul>

I then used the first single level menu above to create the sub-menu.

The first slight problem I ran in to was the fact that all the sub-menu items were showing on every page, regardless of the category that they had been placed in. I needed to incorporate some way of telling the sub-menu which category of items to show. To do this I needed two bits of information, the category that the current page was in and the category that each menu item was in.  To get the page’s category I used a small snippet of php at the top of the template to create a variable which was then passed to a ExpressionEngine tag as follows.

{exp:channel:entries channel="inner_pages" limit="1"}
<?php $current_cat_id = "{categories}{category_id}{/categories}"; ?>
{preload_replace:current_cat_id="<?php echo $current_cat_id ;?>"}
{/exp:channel:entries}

This created a tag named {current_cat_id} to hold the category of the current page.  The next step was to create a tag to hold the category id for each of the menu items as they were read.

{preload_replace:side_nav_id="{categories}{category_id}{/categories}"}

This snippet was placed inside the navigation’s channel tags to make sure it was refreshed for each item.  Then all I needed to do was compare the page’s category tag to the current item’s tag and only display the list item when the two matched.

<ul id="side_nav">
{exp:channel:entries channel="inner_pages" dynamic="off" sort="asc"}
{preload_replace:side_nav_id="{categories}{category_id}{/categories}"}
{if {current_cat_id} == {side_nav_id}}
<li {if "{url_title}" == "{active_url}"} {/if}><a href='{title_permalink="/TemplateGroup/TemplateName"}'>{title}</a></li>{/if}
{/exp:channel:entries}
</ul>

The result was a main navigation that displayed all categories in the site and a sub navigation that just showed items that were in that category.
Active highlighting

This method can be extended further to include active highlighting for each item by including a short if statement to check the active status of the link against a predefined variable.

I created a tag that holds the url title of the currently active page at the top of the template:

{exp:channel:entries channel="inner_pages" limit="1"}
<?php $active_url = "{url_title}"; ?>
{preload_replace:active_url="<?php echo $active_url ;?>"}
{/exp:channel:entries}

Then I placed this if statement within the link to compare the page’s url title tag to the url title of the  current link to add an active class if they matched.

{if "{url_title}" == "{active_url}"} class="active" {/if}

Likewise I added an active class to the main navigation by comparing the {current_cat_id} tag created earlier to a new tag called {main_nav_id} that is iterated over in the main  navigation’s channel, defining the category of the current item.

<ul id="mainNav">
{exp:channel:categories channel="inner_pages}
{preload_replace:main_nav_id="{category_id}"}
<li><a {if {current_cat_id} == {main_nav_id}} class="active" {/if}  href='{path='TemplateGroup/TemplateName/index'}'>{category_name}</a></li>
{/exp:channel:categories}
</ul>

This solution is obviously not perfect or as slick as something like you would find in CMS Made Simple, but if you have settled on ExpressionEngine and you want a way for clients to be able to add new pages without too much fuss then something like this could be your solution.

Add your comment

Remember me?

Notify me of follow-up comments?

Hi Michael,

well written and very useful!

One question: when I use this code in my template:

{exp:channel:entries channel=“inner_pages” limit=“1”}
<?php $active_url = “{url_title}”; ?>
{preload_replace:active_url=”<?php echo $active_url ;?>”}
{/exp:channel:entries}

and I update the page, I get this message:

Method Not Implemented

POST to /system/index.php not supported.

Before you ask, I set “Allow php” to yes…

Thank you!

Marco

Posted by Marco on Thursday, 19th January 2012 at 9:09AM

Hi Marco,

Your code looks OK to me so I’m not sure why it’s not working.  Is PHP set to be parsed at input rather than output because the template will need the $active_url variable available first in order to pass it to an EE tag?

If you don’t manage to figure it out I would highly recommend something like Structure, Navee or Taxonomy as an alternative.  They are so much simpler to implement and maintain than this method. 

Mike

Posted by Mike on Thursday, 19th January 2012 at 9:26AM

Michael's Paintings

Copenhagen

Copenhagen
40cm x 30cm
Acrylics, ink and cotton on canvas

Michael's photos

Beijing Birds Nest Stadium

Beijing Birds Nest Stadium

Featured Web Project

QIQ - List page

Project: QIQ