0

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>
3
  • Are you looking to give each <li> element a unique class, or every <li> the same class that is unique to the page you are on?
    – user10106
    Commented Jan 23, 2014 at 6:59
  • @MichaelJames I just edited the post. It's actually the <span> class, but I want each one to have their own unique id. I was thinking that using something like "if current page, post slug, else link target page slug. But from looking around I can't find how to do that.
    – Derek
    Commented Jan 23, 2014 at 7:08
  • Actually post slugs might not be ideal. I'm not sure what would be an ideal way to make classes for these menu items.
    – Derek
    Commented Jan 23, 2014 at 7:14

3 Answers 3

2

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) . '">' . $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.

8
  • Yeah I don't believe Method One would be a good option. This would currently be applied to 30+ pages, and growing. Would Method Two be a bit more dynamic?
    – Derek
    Commented Jan 23, 2014 at 7:37
  • Depends on what you mean by dynamic? You could at a high level, have a custom field (possibly by using a select) for each page that stores the class name of the icon you want to use, then with a custom walker you could print out the appropriate class name for each page item. More involved, but for a large amount of pages will trim down on the amount of unique css styles you have to add.
    – user10106
    Commented Jan 23, 2014 at 7:46
  • That might be the best option. What exactly is a select?
    – Derek
    Commented Jan 23, 2014 at 8:40
  • I meant a select element, a drop down if you would. Are you okay coding up the required meta box? That way i can look at getting you a walker sample for this.
    – user10106
    Commented Jan 23, 2014 at 9:00
  • I believe I can do that. I've done a bit with custom options/settings so I should be able to get it done. The walker class I'm completely new to though.
    – Derek
    Commented Jan 23, 2014 at 16:09
0

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;
    ...
}
5
  • This doesn't seem like it would work, because each page has different page-item-#'s for the menus. The menu is being used in a page template.
    – Derek
    Commented Jan 23, 2014 at 7:34
  • The CSS class contains the page ID (which is unique), which means that page X has the same CSS class no matter what page (template) it's listed on. Could you elaborate on what is not as it should be?
    – tfrommen
    Commented Jan 23, 2014 at 7:53
  • On page "A" the class is .page-item-838. On page "B" that same menu item that I want the same icon on, is now .page-item-847. Unless I'm not understanding you, I don't want to duplicate CSS 4-5 times per page I use the menu on.
    – Derek
    Commented Jan 23, 2014 at 8:39
  • Unless there is some filtering going on, those two menu items do not lead to the same page. Do you get to the exact same page when following either of the menu items? You can check this by means of the .page-id-{ID} body class. Or, I guess, I'm just not understanding you...
    – tfrommen
    Commented Jan 23, 2014 at 9:18
  • No, the menu actually pulls the parent page, and then lists all of the child page (basically the sub-navigation for a large piece of content broken into pieces). So each page this menu is used on, has links to child pages that are similar in name, but different pages.
    – Derek
    Commented Jan 23, 2014 at 16:05
0

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)

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.