• As the topic title says, I’d like to add specific classes to the <li> and <a> tags with my navigation. I know I can style the elements using .menu-name li a {} in my CSS but I’m really trying to match my WordPress markup as closely as I can to my static HTML templates and match my BEM naming convention. As this is my first venture into creating a custom theme I’m hoping this will teach me (the hard way) a bit more about how it works, rather than taking the easy way out. That said, I am a novice.

    My current wp_nav_menu function looks like this:

    
    function html5blank_nav()
    {
    	wp_nav_menu(
    	array(
    		'theme_location'  => 'header-menu',
    		'menu'            => '',
    		'container'       => false,
    		'container_class' => 'menu-{menu slug}-container',
    		'container_id'    => '',
    		'menu_class'      => 'menu',
    		'menu_id'         => '',
    		'echo'            => true,
    		'fallback_cb'     => 'wp_page_menu',
    		'before'          => '',
    		'after'           => '',
    		'link_before'     => '',
    		'link_after'      => '',
    		'items_wrap'      => '<ul class="site-nav__list">%3$s</ul>',
    		'depth'           => 0,
    		'walker'          => ''
    		)
    	);
    }
    

    And for reference, my static nav markup looks like this:

    
    <nav class="site-nav">
    	<a href="#" class="page-head__logo">
    		<img src="img/interface/logo.png" srcset="img/interface/[email protected] 2x" alt="Wireforce logo" />
    	</a>
    	<a href="#" id="site-nav__toggle">Menu</a>
    	<ul class="site-nav__list">
    		<li class="site-nav__item"><a href="#" class="site-nav__link">Services</a></li>
    		<li class="site-nav__item selected"><a href="#" class="site-nav__link">Security</a></li>
    		<li class="site-nav__item"><a href="#" class="site-nav__link">Blog</a></li>
    		<li class="site-nav__item"><a href="#" class="site-nav__link">About</a></li>
    		<li class="site-nav__item"><a href="#" class="site-nav__link">Contact</a></li>
    	</ul>
    </nav>
    

    I know this may be a bit advanced for me at this stage but I suppose I’m only going to learn one way and the sooner I get into it the better. So I’d really appreciate some help/being pointed in the right direction on this as I imagine it’s something I’ll use again and again.

    Thanks in advance!

Viewing 9 replies - 1 through 9 (of 9 total)
  • Hi, what you are looking for is a custom nav Walker. By setting up a Walker class you can customize the markup of your nav.

    https://codex.www.ads-software.com/Class_Reference/Walker
    Good example of a custom Walker using Twitter Bootstrap: https://github.com/twittem/wp-bootstrap-navwalker

    Moderator keesiemeijer

    (@keesiemeijer)

    Or filter the nav menu items in your (child) theme’s functions.php file.

    This should help you on your way.

    
    // Remove id from nav menu items
    add_filter( 'nav_menu_item_id', '__return_empty_string' );
    
    // Add 'site-nav__item' class
    add_filter( 'nav_menu_css_class', 'My_Theme_nav_menu_item_class', 10, 4 );
    
    function My_Theme_nav_menu_item_class( $classes , $item, $args, $depth ) {
    
    	$new_classes = array( 'site-nav__item' );
    	if ( in_array( 'current-menu-item', $classes ) ) {
    		$new_classes[] = 'selected';
    	}
    
    	return $new_classes;
    }
    
    // Add 'site-nav__link' class
    add_filter( 'nav_menu_link_attributes', 'My_Theme_nav_menu_link_atts', 10, 4 );
    
    function My_Theme_nav_menu_link_atts( $atts, $item, $args, $depth ) {
    	$new_atts = array( 'class' => 'site-nav__link' );
    	if ( isset( $atts['href'] ) ) {
    		$new_atts['href'] = $atts['href'];
    	}
    
    	return $new_atts;
    }
    
    function html5blank_nav() {
    
    	$html = '<nav class="site-nav">';
    	$html .= '<a href="#" class="page-head__logo">';
    	$html .= '<img src="img/interface/logo.png" srcset="img/interface/[email protected] 2x" alt="Wireforce logo" />';
    	$html .= '</a>';
    	$html .= '<a href="#" id="site-nav__toggle">Menu</a>';
    
    	$menu = wp_nav_menu(
    		array(
    			'theme_location'  => 'header-menu',
    			'menu'            => '',
    			'container'       => '',
    			'container_class' => '',
    			'container_id'    => '',
    			'menu_class'      => '',
    			'menu_id'         => '',
    			'echo'            => false,
    			'fallback_cb'     => 'wp_page_menu',
    			'before'          => '',
    			'after'           => '',
    			'link_before'     => '',
    			'link_after'      => '',
    			'items_wrap'      => '<ul class="site-nav__list">%3$s</ul>',
    			'depth'           => 0,
    			'walker'          => ''
    		)
    	);
    
    	if ( $menu ) {
    		echo $html . $menu .  '</nav>';
    	}
    }
    

    btw:
    consider creating a child theme instead of editing your theme directly – if you upgrade the theme all your modifications will be lost.

    • This reply was modified 8 years, 4 months ago by keesiemeijer.
    • This reply was modified 8 years, 4 months ago by keesiemeijer.
    Thread Starter moymadethis

    (@moymadethis)

    Thanks that’s great! I have to save it looks a little advanced for this n00b, I don’t know any PHP really but I’ll give it a shot!

    Do I just put this code after the initial function html5blank_nav() call I make?

    I suppose I good start might be going through the code you’ve made and referencing some of the terms in Google to see what they do so I can familiarise myself with them? You’re comments should definitely set me on the right path ??

    What is the benefit in filtering the items rather than using a walker, is it simpler/less code?

    I think once I get it working I can look at child themes. I can trying to ‘make my own’, using the HTML5 blank theme. I wanted to start with a minimal theme so I didn’t have loads of stuff I didn’t need. Is there a better option or will that do me for now? Like I say, I have next to no knowledge of this stuff. Strictly a designer/html/css boy ??

    Thanks again for your replies and sorry for the delay in responding!

    Moderator keesiemeijer

    (@keesiemeijer)

    Remove the original function html5blank_nav() function and add the code in it’s place.

    What is the benefit in filtering the items rather than using a walker, is it simpler/less code?

    With the walker you have even more control over the output, but for simple things like changing the id and classnames you can use the filters (that are used inside the walker).

    You can also learn a lot by checking out the default theme’s (Twenty Sixteen, etc.) that are shipped with WordPress. Another helpfull resource is the theme handbook

    Thread Starter moymadethis

    (@moymadethis)

    Ok great, if I wanted to include the logo/anchor in the markup would I just remove this bit of the script:

    
    $html = '<nav class="site-nav">';
    $html .= '<a href="#" class="page-head__logo">';
    $html .= '<img src="img/interface/logo.png" srcset="img/interface/[email protected] 2x" alt="Wireforce logo" />';
    $html .= '</a>';
    $html .= '<a href="#" id="site-nav__toggle">Menu</a>';
    

    I thought if I did that I’d need to remove the following code as well as the </nav> tag is in the markup as well so there’s not 2 closing tags …but then it broke (the nav disappeared) ??

    
    if ( $menu ) {
    	echo $html . $menu .  '</nav>';
    }
    

    I’m happy with that code being there, best to keep it all together I guess! I’m just trying to see what does what so I understand it better. Thanks again, this is great, I dropped it in and it worked right away!

    Moderator keesiemeijer

    (@keesiemeijer)

    If you want to remove that bit try this

    
    function html5blank_nav() {
    
    	$menu = wp_nav_menu(
    		array(
    			'theme_location'  => 'header-menu',
    			'menu'            => '',
    			'container'       => '',
    			'container_class' => '',
    			'container_id'    => '',
    			'menu_class'      => '',
    			'menu_id'         => '',
    			'echo'            => false,
    			'fallback_cb'     => 'wp_page_menu',
    			'before'          => '',
    			'after'           => '',
    			'link_before'     => '',
    			'link_after'      => '',
    			'items_wrap'      => '<ul class="site-nav__list">%3$s</ul>',
    			'depth'           => 0,
    			'walker'          => ''
    		)
    	);
    
    	if ( $menu ) {
    		echo '<nav class="site-nav">' . $menu .  '</nav>';
    	}
    }

    I’m not sure that is what you wanted though.

    Thread Starter moymadethis

    (@moymadethis)

    Ah I actually just meant so site-nav wasn’t included in the script as it was already in the markup:

    
    <nav class="site-nav">
    
    	<!-- logo -->
    	<a href="<?php echo home_url(); ?>" class="page-head__logo">
    		<img src="<?php echo get_template_directory_uri(); ?>/img/interface/logo.png" srcset="img/interface/[email protected] 2x" alt="Wireforce" />
    	</a>
    	<!-- /logo -->
    	
    	<?php html5blank_nav(); ?>
    	
    </nav>
    

    I saw you opened it with $html = '<nav class="site-nav">'; and assumed if ( $menu ) { echo $html . $menu . '</nav>'; } was it closing. So I was surprised when I removed both that it broke?

    Also with the add_filter can you add it so it only targets the main (header-menu) navigation? I have a couple of additional menus in the footer and notice the classes are added to them as well.

    Moderator keesiemeijer

    (@keesiemeijer)

    Try it with this (remove previous code first):

    
    // Remove id from nav menu items
    add_filter( 'nav_menu_item_id', 'My_Theme_nav_menu_item_id', 10, 4 );
    
    function My_Theme_nav_menu_item_id( $id, $item, $args, $depth ) {
    	if ( 'header-menu' !== $args->theme_location ) {
    		return $id;
    	}
    
    	return '';
    }
    
    // Add 'site-nav__item' class
    add_filter( 'nav_menu_css_class', 'My_Theme_nav_menu_item_class', 10, 4 );
    
    function My_Theme_nav_menu_item_class( $classes , $item, $args, $depth ) {
    
    	if ( 'header-menu' !== $args->theme_location ) {
    		return $classes;
    	}
    
    	$new_classes = array( 'site-nav__item' );
    	if ( in_array( 'current-menu-item', $classes ) ) {
    		$new_classes[] = 'selected';
    	}
    
    	return $new_classes;
    }
    
    // Add 'site-nav__link' class
    add_filter( 'nav_menu_link_attributes', 'My_Theme_nav_menu_link_atts', 10, 4 );
    
    function My_Theme_nav_menu_link_atts( $atts, $item, $args, $depth ) {
    
    	if ( 'header-menu' !== $args->theme_location ) {
    		return $atts;
    	}
    
    	$new_atts = array( 'class' => 'site-nav__link' );
    	if ( isset( $atts['href'] ) ) {
    		$new_atts['href'] = $atts['href'];
    	}
    
    	return $new_atts;
    }
    
    function html5blank_nav() {
    
    	$menu = wp_nav_menu(
    		array(
    			'theme_location'  => 'header-menu',
    			'menu'            => '',
    			'container'       => '',
    			'container_class' => '',
    			'container_id'    => '',
    			'menu_class'      => '',
    			'menu_id'         => '',
    			'echo'            => false,
    			'fallback_cb'     => 'wp_page_menu',
    			'before'          => '',
    			'after'           => '',
    			'link_before'     => '',
    			'link_after'      => '',
    			'items_wrap'      => '<ul class="site-nav__list">%3$s</ul>',
    			'depth'           => 0,
    			'walker'          => ''
    		)
    	);
    
    	if ( $menu ) {
    		echo $menu;
    	}
    }
    Thread Starter moymadethis

    (@moymadethis)

    I probably shouldn’t bounce this back to the top but I just wanted to say thanks for this! Sorry I went on holiday for a few weeks once you posted this, then Christmas happened so the project was kinda put on hold and I’m just picking it up again now!

    So am I right in thinking the reason the code has changed is because we’re no longer ‘injecting’ the site-nav wrapper along with logo/toggle markup? There for none of it needs to be referenced.

    I’m guessing that why it broke when I removed it from the previous code. Something must’ve still been referencing it/looking for a class/item I’d removed?

Viewing 9 replies - 1 through 9 (of 9 total)
  • The topic ‘Add classes to wp_nav_menu list-items and anchors’ is closed to new replies.