A Smarter Menu for WordPress
I’ve always thought it would be nice to combine pages and categories all inside one menu, and recently I was looking for a nice way to add subtitles as well. So I thought it would be cool to write my own menu function, to suit my needs.
First off I want to give credit to a couple of posts, on how to add subtitles via custom fields, sadly, each had their separate issue.
In the first post I found by Shantanu you would have to hack classes.php, a WordPress core file (not my cup of tea). The second one by Theme Shaper was lacking one very essential thing, dynamic active classes! (A non-dynamic menu is a non-happy menu).
The second post by Theme Shaper was almost doing what I was looking for so I decided to hack on it. The code was wrapped in a function which I could easily add to any of my themes, and it uses custom fields for the job, easy-peasy:)
I’ve spent a great deal of time figuring out how to do this and finally I came up with a solution that worked, next phase was to add categories and home link, ontop of that, I’ve added some more snacks, for instance you can chose if you want the home link and categories to show up, (with active classes of course) and you can sort the listing. Subtitles for home and categories are fetched from their respective description container. Enjoy!
Currently missing the active class on single blog posts, will fix soon. Also, I’ve included the same behaviour on child-pages but I haven’t tested this. If you need it, please test it and report back;)
Add this code to functions.php in your theme folder and call it with
<?php wp_smart_menu(); ?>
// Smart Menu with Subtitles by Jon Kristian Nilsen - http://jonkristian.no
function wp_smart_menu() {
$show_home = "true"; // Show home link true/false.
$home_first = "false"; // In a horizontal list, show home first or last depending on the float, true/false.
$show_cat = "true"; // Show Categories true/false.
$cat_first = "true"; // In a horizontal list, show categories first or last depending on the float, true/false.
$cat_id = "1, 9"; // Filter categories by ID, leave blank for all.
$metakey = "Description"; // The meta key for your subtitle text.
$description = "under"; // Where you want the subtitle over/under link.
$order = "desc"; // Set the link order asc/desc.
global $wpdb;
$sql = "SELECT p.ID, p.post_title, p.guid, pm.meta_value FROM " . $wpdb->posts . " AS p LEFT JOIN ";
$sql .= "(SELECT post_id, meta_value FROM " . $wpdb->postmeta . " AS ipm WHERE meta_key = '$metakey') ";
$sql .= "AS pm ON p.ID = pm.post_id ";
$sql .= "WHERE p.post_type = 'page' AND p.post_parent = 0 AND p.post_status = 'publish' ";
$sql .= "ORDER BY p.menu_order $order ";
$sql .= "LIMIT 0, 10";
$rows = $wpdb->get_results($sql,OBJECT);
if($rows) {
foreach($rows as $row) {
global $post;
$home_url = get_bloginfo('home');
$home_desc = get_bloginfo('description');
$current_page = $post;
$page_id = $row->ID;
$page_url = get_permalink($row->ID);
echo "\n <li>\n";
if ( $show_home == "true" && $home_first == "true" ) {
if (is_front_page()) {
echo " <ul class=\"active\">\n";
} else {
echo " <ul class=\"page_item\">\n";
}
echo " <li class=\"title\"><a href=\"$home_url\">Home</a></li>\n";
echo " <li class=\"description\"><p>$home_desc</p></li>";
echo "\n </ul>\n";
}
if ( $show_cat == "true" && $cat_first == "true" ) {
$categories = get_categories("include=$cat_id");
foreach ($categories as $cat) {
$cat_url = get_category_link( $cat->term_id );
if ( is_category($cat->cat_name) || is_single() && in_category($cat->cat_name) ) {
echo " <ul class=\"active\">\n";
} else {
echo "\n <ul class=\"page_item\">\n";
}
echo " <li class=\"title\"><a href=\"$cat_url\">$cat->cat_name</a></li>\n";
echo " <li class=\"description\"><p>$cat->description</p></li>";
echo "\n </ul>\n";
}
}
if ( !empty($current_page) ) {
$_current_page = get_page( $current_page );
if ( in_array($page_id, (array) $_current_page->ancestors) )
echo " <ul class=\"ancestor\">\n";
if ( $page_id == $current_page )
echo " <ul class=\"active\">\n";
elseif ( $_current_page && $page_id == $_current_page->post_parent )
echo " <ul class=\"parent\">\n";
else
echo " <ul class=\"page_item\">\n";
}
if ( $description == "over" ) {
echo " <li class=\"description\"><p>$row->meta_value</p></li>\n";
echo " <li class=\"title\"><a href=\"$page_url\">$row->post_title</a></li>";
} elseif ( $description == "under" ) {
echo " <li class=\"title\"><a href=\"$page_url\">$row->post_title</a></li>\n";
echo " <li class=\"description\"><p>$row->meta_value</p></li>";
}
echo "\n </ul>\n";
}
if ( $show_cat == "true" && $cat_first == "false" ) {
$categories = get_categories("include=$cat_id");
foreach ($categories as $cat) {
$cat_url = get_category_link( $cat->term_id );
if ( is_category($cat->cat_name) ) {
echo " <ul class=\"active\">\n";
} else {
echo "\n <ul class=\"page_item\">\n";
}
echo " <li class=\"title\"><a href=\"$cat_url\">$cat->cat_name</a></li>\n";
echo " <li class=\"description\"><p>$cat->description</p></li>";
echo "\n </ul>";
}
}
if ( $show_home == "true" && $home_first == "false" ) {
if (is_front_page()) {
echo " <ul class=\"active\">\n";
} else {
echo " <ul class=\"page_item\">\n";
}
echo " <li class=\"title\"><a href=\"$home_url\">Home</a></li>\n";
echo " <li class=\"description\"><p>$home_desc</p></li>";
echo "\n </ul>\n";
}
echo "\n </li>\n";
}
}
thanks for this, I’m sure it will come in handy at some point.
Just wondering if you had a live demo somewhere?
cheers
paul
Sure, I am using it on this site:)
Great menu idea… Looks sharp on this website. I’m looking forward to trying it on a future project.
Thanks!
Great work! I have worked on the same problem trying to implement jQuery’s Superfish menu with Wordpress. Your solution is much more complete. I will try it out.
This rewriting of the menu code causes you to loose some of the dynamic classes. This makes styling of different menu items difficult. I have a pretty simple plug-in that allows the use of wp_page_menu(). The plug-in is a rewrite of Page Lists Plus to strip out all of the extra code to make this plug-in as simple as possible. It is available on my site http://www.thetemplateblog.com/plug-ins/add-wordpress-menu-description#more-604 if you are interested.
Dave
TheTemplateBlog
Nice, a plugin would be beneficial for some, so kudos to that. I was not aware of any issues with my function, it would be nice to get some feedback instead of reports elsewhere that it isn’t working and why. I will post an updated version to take care of this.
Jon, Please accept my apology for the way this was brought to your attention. I have changed the wording in the post as I thought it was less then flattering of the hard work that you put into your solution.
Dave
Dave, No worries:) apology accepted, and thanks for bringing up the problem. I created this function with focus on the dynamic highlighting classes, so it’s important for me that they work:)
Thanks Jon, I also notice another bug when you set the ‘$show_home’ menu to ‘true’. When I enabled that I got a home menu between each of my other menus.
Dave
I would be happy to test your revisions if you like.
I see, I will take a look at this next week, kinda piled up in work at the moment.
Hi Jon, not totally sure if this is intended or not (guessing not), but it seems that if I set both $show_home and $home_first to true, the Home menu item shows up before each other menu item. This would be a result of it being included in the foreach() loop.
If I may make one suggestion, it would be to store all the output in a string variable and then output that string at the end. This way, you could tack the home link onto the front or back of that string based on the developer’s preference. I’ve updated my version of the code to reflect this and am glad to share if you’re interested…I know life can be busy :)
All the same, thanks for doing development on this! Almost exactly what I needed for a current project.
Thanks for the tip, that sounds good. I was going to revisit the code at some stage, hopefully I will get some time to do this soon:) Life can be really busy ;)
Very cool, I’ve been looking for this for a long time. However what I miss about this is the option to have a custom CSS class for every single page item. I’m working on a navigation where the menu text will be replaced with images, therefore every of my menu items need it’s own CSS class.
This is quite easy to achieve, you just need to fetch the page name and output it, I can include this in the next update. And by the way, I am sorry for not updating my function yet, but I’ve been quite tied up, keep your eyes open for a new one very soon.