Adding custom classes to menu items

Hi, I am creating a custom theme based on the Default one. I’d like to add custom classes to the <ul> and <li> elements of the main navigation menu. I saw the menu is built through a call to the $site->publicNav()->menu()->renderMenu(...) function in the layout.phtml template, but I could’nt find any documentation about this function. Is it the right way to achieve this?

Hi @boregar ,

I know you can pass in an option to add a class to the ul, but I’m not sure about the li.

<?php
   echo $site->publicNav()->menu()->renderMenu(null, [
       'maxDepth' => $this->themeSetting('nav_depth') - 1,
       'ulClass' => 'your-class',
   ]);
?>

Thanks fackrellj. The ulClass argument works and I tried to add a liClass without success.

Anyway I need to have a finer control over the output, like conditional classes. For example, the custom classes may vary depending on the depth of the menu item, or if it has children or not etc.

Is the rendermenu function overridable in some way? In which file is its code located?

Back after some research…

The site’s public navigation stuff is built in the /application/src/Api/Representation/SiteRepresentation.php file using Laminas components (more info in this Laminas navigation doc).

Yes, that is correct. I wonder if you could accomplish what you need with just CSS. However, I was coming back to say that you can override the whole menu with the partial used to generate the menu Menu - laminas-navigation - Laminas Docs

That’s exactly what I am doing right now! I’ll get back with the result :wink:

Hi, the methods like setPartial() work with the $site->publicNav()->menu() object if called this way:

echo $site->publicNav()->menu()->setPartial('...');

The Menu object has some useful methods but none of them allows to customize the classes of the menu item tags:

array(59) {
  [0]=>
  string(8) "__invoke"
  [1]=>
  string(6) "render"
  [2]=>
  string(10) "renderMenu"
  [3]=>
  string(13) "renderPartial"
  [4]=>
  string(23) "renderPartialWithParams"
  [5]=>
  string(13) "renderSubMenu"
  [6]=>
  string(7) "htmlify"
  [7]=>
  string(12) "escapeLabels"
  [8]=>
  string(21) "setAddClassToListItem"
  [9]=>
  string(21) "getAddClassToListItem"
  [10]=>
  string(19) "setOnlyActiveBranch"
  [11]=>
  string(19) "getOnlyActiveBranch"
  [12]=>
  string(10) "setPartial"
  [13]=>
  string(10) "getPartial"
  [14]=>
  string(16) "setRenderParents"
  [15]=>
  string(16) "getRenderParents"
  [16]=>
  string(10) "setUlClass"
  [17]=>
  string(10) "getUlClass"
  [18]=>
  string(16) "setLiActiveClass"
  [19]=>
  string(16) "getLiActiveClass"
  [20]=>
  string(6) "__call"
  [21]=>
  string(10) "__toString"
  [22]=>
  string(10) "findActive"
  [23]=>
  string(6) "accept"
  [24]=>
  string(6) "setAcl"
  [25]=>
  string(6) "getAcl"
  [26]=>
  string(6) "hasAcl"
  [27]=>
  string(15) "setEventManager"
  [28]=>
  string(15) "getEventManager"
  [29]=>
  string(12) "setContainer"
  [30]=>
  string(12) "getContainer"
  [31]=>
  string(12) "hasContainer"
  [32]=>
  string(9) "setIndent"
  [33]=>
  string(9) "getIndent"
  [34]=>
  string(11) "setMaxDepth"
  [35]=>
  string(11) "getMaxDepth"
  [36]=>
  string(11) "setMinDepth"
  [37]=>
  string(11) "getMinDepth"
  [38]=>
  string(18) "setRenderInvisible"
  [39]=>
  string(18) "getRenderInvisible"
  [40]=>
  string(7) "setRole"
  [41]=>
  string(7) "getRole"
  [42]=>
  string(7) "hasRole"
  [43]=>
  string(17) "setServiceLocator"
  [44]=>
  string(17) "getServiceLocator"
  [45]=>
  string(9) "setUseAcl"
  [46]=>
  string(9) "getUseAcl"
  [47]=>
  string(13) "setDefaultAcl"
  [48]=>
  string(14) "setDefaultRole"
  [49]=>
  string(17) "getClosingBracket"
  [50]=>
  string(7) "setView"
  [51]=>
  string(7) "getView"
  [52]=>
  string(13) "setTranslator"
  [53]=>
  string(13) "getTranslator"
  [54]=>
  string(13) "hasTranslator"
  [55]=>
  string(20) "setTranslatorEnabled"
  [56]=>
  string(19) "isTranslatorEnabled"
  [57]=>
  string(23) "setTranslatorTextDomain"
  [58]=>
  string(23) "getTranslatorTextDomain"
}

So the most promising way would be to iterate through the nodes (the “pages”) of the menu tree (the “container”) using an iterator, like in the following:

$container = $site->publicNav()->menu()->getContainer();
$iter = new RecursiveIteratorIterator($container, RecursiveIteratorIterator::SELF_FIRST);
foreach ($iter as $page) {
  // render the menu item according to its depth, active status etc.
}

Hi there,

If you’d like to look at one of our themes that sets classes on the menu <li>, you can peek at Foundation for Omeka S. In this line, it sets a partial and is able to pass parameters to it. The variables in the example are mainly to apply a class and id to the main <ul>, but they can be anything really.

<?php echo $site->publicNav()->menu()->setPartial('common/foundation-navigation.phtml')->renderPartialWithParams(['layout' => 'vertical', 'navId' => 'main-nav']); ?>

Here is a link to the partial itself, and how it applies custom classes.

Hopefully there’s something in there that’s helpful.

1 Like

Thanks kim, the code I finally wrote for my theme (built using the Bulma framework) uses a similar approach as yours (iterating through the container’s pages). I now have a nice menu :sweat_smile: