1

I would like to add an ID attribute to each list <ul> submenu. I am using the Walker class extension. I have managed to modify some elements, but I am left to solve this one problem. I need the ID attribute to expand the menu in the mobile version. Unfortunately, I don't know exactly how to do this. The way the menu works is that I put a button in front of each submenu with a data-target attribute that targets in the ID of that submenu.

This is what the fragment of my functions.php file looks like with the Walker class extension

class Walker_N_Menu extends Walker_Nav_Menu {

    function start_lvl( &$output, $depth = 0, $args = array() ) {
        // Depth-dependent classes.
        $indent = ( $depth > 0  ? str_repeat( "\t", $depth ) : '' ); // code indent
        $display_depth = ( $depth + 1); // because it counts the first submenu as 0
        $classes = array(
            'sub-menu',
            'collapse',
            'menu-depth-' . $display_depth
        );
        $button = ( $depth > 0 ) ? '<button class="btn-submenu" data-target="#submenu1"><ion-icon name="chevron-forward-outline" role="img" class="md hydrated" aria-label="chevron forward outline"></ion-icon></button>' : '<button class="btn-submenu" data-target="#submenu1"><ion-icon name="chevron-down-outline" role="img" class="md hydrated" aria-label="chevron down outline"></ion-icon></button>';
        $class_names = implode( ' ', $classes );

        // Build HTML for output.
        $output .= "\n" . $indent . $button . "\n" . $indent . '<ul id="submenu1" class="' . $class_names . '">' . "\n";
    }

    function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        global $wp_query;
        $indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent

        // Depth-dependent classes.
        $depth_classes = array(
            ( $depth == 0 ? 'nav-item' : 'submenu-item' ),
            'menu-item-depth-' . $depth
        );
        $depth_class_names = esc_attr( implode( ' ', $depth_classes ) );

        // Passed classes.
        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );

        // Build HTML.
        $output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';

        // Link attributes.
        $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        ) .'"' : '';
        $attributes .= ' class="' . ( $depth == 0 ? 'nav-link' : '' ) . '"';

        // Build HTML output and pass through the proper filter.
        $item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
            $args->before,
            $attributes,
            $args->link_before,
            apply_filters( 'the_title', $item->title, $item->ID ),
            $args->link_after,
            $args->after
        );
        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
}

Since I don't know how to do this correctly, the code generates the same ID for each submenu at this point. Ultimately, I would like each button to have a different data-target attribute that will match each submenu ID it is over.

In addition to searching the forums for a similar topic, I tried to solve this problem using the preg_replace function by adding such a function to the functions.php file:

function change_submenu_id( $menu ) {
    for ($i = 0; $i < 10; $i++) {
        $menu = preg_replace( '/<ul id="" class="sub-menu/' , '<ul id="'. $i .'" class="sub-menu' ,$menu);
    }
}
add_filter( 'wp_nav_menu', 'change_submenu_id' );

Unfortunately, I'm doing something wrong by which this function generates the digit 0 for each ID....

I will be very grateful for any help.

0

In your Walker_N_Menu class's start_lvl() method, you're setting the <ul>'s id to submenu1. You can change this (using, in this example, the $depth parameter passed to start_lvl()) to be unique:

function start_lvl( &$output, $depth = 0, $args = array() ) {
    // Depth-dependent classes.
    $indent = ( $depth > 0  ? str_repeat( "\t", $depth ) : '' ); // code indent
    $display_depth = ( $depth + 1); // because it counts the first submenu as 0
    $classes = array(
        'sub-menu',
        'collapse',
        'menu-depth-' . $display_depth
    );
    $button = ( $depth > 0 ) ? '<button class="btn-submenu" data-target="#submenu1-' . $depth . '"><ion-icon name="chevron-forward-outline" role="img" class="md hydrated" aria-label="chevron forward outline"></ion-icon></button>' : '<button class="btn-submenu" data-target="#submenu1-' . $depth . '"><ion-icon name="chevron-down-outline" role="img" class="md hydrated" aria-label="chevron down outline"></ion-icon></button>';
    $class_names = implode( ' ', $classes );

    // Build HTML for output.
    $output .= "\n" . $indent . $button . "\n" . $indent . '<ul id="submenu1-' . $depth . '" class="' . $class_names . '">' . "\n";
}

If you need this for more than one menu in a page, you should be able to pass in a menu prefix in the $args array to the start_lvl() method.

As to why your add_filter() code doesn't seem to work properly: functions hooked to filters need to return a value, generally speaking, and your function isn't returning anything. Also, running a for() loop over the values won't do what you're expecting; I would suggest that, if you were returning a value (ie, return $menu; at the end of the function), you'd find all the values would be 9.

1
  • Thank you for the hint. At the moment I have several submenus on each level with the same id e.g. submenu1-1, submenu1-2. I don't quite understand how to solve this.
    – nsog8sm43x
    May 17 at 20:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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