I’m developing a wordpress theme with nested submenus. I need to make the elements with no children visually different from the ones that have children. Right now I have this menu, but that could change:
A
a1
a2
B
b1
b2
C
As you can see, A and B have children. C doesn’t – I need it to be different in the CSS level.
Ideally, I would like to have a has-children
class in A and B, but not in C.
So far I’ve managed to create a “Menu Walker” PHP class that I can instantiate and pass to wp_nav_menu . Its constructor looks like this:
class My_Walker_Nav_Menu extends Walker_Nav_Menu {
function start_el(&$output, $item, $depth, $args) {
...
if(??? $item has children???) {
// I know what to do here
}
}
}
So, how do I tell whether $item
has children, or is a leaf?
EDIT: this question was answered by someone called “keesiemeijer” in the WordPress forums. I’m leaving this bounty expired just in case he wants to reclaim it. Otherwise, I’ll be marking my own answer as valid.
Add this to
functions.php
it will add the ‘dropdown’ class to parentsNew way beter for performance
Old: intensive on the DB
Simple you use this way:
Explain:
I create menu with “walker”:
Class Walker:
We have object ‘walker’, you can var_dump($args) to see more things.
I’m using for my project !
It seems that the problem has finally been addressed. The latest WordPress beta as of current writing 4.0, has updated the Walker_Nav_Menu class and added a
$has_children
property.So, we don’t need to hack
function display_element(...)
anymore.I asked in the WordPress forum and keesiemeijer pointed me to this other post, in which they did the following:
Instead of modifying
start_el
, they modifieddisplay_element
, adding the following two lines (lines 37-38 here):I’ve left the previous two lines as a spacial reference, and also as a comment to other answers in this post. It seems that wordpress is “trying” to set a ´has_children´ property in
$args
, but it’s either doing it wrong or in a way I don’t understand. In any case, thathas_children
parameter is never passed down tostart_el
(see sample var_dump of an $args here)This might be a bug on the WordPress version I’ve got (3.2.1) and might have been fixed in the most recent version.
In any case, the answer I got in the WordPress forum is the one that helped me fix it, so I consider this settled. I’ll wait for the bounty to expire just in case keesiemeijer wants to put his answer here.
Add this to your functions.php
Then in your walker your can check if $item->has_children is true or false
Kikito’s answer above gets the trick done, but not in the most reusable way. In my view, the better approach is like this:
Overriding
Walker::display_element()
is the right move, but it’s better to actually address the problem at the root of the issue rather than simply tacking a class on at this point, for two reasons. First, the real problem isn’t a missing class but rather the un-patched bug in WordPress that Kikito noted: the problem is that$args[0]
isn’t always an array. That appears to be the typically expected type forWalker::display_element()
, but we’re actually dealing here withWalker_Nav_Menu::display_element()
, and in that caseargs
ends up being passed in as a standard object type rather than an array type. As such, we simply need to add thehas_children
element using object notation instead of array notation. Problem solved![1]Adding that
elseif
accounts for the ordinary Nav Menu case. This is the same form that will hopefully make it into the core class in this patch, at which point you’ll no longer have to extend it. They should probably patch it further to account for the case that$args[0]
is neither an array nor an object, but I don’t expect to see that happen.Second, in order to keep good separation of concerns between the various methods, classes should really be added in the
start_el()
method or elsewhere, sincedisplay_element()
isn’t doing any of the class handling.As a result, you can then override
start_el()
however you like: you can add your own custom classes, or ignore elements entirely, or supply custom text, or whatever you like. (In my case, I’m working around an existing Javascript menu implementation that has very specific classing requirements based on parents and children, so I can’t just add the same classes to everything that has a child â which is precisely why this separation of concerns matters.) In my code:[1] This is of course one of the potential pitfalls of dynamically typed languages like PHP… it’s not a problem, as long as you’re careful. The WordPress developers weren’t careful here.
If you don’t want the overhead of a hard query or function, you can do this in jQuery:
}
Since this question is one of the first results on Google search, has been referenced in other webpages and most the of the answers here are outdated I thought I would post this answer.
start_el() has classes as part of $item object. So You can add this in start_el():
Note that $depth is not required in the conditions but if removed your code will be applied to the first item (i.e. item with 0 depth).
As for the compatibility part, the class ‘menu-item-has-children’ has been added to WordPress 3.7 (October 2013) and I have tested it on the latest WordPress 4.4 (as of the time of posting this answer).
Instead of rewriting core functionality of the
Walker_Nav_Menu
orWalker
class methods let’s just use good old class inheritance.The Code
How It Works
Has_Child_Walker_Nav_Menu
that inherits fromWalker_Nav_Menu
that in turn inherits fromWalker
.Walker->display_element()
method with our own function since it the first method called for each individual menu-items while we are walking the menu item tree.display_element()
method we create a new propertyhas_children
on the$element
a.k.a. menu item. This makes the property available on the$item
object in bothstart_el()
andend_el()
methods, this means it also passed to the filters called bystart_el()
.has_children
property value is calculated the same way theWalker
instance propertyhas_children
is determined.display_element()
method on the parentWalker_Nav_Menu
… well actually grandparentWalker
sinceWalker_Nav_Menu
doesn’t have it’s owndisplay_element()
method. We make sure to pass all the values from ourdisplay_element()
method to theWalker->display_element()
method.That’s it!
Now we have
$item->has_children
available anywhere we might need it including filters.Example Usage
The Menu
The Filter
Thanks for Start_el function, my function follow that function to run query.
I have a function will count sub menu of parent menu by ID.
Run
In foreach of wp_get_nav_menu_items function, select ID of parent Menu by $item->menu_item_parent ==0.
It’s working for me and very simple.
There is a simple solution source.
use this simple code in you walker class