Fully Dynamic Hierarchical Nav Menu Template
Problem
You want to output a navigation menu that automatically lists a Structure Section’s page hierarchy, with various CSS classes that indicate current page, current section, has children, etc.
Solution
The Craft docs have a basic example of a hierarchical nav menu, but usually you want to add various CSS classes that indicate certain things about each page in the menu so you can style them appropriately. Here is a template that shows you how to add such classes.
<ul class="nav-list nav-list--level-1">
{% set homeIsCurrent = (entry.section.handle == 'homepage') %}
<li class="nav-item nav-item--home nav-item--level-1 {% if homeIsCurrent %}nav-item--current{% endif %}">
<a href="{{ siteUrl }}">Home</a>
</li>
{% nav page in craft.entries.section('pages') %}
{% set pageIsCurrent = (page.id == entry.id) %}
{% set pageIsTopParent = (craft.request.firstSegment == page.uri) %}
{% set pageIsInPath = (entry.uri matches '{^' ~ page.uri ~ '}') %}
{% set classes = ['nav-item--level-' ~ page.level] %}
{% set classes = classes|merge([pageIsCurrent ? 'nav-item--is-current' : '']) %}
{% set classes = classes|merge([pageIsTopParent ? 'nav-item--is-top-parent' : '']) %}
{% set classes = classes|merge([pageIsInPath ? 'nav-item--is-in-path' : '']) %}
{% set classes = classes|merge([page.hasDescendants ? 'nav-item--has-children' : '']) %}
{% set classes = classes|join(' ') %}
<li class="{{ classes }}">
<a href="{{ page.url }}">
{{ page.title }}
{% if page.hasDescendants %}<span class="nav-toggle nav-toggle--level-{{ page.level }}">▼</span>{% endif %}
</a>
{% ifchildren %}
<ul class="nav-list nav-list--level-{{ page.level + 1 }}">
{% children %}
</ul>
{% endifchildren %}
</li>
{% endnav %}
</ul>
Some things to note:
- Make sure you replace the ‘pages’ argument in
craft.entries.section('pages')
with the handle of your own structure section. - You can of course set whatever CSS classes you want, but I am using
nav-list
,nav-item
prefixes to denote theul
andli
elements. - You can change the actual CSS class names that get output, by altering the strings in the various
{% set classes = ... %}
lines. pageIsCurrent
means that the nav item is the page currently being viewed.pageIsTopParent
means that the nav item is a top-level page in the given section that is “above” the page currently being viewed (that is, it is the top-level menu item that the currently-viewed page lives under).pageIsInPath
means that the nav item is a parent at any level (direct parent, grandparent, great-grandparent, etc.) of the page currently being viewed. Note that it will also be true for the page currently being viewed itself.- All that “set classes” junk is kind of ugly, but it does keep a lot of logic out of the html itself, so in my opinion it is slightly cleaner than littering the actual css attribute of the html tags with a bunch of twig
if
statements. - If you don’t want the homepage link in your nav menu, just delete that first
<li>
for it (along with theisHome
variable setting line above it).
Submitted by Jordan Lev on 1st February, 2017