Textpattern tips, tutorials and code snippets

Section and article navigation with TXP

One of the hardest things in any content management system is the construction of reliable, scalable navigation systems. While Textpattern facilitates rapid site development, building dynamic navigation elements can be a challenge because of the necessary code wrangling and tag trickery. There are a lot of plugins out there that help, but best practices dictate that a site should be built with as much native functionality as possible to avoid reliance on third-party solutions that might fall into development stagnation.

This tutorial explores building a fully dynamic navigation system using sections and articles. Our goal is to create a left-hand navigation that uses sections as the primary menu elements with articles as the submenu elements, and to make the submenu items appear only when that section is active. The tag examples take advantage of functionality available only in version 4.0.7 and beyond, and will not work with previous versions of the CMS.

Let’s pretend we’re building the navigation for a small corporate website. Our high-level navigation will look like this:

  • About
  • Services
  • Case Studies
  • Contact

Each of these represents a section. The above is what the visitor would see on the homepage, before any items are clicked. However, if they click “Services”, for example, we want to display the articles within that section as a submenu, like this:

  • About
  • Services
    • Garage Organization
    • Gutter Cleaning
    • Interior Painting
  • Case Studies
  • Contact

As a bonus, we also want to add a class of “active” to whatever page is currently being viewed, so we can style the navigation accordingly.

A Quick Peek at Our Page Template

First, let’s look at what part of our page template might look like:

<div id="nav">
<txp:output_form form="nav" />
</div>
<div id="content">
<txp:if_individual_article>
<txp:article />
<txp:else />
<txp:article status="sticky" />
</txp:if_individual_article>
</div>

Essentially, we have the nav relegated to a separate form, which we’ll tackle in a little bit. Below that is our main content area. This basic code is telling Textpattern that if we’re looking at an individual page (like “Gutter Cleaning”), show it, but if not, show the article with a sticky status for the landing page content. That way we can go ahead and create an article called “Services”, make it sticky, and use it for the landing page content when they click “Services” in the nav. Easy. On to the good stuff.

Using section_list as Our Foundation

We’re going to use the tag <txp:section_list /> as our foundation for the navigation. This tag is designed, at its heart, to display a list of defined sections, usually in the context of navigation. Prior to 4.0.7, this would have been insufficient for this tutorial. However, it now boasts one key piece of new functionality — the ability to define a form that renders the output. This opens up an entire world of possibilities because we can now use all kinds of Textpattern tags to define precisely how that list of sections gets generated.

In the previous section, we referenced the form nav. This is a misc-type form. It contains this single line of code:

<txp:section_list form="nav.build" class="" break=""
wraptag="dl" sections="about,services,case-studies,contact" />

Let’s examine this tag briefly. First of all, we’re using the new form attribute to reference nav.build, which we’ll cover in the next section. We’re then nulling the default values for the attributes class and break so they do not interfere with our HTML. We’re then defining a wraptag of <dl>, which will encase our entire navigation. Finally, we’re telling Textpattern precisely which sections we want to appear in this list; this mirrors what we discussed in the beginning of the tutorial. Simple, right? On to the good stuff.

Getting Funky With if_different

Textpattern is very unique as a CMS, but one tag that almost singlehandedly defines the system’s off-the-beaten-path approach is <txp:if_different>. Textbook describes it thus: “The tag will execute the contained statement when the value of the contained statement differs from the preceding value for that contained statement.” Not exactly intuitive. In order to understand its power (and limitations), you just have to experiment.

In the previous section, we referenced the form nav.build, which is an article-type form. This is where the heavy lifting is done. Here is the core content of nav.build:

<txp:if_different>
<dt><txp:section link="1" title="1" /></dt>
</txp:if_different>
<txp:article_custom section='<txp:section />' form="nav.build.link" />

Let’s dig in. The <txp:if_different> tag is wrapping a normal <txp:section /> tag. That section tag will always be rendered, and in this case we want both the formal title to be displayed in addition to making it a link. After that, however, we are instructing Textpattern to go find all of the articles within that section and display each one using the form nav.build.link. Note that we are using the <txp:section /> tag (in single quotes) as the value for the section attribute in the <txp:article_custom /> tag, which is possible because of the new tag parsing engine in 4.0.7. This variable — which simply renders the current section name — enables us to avoid hard-coding section names, and helps make the navigation truly dynamic and scalable.

Making the Submenu Contextual

We’re not done yet. Using that above code, every article across all the sections would be rendered as submenu items and shown all at once, resulting in something like this:

  • About
    • Principals
    • History
    • News and Press
  • Services
    • Garage Organization
    • Gutter Cleaning
    • Interior Painting
  • Case Studies
    • Current
    • Archive
  • Contact

This is not what we set out to do. We only want the submenu for the currently active section to appear, as we discussed in the beginning of the tutorial. In order for this to happen, we need to add a little more code to our nav.build form:

<txp:if_different>
<dt><txp:section link="1" title="1" /></dt>
</txp:if_different>
*<txp:if_section name='<txp:section />'>*
<txp:article_custom section='<txp:section />' form="nav.build.link" />
*</txp:if_section>*

The new code is in bold. This restricts the output of the submenu items to only ones that are attributed to the current section. In other words, when the visitor selects “Services”, those three submenu articles will appear because they are all attributed to the services section; articles in any other section will not render because the <txp:if_section> tag is restricting the output. This is an important addition if you want to show only the submenu items that are applicable to that section.

Building the Submenu Links

OK, let’s move on to the article-type form nav.build.link, which we referenced above. This is small bit of code that outputs the content for each submenu article:

<dd><a href="<txp:permlink />"><txp:title /></a></dd>

This is an easy one. A simple <dd> tag wraps the link, with the article title as the anchor text.

Adding an “Active” Class to Menu Items

Let’s review what we have to this point. Using our introductory example of “Services” as being the section the visitor clicks, our HTML would like this:

<dl>
     <dt><a href="/about">About</a></dt>
     <dt><a href="/services">Services</a></dt>
          <dd><a href="/services/garage-organization">Garage Organization</a></dd>
          <dd><a href="/services/gutter-cleaning">Gutter Cleaning</a></dd>
          <dd><a href="/services/interior-painting">Interior Painting</a></dd>
     <dt><a href="/case-studies">Case Studies</a></dt>
     <dt><a href="/contact">Contact</a></dt>
</dl>

This is all good and fine, but to make it really ideal, we want to ensure that whatever page is currently being viewed has a class of “active” added to the corresponding menu item so that we can style it appropriately. We need to apply this to both the first level menu items (the sections) as well as the submenu items (the articles). We’ll need to edit both the nav.build and nav.build.link forms.

The Section Landing Pages

First, the first-level section-based navigation items in the nav.build form. The new code is bolded.

<txp:if_different>
<dt*<txp:if_section name='<txp:section />'>
<txp:if_individual_article><txp:else /> class="active"</txp:if_individual_article></txp:if_section>*>
<txp:section link="1" title="1" /></dt>
</txp:if_different>
<txp:if_section name='<txp:section />'>
<txp:article_custom section='<txp:section />' form="navbuild.link" />
</txp:if_section>

This is kind of convoluted, but essentially the two conditional tags <txp:if_section> and <txp:if_individual_article> are working in tandem to figure out if you’re really looking at a section landing page like /services. The former tag is triggered if you’re looking at the current section (which we are), and the second tag is triggered if the current page is not an individual article (which it is not — it’s a section). Once both conditions are met, a class of “active” is applied to the parent <dt>. Removing either conditional tag would cause the exclusivity to fail. Here is an example of the final output:

<dl>
     ...
     <dt class="active"><a href="/services">Services</a></dt>
     ...
</dl>

The Individual Article Pages

Now that we have tackled the first-level section menu items, we need to adjust the form nav.build.link so a class of “active” is applied to the appropriate submenu item when that individual page is being viewed. Here is the form again with the new code bolded:

<dd*<txp:if_article_id> class="active"</txp:if_article_id>*>
<a href="<txp:permlink />"><txp:title /></a></dd>

This one is a little lighter than the first-level menu items because we only need one conditional to figure out whether we are looking at the current page. This takes advantage of new 4.0.7 functionality in the <txp:if_article_id> tag, wherein the attribute id, when left blank, defaults to the current article. This funky logic is basically saying “if you’re looking at the current article, you must be looking at the current article.” This provides the perfect conditional for slapping a class of “active” on whatever page we’re currently viewing. Here is an example of the final output:

<dl>
     ...
     <dt><a href="/services">Services</a></dt>
          <dd class="active"><a href="/services/garage-organization">Garage Organization</a></dd>
     ...
</dl>

Summary

This tutorial supports a relatively simple navigational structure, but one that is ideal for smaller websites. Adjusting a few conditional tags here and there will alter the resulting output greatly, so I encourage you to experiment to see what can be accomplished — especially with the <txp:if_different> tag, expertise with which only comes after significant trial and error.

24 Comments Comment feed

This was an INCREDIBLY useful tutorial… my only question is, could you do this with ul and li instead of dt and dd? Would you just “nest” the second li?

Thank you for clarifying a lot of these tags for me. I’ve been using plugins to acheive my subnavs, but if I can do it without, that would be fantastic. thanks again!

Ok i figured out how to get this effect using ul and li (and nested list items) instead of dd, dt. (i enjoy working with lists for navigation- i find it more flexible, but thats just personal preference

here is what i used:

<txp:section_list default_title=’<txp:text item=“home” />’ include_default=“1” wraptag=“ul” break=”“ form=“navbuild” />

navbuild form:

<txp:if_different>
<li<txp:if_section name=’<txp:section />’>
<txp:if_individual_article>class=“active”<txp:else />class=“active”</txp:if_individual_article></txp:if_section>>
<txp:section link=“1” title=“1” /></li>
</txp:if_different>

<txp:if_section name=’<txp:section />’>
<txp:article_custom section=’<txp:section />’ form=“navbuildlinks” wraptag=“ul” class=“subnav” />
</txp:if_section>

navbuildlinks form:

<li<txp:if_article_id> class=“active”</txp:if_article_id>><a href=”<txp:permlink />”><txp:title /></a></li>

Thanks for helping me with this!

Wow… This is exactly what I have been looking for.

An intelligent way to tackle Section, Landing and Article nav with pure TXP. And I can’t believe how timely it was me finding it, after I’d just decided to use Static pages for landing pages — your solution uses precisely this. Hurrah! Most grateful to you.

AND I’m a fan of DL too (not that ULs aren’t excellent too for nav ;)

Cheers!

Thanks for the positive feedback, and I’m glad people found it useful. Marie — that is a great tweak so can use @<ul>@ instead of @<dl>@. Very clever.

Hi Kevin,

I seem to have a weird problem with TXP, immediately following the appearance of the nav.

To test it, I add before and after, the section command so the page I am viewing reports the section I am on, like this:

BEOFRE: txp:section

txp:output_form form=“nav”

AFTER: txp:section

The section changes, the BEFORE one reports correctly, and the AFTER always reports the last section in TXP.

I have tried hard to find/fix this, even just creating a vanilla install of 4.0.7, but the problem persists.

It seems that in my line that brings the nav into my page:

txp:section_list form=“nav_build” etc…

if I include the form=“nav_build” attribute, then it goes wrong — even if I include it but then have nothing in the subsequent forms (empty them). So I don’t think your code is wrong.

Sorry this is vague and don’t feel you have to reply, I am just itching to use this nav solution and would love to work out the problem. I am wondering if it is a 4.0.7 problem(?) — although I can’t believe it is…

Thanks again in advance for the post in the first place, whether you have time to reply or not.

Cheers, -Alan

Hi Kevin,

This problem I noted above is a bug in 4.0.7, about to be fixed in 4.08., I think.

Cheers, -Alan

See: http://forum.textpattern.com/viewtopic.php?pid=198217

  • Christoph
  • 31 January 2009

This is a very good tutorial. But I have two problems:
1) This is very complicated, is there really no easier way in textpattern to output a nested list? This shown here isn’t far away from coding php!
2) What about only one article in one section? The menu then show the section and the article title, but I only need the section in my menu. Any idea?
Thanks

Christoph, we just published a new tip from Els that is a response to your comment. Take a look.

@Christoph —

1) The reason for the complexity is because the navigation is completely dynamic. It has to sniff out a lot of stuff in order to display the correct info. You can always hard-code nav links to avoid all of the conditionals.

2) Jonathan’s new tip is nice, but my solution is simpler :) …

txp:section list=“first,group,of,multi-article,sections”
txp:section name=“single-article-section” title=“1” link=“1”
txp:section list=“second,group,of,multi-article,sections”

Not very elegant, but it works like a charm, and allows me to retain explicit control of the order of the sections.

Actually, in going over what I just wrote, and using this technique on another site, I just realized that splitting the navigation into different tags is unnecessary if you use the txp:if_individual_article conditional tag that I mentioned in the begining of the article to display section landing pages versus individual article pages.

  • Tym
  • 22 February 2009

Kevin, the article is great. Furthermore, you never posted an easy solution (create these forms with that content), which forces to read the whole article and makes the reader understand the logic behind it – very nice since not every TxP user has to know it in such depth. I realy like it.

A question, maybe dummy. As far as the section list goes, the order of items is quite obvious. Since I am not TxP heavy user (just need this navigation, really), is there a way to sort (by title, for instance) the list of articles in sections? I mean the way these are displayed beneath the section names inthe navigation. Thanks in advance.

@tym These are all tags that have a sort attribute Tag attribute cross-reference for sort

  • Lars Andersen
  • 28 March 2009

Nice tutorial! The effect of showing the changes in the code examples in bold does not work, instead there are asterisks embedded in the code examples!

Another thing: why does my navigation show the article id in the url? Like this: /services/08/gutter-cleaning ? :-)

  • Lars Andersen
  • 28 March 2009

Sorry, I’ve found out why on my own. :-)

@Lars – the asterisks in the code were originally bold tags set by the author, but unfortunately, Textile does not display bold tags within a code block. I left the asterisks as a visual aid.

  • Sebastian
  • 9 April 2009

Wow, this article gave me a jump start in building dynamic navigations.

I’ve tried to extend the navigation by a third layer: sections/categories/articles

The problem I’m now struggling with is that whenever I navigate through section/category/article, my navigation is completely resetted. The categories fold again and my article list vanishes. I suppose that’s because the category isn’t handed over in the article url. Is there a workaround that or can I achieve building a 3-level navigation without using categories?

Thanks,
Sebastian

@Sebastian — TXP does not support this URL structure out of the box. instead you need to use a plugin like gbp_permanent_links which is far, far beyond the scope of this tutorial :)

  • Keith Reeder
  • 27 July 2009

Excellent tutorial gents – I recently referred a buddy to it who could not get Txp’s navigation logic, and now he’s talking like an expert!

  • Josh
  • 12 November 2009

This is a great strategy for creating sub pages/subnav. What’s a sound, textpattern-native strategy for adding an additional level below the subpages?

For instance:

  • ABOUT
    - principals
    - history
    —- 1900-1955
    —- 1955 to present
    - news & press
    —- industry news
    —- company press
  • dsgnr
  • 14 July 2010

new to TXP here.
I have followed the above, but cannot get the sticky to work.
Using <txp:article status=“sticky” /> causes nothing to appear when a section is clicked.

Side NOTE: I actually cant get “sticky” to work at all. Whenever it is used content disappears (e.g. if an article is set to “sticky” through the article tab, that article will not be displayed on the live site and is also removed from the article listing)

Any help, guidance or direction, would be greatly appreciated.
Thank you in advance

@dsgnr – suggest you post on the TXP forums for help with your issue.

I always like reading what Kevin has to say….great techniques and insights.

Just found this article via a friend and, whilst I’m not a Textpattern dev, I did want to throw something into the mix about accessibility and usability…

Whilst it’s essential to show the user where they are in your site structure, simply adding a class imparts nothing semantically. Neither does linking to the page you’re currently on in the same structure.

Personally, if the publishing platform I’m developing for will allow it (and Textpattern looks good enough to do it), I swap out the current page’s A tag in side navigation for an EM element.

By all means, keep the “current” class declaration in there if it helps programatically. But relying on non-semantic element attributes to imply location isn’t as broadly useful as using a meaningful element.

  • Morrey
  • 10 March 2015

hi . i have it working fine (using Marie's UL/LI version). I wanted a submenu listing for the parent galleries and its sub-categories. But on About and Contact sections it adds a submenu for the articles in each section. I don't want any submenus for About and Contact as those sections only HAVE one entry. how to fix that?

Add a comment

Use Textile help to style your comments