Ways to give a wp_list_pages menu link specific class names?

The question:

I’m looking to style my wp_list_pages with a span class. I’d like to give it a unique class so that I can give each a specific icon.

'link_before' => '<span class="'.[unique-identifier-here].'"></span>',

I was thinking of using the page slug but the menu I use also has child pages involved so it would have to pull the target links page slug not the $post->post_name one.

I’m not sure if it’s a good option which just needs better logic or if there’s a better solution altogether.

Here’s my full menu code:

global $post;

if ( is_page() && wap8_has_children( $post->ID ) == '1' || wap8_has_siblings() == '1' ) {

 if ( $post->post_parent ) {

      $args = array(
           'sort_column' => 'menu_order',
           'title_li'    => '',
           'child_of'    => $post->post_parent,
           'echo'        => 1,
       'link_before' => '<span class="'.$post->post_name.'"></span>',
      );

 } else {

      $args = array(
           'sort_column' => 'menu_order',
           'title_li'    => '',
           'child_of'    => $post->ID,
           'echo'        => 1,
    'link_before' => '<span class="'.$post->post_name.'"></span>',
      );

 }

 echo "<ul>n";
     wp_list_pages( $args );
 echo "</ul>n";

}

-edit- What I’d like the output to look like.

<ul>
<li>
<a>
<span class="unique_class1"></span>
</a>
<li>
<li>
<a>
<span class="unique_class2"></span>
</a>
<li>
<li>
<a>
<span class="unique_class3"></span>
</a>
<li>
</ul>

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

Okay well in that case the way you’ve suggested in your question will not work as intended. It will put the same class on each <span>, that class would also be the post_name of the current page you are on.

You do have two options that you can use, one being much easier than the other:

Method One

Each <li> item generated by wp_list_pages() is given a unique class. The class follows a pattern of page-item-$ID where $ID is the ID of the page currently being processed by wp_list_pages().

If you change the link_before parameter to be just <span></span> then you can target the spans I’m your css like so .page-item-$ID span. Though I imagine this would get tedious if you had loads of pages.

Method Two

You could write a custom walker for the wp_list_pages() function and pass it into the walker parameter of the function. This would give you considerably more control over the naming of classes. Attached is a custom walker that you can use, with an explanation beneath.

Add to functions.php:

class WPSE_130877_Custom_Walker extends Walker_Page {

    function start_el( &$output, $page, $depth, $args, $current_page = 0 ) {
        if ( $depth )
            $indent = str_repeat("t", $depth);
        else
            $indent = '';
            extract($args, EXTR_SKIP);
            $css_class = array('page_item', 'page-item-'.$page->ID);
        if ( !empty($current_page) ) {
            $_current_page = get_post( $current_page );
            if ( in_array( $page->ID, $_current_page->ancestors ) )
                $css_class[] = 'current_page_ancestor';
            if ( $page->ID == $current_page )
                $css_class[] = 'current_page_item';
            elseif ( $_current_page && $page->ID == $_current_page->post_parent )
                $css_class[] = 'current_page_parent';
        }
        elseif ( $page->ID == get_option('page_for_posts') ) {
            $css_class[] = 'current_page_parent';
        }

        $css_class = implode( ' ', apply_filters( 'page_css_class', $css_class, $page, $depth, $args, $current_page ) );
        $icon_class = get_post_meta($page->ID, 'icon_class', true); //Retrieve stored icon class from post meta

        $output .= $indent . '<li class="' . $css_class . '">';
            $output .= '<a href="' . get_permalink($page->ID) . '" rel="nofollow noreferrer noopener">' . $link_before;

                if($icon_class){ //Test if $icon_class exists
                    $output .= '<span class="' . $icon_class . '"></span>'; //If it exists output a span with the $icon_class attached to it
                }

                $output .= apply_filters( 'the_title', $page->post_title, $page->ID );
            $output .= $link_after . '</a>';

        if ( !empty($show_date) ) {
            if ( 'modified' == $show_date )
                $time = $page->post_modified;
                else
                $time = $page->post_date;
                $output .= " " . mysql2date($date_format, $time);
        }
    }
}

Call your wp_list_pages() like below:

wp_list_pages(array(
    'sort_column' => 'menu_order',
    'title_li'    => '',
    'echo'        => 1,
    'walker' => new WPSE_130877_Custom_Walker()
)); 

Take note that we have removed the link_before parameter and added in a walker parameter, which takes an instance of our custom walker class. Notice it has the same name as the snippet of code i’ve asked you to put into your functions.php.

The code for the custom walker is essentially default WordPress code but we are overriding it with the new walker. We are only making a small edit in the middle of the default WordPress function that handles the markup for the beginning of each page-item. All of the code I added has comments next to it.

So as build the markup for each page-item we check to see if it has a meta key of icon_class, if it does then we add in a <span> tag with our icon_class value before the page-item link. The conditional is there to prevent printing out redundant elements, however the code could easily be edited to add a default icon in.

Essentially all you need to do is build a meta box for your pages and then replace icon_class in the above code with whatever you decide to call the key that holds the class name. It should be noted that the code expects icon_class to return a string.

Method 2

There is no need for extra markup. Your menu items have a unique CSS class page-item-{ID}, which you could use.

First, give your list an ID, like <ul id="my-list"> .

Then, target the items, for instance, like so:

#my-list .page-item-123 a {
    padding-left: 20px;
    background-image: url(...);
    ...
}

or

#my-list .page-item-123 a:before {
    content: '123';
    font-family: SomeIconFont;
    ...
}

Method 3

To avoid error on debug, The declaration of this function has to have the parameters defined like this:

function start_el(&$output, $page, $depth = 0, $args = array(), $current_page = 0)


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment