Number of items in a menu

I was curious and decided to check it out, regardless if it’s relevant for a CSS problem 😉

I first peeked into the database tables to find more about the menu structure:

Menu – building blocks

Each navigational menu is registered as a term in the nav_menu taxonomy.

Then when we add items to that menu, we are creating new post objects of the type nav_menu_item.

Now the tricky part, where’s the tree structure for that menu stored?

It’s not the stored in the post_parent field of the nav_menu_item posts, as expected.

We actually find it in the post meta table where it’s stored under the _menu_item_menu_item_parent meta key, for each nav_menu_item post.

Now there are various ways to get the count. Here are few examples:

Example – get_term_by() / wp_get_nav_menu_object()

First the easy one: If we need to get the number of items in a particular menu we can actually get it from a term query, because it’s stored in the count column of the wp_term_taxonomy table.

First we get the menu term with:

$menuterm = get_term_by( 'slug', 'some-menu-slug', 'nav_menu' );

There’s exists a wrapper for this, namely:

$menuterm = wp_get_nav_menu_object( 'some-menu-slug' );

To get the total count of items in the menu, we can then use:

$total_count = ( $menuterm instanceof \WP_Term ) ? $menuterm->count : 0;

Example – WP_Query() / get_posts()

If we only want to count menu items without parents, then we can collect all the post objects within our menu with:

if( $menuterm instanceof \WP_Term )
   $pids = get_objects_in_term( $menuterm->term_id, 'nav_menu' );

where $pids is an array of post IDs.

Then we can make the following meta query:

$args = [
    'post__in'            => $pids,
    'post_type'           => 'nav_menu_item',
    'fields'              => 'ids',
    'ignore_sticky_posts' => true,
    'nopaging'            => true,
    'meta_query'          => [
        [
            'key'       => '_menu_item_menu_item_parent',
            'value'     => 0,
            'compare'   => '=',
        ]
    ]
]; 

$count = count( get_posts( $args ) );

There’s also a way with:

$q = new WP_Query();
$count = count( $q->query( $args ) );

or just:

$count = $q->found_posts;

with the common trick, posts_per_page as 1, to reduce fetched data.

Example – wp_get_nav_menu_items()

There exists handy wrappers in core, like wp_get_nav_menu_items( $menu, $args ) where $args are get_posts() arguments.

Here’s one way:

$count = count( 
    wp_list_filter( 
        wp_get_nav_menu_items( 'some-menu-slug' ), 
       [ 'menu_item_parent' => 0 ] 
    ) 
);

We can also use:

$args = [
    'meta_query'    => [
        [
            'key'       => '_menu_item_menu_item_parent',
            'value'     => 0,
            'compare'   => '=',
        ]
    ]
];

$count = count( wp_get_nav_menu_items( 'some-menu-slug', $args ) );

Example – filter/hooks

There are many filters/actions that we could hook into to do the counting, e.g. the wp_get_nav_menu_items filter. Even hooks within the nav menu walker.

We could even hook into the wp_update_nav_menu that fires when the menu is updated. Then we could count the items with no parents and store it e.g. as term meta data.

Then there are also various ways with javascript.

Hopefully this will give you some ideas to take this further.