1

I am creating a split menu (half the items on the left, logo in the middle, remaining items on the right). I don't really understand Walkers all that well, but I found this code snippet.

This was working well for me until my client wanted to add child items. The current code is counting all the menu items (10) when I want it to just count the top level items (currently 7).

How do I adjust this code so that it only counts the top level items?

class Split_Menu_Walker extends Walker_Nav_Menu {

    var $current_menu = null;
    var $break_point  = null;

    function start_el(&$output, $item, $depth, $args, $id=0) {

        global $wp_query;

        if( !isset( $this->current_menu ) )
            $this->current_menu = wp_get_nav_menu_object( $args->menu );

        if( !isset( $this->break_point ) )
            $this->break_point = ceil( $this->current_menu->count / 2 ) + 1;

        $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

        $class_names = $value = '';

        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $classes[] = 'menu-item-' . $item->ID;

        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
        $class_names = ' class="' . esc_attr( $class_names ) . '"';

        $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
        $id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';

        if( $this->break_point == $item->menu_order )
            $output .= $indent . '</li></ul><ul><li' . $id . $value . $class_names .'>';
        else
            $output .= $indent . '<li' . $id . $value . $class_names .'>';

        $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
        $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
        $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
        $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';

        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'>';
        $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
}

1 Answer 1

3

Calculation you talking about going here:

if( !isset( $this->break_point ) )
        $this->break_point = ceil( $this->current_menu->count / 2 ) + 1;

You can't get top-level items count this way. Instead you should count it yourself. You can do this using wp_get_nav_menu_items(). Every top-level item should have menu_item_parent set to '0'.

You should create additional variable where you calculate already displayed elements. So top of your file should looks like this:

class Split_Menu_Walker extends Walker_Nav_Menu {

    public $break_point = null;
    public $displayed = 0; 

function start_el(&$output, $item, $depth, $args, $id=0) {

    global $wp_query;

    if( !isset( $this->break_point ) ) {
        $menu_elements = wp_get_nav_menu_items( $args->menu );
        $top_level_elements = 0;

        foreach( $menu_elements as $el ) {
            if( $el->menu_item_parent === '0' ) {
                $top_level_elements++;
            }
        }
        $this->break_point = ceil( $top_level_elements / 2 ) + 1;
     }   


    $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
...
...
...

Then you should increment $this->displayed++; every time you display top level element (for example at the end of start_el() function):

if( $item->menu_item_parent === '0' ) {
    $this->displayed++;
}

Lastly instead of

if( $this->break_point == $item->menu_order ) 

you should use

if( $this->break_point == $this->displayed )
10
  • Thank you! This is now getting the correct break point, but since it is using if( $this->break_point == $item->menu_order ) to check if it's it's splitting it within a submenu. Is there a way to make sure that it only splits at a parent level?
    – clancimus
    Nov 17, 2015 at 14:21
  • You can do this exactly the same way. Just check if $item has menu_item_parent set to '0', so something like this: if( $this->break_point == $item->menu_order && $item->menu_item_parent === '0' ) Nov 17, 2015 at 15:10
  • This is the logic I initially thought of, however since it reaches the break point at a submenu, it just skips now and doesn't put the split in at all. The logic I'm thinking of is "If the break point has been reached and it's a parent item, put the break in. Else, continue on until the submenu has closed, then put the break in". I'm just not sure how to write this logic into the walker.
    – clancimus
    Nov 17, 2015 at 15:38
  • Ok, I updated the answer, so now i think it's complete. If it helped you please mark this answer as correct. :) Nov 17, 2015 at 16:34
  • I modified my code, but it's showing the way that it did in the beginning. I have the following menu structure: Parent Parent - Child - Child - Child Parent Parent Parent Parent Parent And it's splitting after the third child. Here is my updated code: pastebin.com/K7xan2qM
    – clancimus
    Nov 17, 2015 at 16:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.