Textpattern tips, tutorials and code snippets

Hierarchical category menu with articles

The idea here is to create a menu based on categories & sub-categories together with their articles, all without using plugins.

Setting up

Create the following forms as follows:

  1. cat_menu
  2. cat_menu_article
  3. cat_submenu1
  4. cat_submenu2
  5. cat_submenu3

cat_menu

<txp:category_list wraptag="ul" break="" children="0">
	<li<txp:if_category name='<txp:category />'> class="active_cat"</txp:if_category>><txp:category title="1" link="1" />
	<txp:output_form form="cat_submenu1"><txp:category /></txp:output_form>
	</li>
</txp:category_list>

cat_menu_article

<txp:variable name="active_article_class" value="" />
<txp:if_article_id>
	<txp:variable name="active_article_class" value=" active_article" />
</txp:if_article_id>
<txp:permlink class='article_class<txp:variable name="active_article_class"/>'><txp:title/></txp:permlink>

cat_submenu1

<txp:variable name="submenu1_cats_found" value="0" />
<txp:category_list wraptag="ul" break="" parent='<txp:yield />' children="0" exclude='<txp:yield />'>
	<txp:variable name="submenu1_cats_found" value="1" />
	<li<txp:if_category name='<txp:category />'> class="active_cat"</txp:if_category>><txp:category title="1" link="1" />
	<txp:output_form form="cat_submenu2"><txp:category /></txp:output_form>
	</li>
	<txp:if_variable name="cat_menu_articles" value="1">
		<txp:if_last_category>
			<txp:article_custom category='<txp:yield />' break="li" sort="title asc" form="cat_menu_article" />
		</txp:if_last_category>
	</txp:if_variable>
</txp:category_list>

<txp:if_variable name="cat_menu_articles" value="1">
	<txp:if_variable name="submenu1_cats_found" value="1">
	<txp:else/>
		<txp:article_custom category='<txp:yield />' wraptag="ul" break="li" sort="title asc" form="cat_menu_article" />
	</txp:if_variable>
</txp:if_variable>

cat_submenu2

<txp:variable name="submenu2_cats_found" value="0" />
<txp:category_list wraptag="ul" break="" parent='<txp:yield />' children="0" exclude='<txp:yield />'>
	<txp:variable name="submenu2_cats_found" value="1" />
	<li<txp:if_category name='<txp:category />'> class="active_cat"</txp:if_category>><txp:category title="1" link="1" />
	<txp:output_form form="cat_submenu3"><txp:category /></txp:output_form>
	</li>
	<txp:if_variable name="cat_menu_articles" value="1">
		<txp:if_last_category>
			<txp:article_custom category='<txp:yield />' break="li" sort="title asc" form="cat_menu_article" />
		</txp:if_last_category>
	</txp:if_variable>
</txp:category_list>

<txp:if_variable name="cat_menu_articles" value="1">
	<txp:if_variable name="submenu2_cats_found" value="1">
	<txp:else/>
		<txp:article_custom category='<txp:yield />' wraptag="ul" break="li" sort="title asc" form="cat_menu_article" />
	</txp:if_variable>
</txp:if_variable>

cat_submenu3

<txp:variable name="submenu3_cats_found" value="0" />
<txp:category_list wraptag="ul" break="" parent='<txp:yield />' children="0" exclude='<txp:yield />'>
	<txp:variable name="submenu3_cats_found" value="1" />
	<li<txp:if_category name='<txp:category />'> class="active_cat"</txp:if_category>><txp:category title="1" link="1" />
	<txp:if_variable name="cat_menu_articles" value="1">
		<txp:article_custom category='<txp:category />' wraptag="ul" break="li" sort="title asc" form="cat_menu_article" />
	</txp:if_variable>
	</li>
	<txp:if_variable name="cat_menu_articles" value="1">
		<txp:if_last_category>
			<txp:article_custom category='<txp:yield />' break="li" sort="title asc" form="cat_menu_article" />
		</txp:if_last_category>
	</txp:if_variable>
</txp:category_list>

<txp:if_variable name="cat_menu_articles" value="1">
	<txp:if_variable name="submenu3_cats_found" value="1">
	<txp:else/>
		<txp:article_custom category='<txp:yield />' wraptag="ul" break="li" sort="title asc" form="cat_menu_article" />
	</txp:if_variable>
</txp:if_variable>

Outputting the menu on the page:

<txp:variable name="cat_menu_articles" value="1" />
<txp:output_form form="cat_menu" />

The result

You should end up with something like:

  • category1
    • subcategory1.1
      • subcategory1.1.1
      • subcategory1.1.2
      • article1.1a
    • subcategory1.2
    • article1a
    • article1b
  • category2
    • subcategory2.1
    • subcategory2.2
      • subcategory2.2.1
        • subcategory2.2.1.1
        • subcategory2.2.1.2
          • article2.2.1.2a
        • article2.2.1a
        • article2.2.1b
    • subcategory2.3
  • category3
    • subcategory3.1
      • article3.1a
      • article3.1b
  • category4
    • article4a
    • article4b
    • article4c

Notes

  • Textpattern 4.2.0+ is required
  • This solution will give you 4 levels of categories & sub-categories, with articles dangling at the appropriate places
  • If you don’t want articles, then don’t set the cat_menu_articles variable before running the cat_menu form
  • The active category will have a class of “active_cat” applied
  • The active article will have a class of “active_article” applied
  • all article links have a class of “article_class”

4 Comments Comment feed

WOW!
Just trying this on a new website.
Still have to understand how to tweak and style this monster.
Main issue with creating a category driven sidebar menu was that your adi_cat_menu doesn’t offer such multi-level output :)

Class active_cat needs to be assigned to the a instead of li. Otherwise the class is applied to the whole li including the submenu ul.

Pls. note the nested tags with one ' and two single quotes. My working code for active anchors (TXP 4.4.1):

<li><txp:category title="1" link="1"
 class='<txp:if_category name=''<txp:category />''>
active_cat
</txp:if_category>' />

This needs to be done for cat_menu and all three submenus.

txp:category

class=“class name”
(X)HTML class attribute, applied to wraptag. If no wraptag is supplied (and link=“1”), the class is applied to the anchor instead.
Default: unset.

(The same is true for articles dangling at the appropriate places. They are listed at the end of the submenu .. still have to check that.)

Another small trap.
Per default the menu outputs li which filter the landing page by section.

Example: If all articles reside in section ‘articles’ the landing page will be empty if you click the menu inside section ‘about’.

Deactivate the section filter: To get consistent landing pages you have to add section="" to txp:category.

<li><txp:category title="1" link="1"
 class='<txp:if_category name=''<txp:category />''>
active_cat
</txp:if_category>' />

txp:category

section=“section name”
Restricts category search to named section.
Default: current section (for backwards compatibility).

Sorry, I forgot to add the section="" to the code. This is the correct one:

<li><txp:category section="" title="1" link="1"
 class='<txp:if_category name=''<txp:category />''>
active_cat
</txp:if_category>' />

Add a comment

Use Textile help to style your comments