How can I disable parent menu item links?

Do not create fake URLs (#). This would be very bad for users with a screen reader: the are using a list of available links to browse your site. The same is true for javascript: links, they are not exactly elegant markup anyway.

You need two steps:

  1. Mark the items with children before the walker starts.
  2. Replace the markup for the top parents.

I will use an empty <a> element here to make it easy to get the cursor. You will probably need something like this in your stylesheet:

.menu > .has-children {
    cursor: pointer;
}

The filter to mark parent items is taken from this answer. The second function just checks for that property and it tests if the item is itself not a child item.

add_filter( 'wp_nav_menu_objects',      't5_add_has_children_to_nav_items' );
add_filter( 'walker_nav_menu_start_el', 't5_unlink_parent_item', 10, 4 );

/**
 * Add aproperty 'has_children' to menu items
 *
 * @wp-hook wp_nav_menu_objects
 * @param   array $items
 * @return  array
 */
function t5_add_has_children_to_nav_items( $items )
{
    $parents = wp_list_pluck( $items, 'menu_item_parent' );
    $out     = array ();

    foreach ( $items as $item )
    {
        in_array( $item->ID, $parents ) && $item->has_children = TRUE;
        $out[] = $item;
    }
    return $items;
}
/**
 * Replace top parent element markup.
 *
 * @wp-hook walker_nav_menu_start_el
 * @param   string $item_output
 * @param   object $item
 * @param   int    $depth
 * @param   object $args
 * @return  string
 */
function t5_unlink_parent_item( $item_output, $item, $depth, $args )
{
    // not first level parent item
    if ( empty ( $item->has_children ) or 0 != $item->menu_item_parent )
        return $item_output;

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

    $classes = empty( $item->classes ) ? array() : (array) $item->classes;
    $classes[] = 'menu-item-' . $item->ID;
    $classes[] = 'has-children';
    $class_names = join(
        ' ',
        apply_filters(
            'nav_menu_css_class',
            array_filter( $classes ),
            $item,
            $args
        )
    );
    $class_names = $class_names
        ? ' class="' . esc_attr( $class_names ) . '"'
        : '';

    return "<li$id>$args->before<a class="menu-item has-children">$title</a>$args->after";
}

Leave a Comment