Break apart wp_list_pages in order to customise it

I’m trying to ‘break apart’, (think explode is the correct terminology), wp_list_pages in order to add some definition list code into it, (dl, dt, dd).

Here is the html code that I’m trying to output:

Read More
<div id="nav">             
            <ul id="drop-nav">                 
                <li><a href="#">About Us</a>
                <div class="subnav">
                        <div class="subnavTop">
                            <ul class="subnavContent">
                                <li>
                                    <dl>                                             
                                        <dt><a href="#">Help Desk</a></dt>
                                        <dd><a href="#">Email your question</a></dd>
                                        <dd><a href="#">Text a question</a></dd>            
                                    </dl>                                      
                                    <dl>
                                        <dt><a href="#">Life stories</a></dt>                                                 
                                        <dt><a href="#">Statistics</a></dt>
                                        <dd><a href="#">World Statistics</a></dd>
                                        <dd><a href="#">UK Statistics</a></dd>
                                        <dd><a href="#">South West Statistics</a></dd>
                                        <dd><a href="#">Research projects</a></dd>                    
                                    </dl>                                        
                                </li>                   
                            </ul>
                        </div>                           
                    </div>
                </li> 
                <li><a href="#">Talk to us</a></li>
            </ul>
            <br style="clear: left" />
    </div>

Here is my current outputted html code:

<div id="nav">  
            <ul id="drop-nav">          
                <li><a href="#">About Us</a>
                <div class="subnav">
                        <div class="subnavTop">
                            <ul class="subnavContent">
                                <li class="page_item page-item-15"><a href="#">Child of About</a>
                                    <ul class='children'>
                                        <li class="page_item page-item-28"><a href="#">Grandchild of About</a></li>
                                    </ul>
                                </li>

                            </ul>
                        </div>
                    </div>
                </li>
                <li><a href="#">Talk to us</a></li>
            </ul>
    <br style="clear: left" />
    </div>

Which is generated using the following code from within my header.php, (can’t remember which site I found this on):

<div id="nav">  
    <ul id="drop-nav">          
    <?php
    // Query the database for all top-level page IDs, and store them in the $menuPages array under the [ID] sub-array
    $menuPages = $wpdb->get_results("SELECT ID FROM $wpdb->posts WHERE post_parent=0 AND post_type='page' AND post_status='publish' ORDER BY menu_order ASC");

    // For each element in the $menuPages array, get the value from the [ID]-subarray to create the top-level menu and it's children
    foreach($menuPages as $menuItem){
        $menuItemTitle = get_the_title($menuItem->ID);          
        $menuItemClass = strtolower(trim($menuItemTitle));                  
        $menuItemClass = preg_replace('/[^a-z0-9-]/', '-', $menuItemClass);
        $menuItemClass = preg_replace('/-+/', "-", $menuItemClass);             
        $menuItemPermalink = get_permalink($menuItem->ID);
            if (isset($menuItemTitle) && $menuItemTitle != 'Home'){                 
                _e('<li class="'.$menuItemClass.'">'.PHP_EOL);
                _e('<a href="'.$menuItemPermalink.'">'.$menuItemTitle.'</a>'.PHP_EOL);

            // Run wp_list_pages to fetch any children, and put the HTML <LI> list results into $menuItemChildren as a string
                $menuItemChildren = wp_list_pages('title_li=&echo=0&depth=1&sort_column=menu_order&child_of='.$menuItem->ID);

            // If results were returned, $menuItemChildren is now a string with HTML in it, so create a drop-down and echo out the HTML
                if($menuItemChildren){                      
                    _e('<div class="subnav">'.PHP_EOL);
                    _e('<div class="subnavTop">'.PHP_EOL);
                    _e('<ul class="subnavContent">'.PHP_EOL);                                   
                    echo $menuItemChildren . PHP_EOL;
                    _e('</ul>'.PHP_EOL);
                    _e('</div>'.PHP_EOL);
                    _e('</div>'.PHP_EOL);                       
                }
                _e('</li>'.PHP_EOL);            
            }
        }
    ?>
    </ul>
    <br style="clear: left" />
    </div>

To be honest, this is confusing the life out of me. I can see that it should work, just not sure how to make it work. I somehow need to change each child page/item into a , the grandchildren into ‘s, all wrapped up nicely in ‘s.

Hope this makes sense. Any ideas/help greatly appreciated, S.

(The code is being used to dynamically generate a ‘mega-menu’)

/*Updated Walker Class code below 08-03-11, as originally provided by wyrfel – many thanks */

class My_Walker_Page extends Walker {
var $tree_type = 'page';

var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');

function start_lvl(&$output, $depth) {
    $indent = str_repeat("t", $depth);
    switch ($depth) {
        case 0:
            $output .= "<div class="subnav">n";
            $output .= "<div class="subnavTop">n";
            $output .= $indent."<ul class='subnavContent'>n";
            $output .= "<li>n<dl>n";
            break;
        case 1:                
            break;
        default:
            break;
    }
}

function end_lvl(&$output, $depth) {
    $indent = str_repeat("t", $depth);
    switch ($depth) {
        case 0:
            $output .= "n</dl>n</li>n";
            $output .= "</ul>n";
            $output .= "</div>n";
            $output .= "</div>n";
            break;
        case 1:                
            break;
        default:
            break;
    }
}

function start_el(&$output, $page, $depth, $args, $current_page) {
    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_page( $current_page );
        _get_post_ancestors($_current_page);
        if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_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));

    $page_link = '<a href="' . get_permalink($page->ID) . '" title="' . esc_attr( wp_strip_all_tags( apply_filters( 'the_title', $page->post_title, $page->ID ) ) ) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';
    $top_class = strtolower(trim($page->post_title));                   
    $top_class = preg_replace('/[^a-z0-9-]/', '-', $top_class);
    $top_class = preg_replace('/-+/', "-", $top_class);         

    switch ($depth) {
        case 0:
            $output .= $indent . '<li class="' . $top_class . '">'.$page_link . PHP_EOL;
            break;
        case 1:
            $output .= $indent . '<dt>'.$page_link.'</dt>' . PHP_EOL;
            break;
        default:
            $output .= $indent . '<dd>'.$page_link.'</dd>' . PHP_EOL;
            break;
    }

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

function end_el(&$output, $page, $depth) {
    switch ($depth) {
        case 0:
            $output .= "</li>n";
            break;
        case 1:
            break;
        default:
            break;
    }
}
}

Related posts

Leave a Reply

2 comments

  1. This is one of those question where solid answer is not an easy one to follow. This function is powered by Walker_Page class and you can replace it with your own walker (extended from Walker_Page or just Walker) by passing its name in walker argument to wp_list_pages().

    In a nutshell it is very highly flexible way to do it, but not very convenient and easy to grasp, especially when not messing with walkers regularly.

  2. I cannot test this right now, but i tried writing your walker for you. It doesn’t exactly do what you want as the top-levels class/id attributes are hard to modify, but everything below that should be fine (or so i hope).

    class My_Walker_Page extends Walker {
        var $tree_type = 'page';
    
        var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');
    
        function start_lvl(&$output, $depth) {
            $indent = str_repeat("t", $depth);
            switch ($depth) {
                case 0:
                    $output .= $indent."<ul id="drop_nav">n";
                    break;
                case 1:
                    $output .= "<div class="subnav">n";
                    $output .= "<div class="subnavTop">n";
                    $output .= $indent."<ul class='subnavContent'>n";
                    $output .= "<li>n<dl>n";
                    break;
                default:
                    break;
            }
        }
    
        function end_lvl(&$output, $depth) {
            $indent = str_repeat("t", $depth);
            switch ($depth) {
                case 0:
                    $output .= "n".$indent."</ul>n";
                    break;
                case 1:
                    $output .= "n</dl>n</li>n";
                    $output .= "</ul>n";
                    $output .= "</div>n";
                    $output .= "</div>n";
                    break;
                default:
                    break;
            }
        }
    
        function start_el(&$output, $page, $depth, $args, $current_page) {
            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_page( $current_page );
                _get_post_ancestors($_current_page);
                if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_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));
    
            $page_link = '<a href="' . get_permalink($page->ID) . '" title="' . esc_attr( wp_strip_all_tags( apply_filters( 'the_title', $page->post_title, $page->ID ) ) ) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';
            switch ($depth) {
                case 0:
                    $output .= $indent . '<li class="' . $css_class . '">'.$page_link;
                    break;
                case 1:
                    $output .= $indent . '<dt class="' . $css_class . '">'.$page_link;
                    break;
                default:
                    $output .= $indent . '<dd class="' . $css_class . '">'.$page_link;
                    break;
            }
    
            if ( !empty($show_date) ) {
                if ( 'modified' == $show_date )
                    $time = $page->post_modified;
                else
                    $time = $page->post_date;
    
                $output .= " " . mysql2date($date_format, $time);
            }
        }
    
        function end_el(&$output, $page, $depth) {
            switch ($depth) {
                case 0:
                    $output .= "</li>n";
                    break;
                case 1:
                    $output .= "</dt>n";
                    break;
                default:
                    $output .= "</dd>n";
                    break;
            }
        }
    }
    

    You should be able to just do:

    $args = array(
        'title_li' => '',
        'sort_column' => 'menu_order',
        'walker' => new My_Walker_Page(),
    );
    wp_list_pages($args);