There are a thousand ways to accomplish menu styling, and everyone has their own goals and objectives when designing them. ListMenuX was a simple, recursive list menu maker with the ability to allow you to specify style classes for each element, and even append suffixes to them to distinguish different levels. I have many versions of this all over the web that work the way I needed them to. Your requirements for the generated markup may be different, as may be your approach to styling them with CSS, so I’m sure you’ll need to modify the script to get it to behave exactly as you want it to (as sottwell did)..
That said, we did not use ListMenuX on the MODx CMS site, as we already had a another snippet which added a hardcoded class called .here to the first level LI that is the parent of the current page, so you can highlight the parent and current page with appropriate styles.
In the meantime, here is the menu snippet from the MODx CMS site, which is being refactored (by allowing a lot more configuration parameters, such as id, class, and style selectors for each list, element, and/or link, optional surrounding divs, etc.) for release as the default menu building snippet in the next release, if you’re interested in that specific menu behavior:
// ###########################################
// Drop Menu #
// ###########################################
// Create navigation that allows for non-content section folders
// Can be styled pretty much in any way to function as a
// that link to the first child of the folder.
//
// Developed by Vertexworks.com and Opengeek.com for modxcms.com
// Feel free to use if you keep this header and credits in place
//
// Inspired by List Site Map by Jaredc and SimpleList by Bravado
// ###########################################
// Configuration parameters #
// ###########################################
// $siteMapRoot [int]
// The parent ID of your root. Default 0. Can be set in
// snippet call with startDoc (to doc id 10 for example):
// [[DropMenu?startDoc=10]]
$siteMapRoot = 0;
// $titleOfLinks [ string ]
// What database field do you want the title of your links to be?
// The default is pagetitle because it is always a valid (not empty)
// value, but if you prefer it can be any of the following:
// id, pagetitle, description, parent, alias, longtitle
$titleOfLinks = 'pagetitle';
// $removeNewLines [ true | false ]
// If you want new lines removed from code, set to true. This is generally
// better for IE when lists are styled vertically.
$removeNewLines = false;
// $maxLevels [ int ]
// Maximum number of levels to include. The default 0 will allow all
// levels. Also settable with snippet variable levelLimit:
// [[DropMenu?levelLimit=2]]
$maxLevels = 0;
// $showDescription [true | false]
// Specify if you would like to include the description
// with the page title link.
$showDescription = false;
// $selfAsLink [ true | false ]
// Define if the current page should be a link (true) or not
// (false)
$selfAsLink = false;
// $pre [ string ]
// Text to append before links inside of LIs
$pre = '';
// $post [ string ]
// Text to append before links inside of LIs
$post = '';
// Need to add ability to add hover zones
// Styles
// static for now, need to convert to pass into the snippet
// .topnav span surrounding current page if $selfAsLink is false
// .subnav description of page
// .here you are here
// ###########################################
// End config, the rest takes care of itself #
// ###########################################
// Initialize
$MakeMap = "";
$siteMapRoot = (isset($startDoc))? $startDoc : $siteMapRoot ;
$maxLevels = (isset($levelLimit))? $levelLimit : $maxLevels ;
$ie = ($removeNewLines)? '' : "\n" ;
// Overcome single use limitation on functions
global $MakeMap_Defined;
if( !isset( $MakeMap_Defined ) ) {
function MakeMap($callBy, $listParent, $listLevel, $description, $titleOfLinks, $maxLevels, $inside, $pre, $post, $selfAsLink, $ie, $activeLinkIDs){
$children = $callBy->getActiveChildren($listParent, 'menuindex', 'ASC', 'id, pagetitle, description, isfolder, content, parent, alias, longtitle' );
if(is_array($children) && !empty($children)) {
$numChildren = count($children);
// determine if it's a top category or not
$toplevel = !$inside;
$output = ($toplevel)? '<ul class="topnav">'.$ie : $ie.' <ul class="subnav">'.$ie ;
//loop through and process subchildren
foreach($children as $child) {
// figure out if it's a containing category folder or not
$numChildren--;
$isFolder = $child['isfolder'];
$itm = "";
if (!$inside) {
$itm .= (!$selfAsLink && ($child['id']==$callBy->documentIdentifier))?
$pre.$child['pagetitle'].$post :
'<a href="[~'.$child['id'].'~]" title="'.$child['longtitle'].'">'.$pre.$child['pagetitle'].$post.'</a>' ;
} elseif ($isFolder && $inside) {
$itm .= '<a href="[~'.$child['id'].'~]" title="'.$child['longtitle'].'">'.$child['pagetitle'].'</a>';
} else {
$itm .= ($child['alias'] > '0')? '<a href="[~'.$child['id'].'~]" title="'.$child['longtitle'].'">'.$child['pagetitle'].'</a>': '<span>'.$child['pagetitle'].'</span>';
}
// loop back through if the doc is a folder and has not reached the max levels
if ($isFolder && (($maxLevels==0) || ($maxLevels > $listLevel+1 ))) {
$itm .= MakeMap($callBy, $child['id'], $listLevel+1, $description, $titleOfLinks, $maxLevels, true, $pre, $post, $selfAsLink, $ie, $activeLinkIDs);
}
if ($itm && !$selfAsLink && ($child['id']==$callBy->documentIdentifier) ) {
$output .= " <li class=\"here".($numChildren==0?' last':'')."\">$itm</li>$ie";
} elseif ($itm) {
if($numChildren==0) {
$class='last';
}
if(is_array($activeLinkIDs)) {
if(in_array($child['id'],$activeLinkIDs)) {
$class.=($class?' ':'').'here';
}
}
if($class) {
$class = ' class="'.$class.'"';
}
$output .= "<li".$class.">$itm</li>$ie";
$class='';
}
}
$output .= "</ul>$ie";
}
return $output;
}
$MakeMap_Defined = true;
}
$currentID = $modx->documentObject['id'];
$parentID = $currentID;
// find the parent docs of the current "you-are-here" doc
// used in the logic to mark parents as such also
while($parentID != $siteMapRoot && $parentID!=0) {
$parent = $modx->getParent($parentID,0);
if($parent) {
$parentID = $parent['id'];
$activeLinkIDs[] = $parentID;
} else {
$parentID = 0;
}
}
return MakeMap($modx, $siteMapRoot, 0, $showDescription, $titleOfLinks, $maxLevels, false, $pre, $post, $selfAsLink, $ie, $activeLinkIDs);