Отобразить часть / ветвь дерева меню, используя wp_nav_menu ()

109

У меня есть меню, определенное в WP Admin, которое выглядит так:

альтернативный текст

Я хочу иметь возможность отображать все дочерние ссылки на боковой панели, когда я нахожусь на родительской странице. Например, если пользователь находится на моей странице «О нас», я хочу, чтобы на боковой панели отображался список из 4 ссылок, выделенных зеленым цветом.

Я посмотрел документацию для wp_nav_menu (), и в ней нет встроенного способа указать конкретный узел данного меню, который будет использоваться в качестве отправной точки при создании ссылок.

Я создал решение для аналогичной ситуации, которое основывалось на отношениях, созданных родителем страницы, но я ищу решение, которое использует систему меню специально. Любая помощь будет оценена.

jessegavin
источник
2
Таким образом, вы хотите сохранить все меню как пользовательское меню, но создать собственный ходок, который отображает его, расширяя только активное поддерево? Как этот код , но расширение wp_nav_menu вместо wp_list_pages? Недавно я сделал что-то подобное и мог бы опубликовать код, если это то, что вы ищете ...
goldenapples 12.10.10
1
@ золотые яблоки, это именно то, что я после. Если вы не против опубликовать свой код в качестве ответа, я был бы очень признателен.
jessegavin
1
Интересно, что такой очевидный полезный функционал еще не построен. Это в целом очень полезно для любого сайта, который использует "CMS".
hakre
Я пытаюсь решить вышеуказанную проблему или что-то подобное. В качестве альтернативы я предложил CSS-решение здесь: stackoverflow.com/q/7640837/518169
hyperknot,

Ответы:

75

Это все еще было в моей голове, поэтому я пересмотрел его и собрал это решение, которое не слишком зависит от контекста:

add_filter( 'wp_nav_menu_objects', 'submenu_limit', 10, 2 );

function submenu_limit( $items, $args ) {

    if ( empty( $args->submenu ) ) {
        return $items;
    }

    $ids       = wp_filter_object_list( $items, array( 'title' => $args->submenu ), 'and', 'ID' );
    $parent_id = array_pop( $ids );
    $children  = submenu_get_children_ids( $parent_id, $items );

    foreach ( $items as $key => $item ) {

        if ( ! in_array( $item->ID, $children ) ) {
            unset( $items[$key] );
        }
    }

    return $items;
}

function submenu_get_children_ids( $id, $items ) {

    $ids = wp_filter_object_list( $items, array( 'menu_item_parent' => $id ), 'and', 'ID' );

    foreach ( $ids as $id ) {

        $ids = array_merge( $ids, submenu_get_children_ids( $id, $items ) );
    }

    return $ids;
}

использование

$args = array(
    'theme_location' => 'slug-of-the-menu', // the one used on register_nav_menus
    'submenu' => 'About Us', // could be used __() for translations
);

wp_nav_menu( $args );
Rarst
источник
Прекрасная техника! Могу ли я спросить что-то, возможно, касающееся этого: Как бы вы отобразили содержимое этих страниц подменю в шаблоне?
daniel.tosaba
2
@ daniel.tosaba вам нужно будет создать подкласс или использовать фильтры в Walker_Nav_Menuклассе. Как и все меню слишком много для комментариев - задать новый вопрос об этом?
Первый
3
Такой фантастический ответ. Огромное спасибо. Это действительно должна быть опция по умолчанию в WordPress.
Дотти,
3
Действительно аккуратно. Если кому-то интересно, сделать то же самое, но по идентификатору страницы, измените wp_filter_object_listстроку наwp_filter_object_list( $items, array( 'object_id' => $args->submenu ), 'and', 'ID' );
Бен
14

@goldenapples: Ваш класс Уокера не работает. Но идея действительно хорошая. Я создал ходок на основе вашей идеи:

class Selective_Walker extends Walker_Nav_Menu
{
    function walk( $elements, $max_depth) {

        $args = array_slice(func_get_args(), 2);
        $output = '';

        if ($max_depth < -1) //invalid parameter
            return $output;

        if (empty($elements)) //nothing to walk
            return $output;

        $id_field = $this->db_fields['id'];
        $parent_field = $this->db_fields['parent'];

        // flat display
        if ( -1 == $max_depth ) {
            $empty_array = array();
            foreach ( $elements as $e )
                $this->display_element( $e, $empty_array, 1, 0, $args, $output );
            return $output;
        }

        /*
         * need to display in hierarchical order
         * separate elements into two buckets: top level and children elements
         * children_elements is two dimensional array, eg.
         * children_elements[10][] contains all sub-elements whose parent is 10.
         */
        $top_level_elements = array();
        $children_elements  = array();
        foreach ( $elements as $e) {
            if ( 0 == $e->$parent_field )
                $top_level_elements[] = $e;
            else
                $children_elements[ $e->$parent_field ][] = $e;
        }

        /*
         * when none of the elements is top level
         * assume the first one must be root of the sub elements
         */
        if ( empty($top_level_elements) ) {

            $first = array_slice( $elements, 0, 1 );
            $root = $first[0];

            $top_level_elements = array();
            $children_elements  = array();
            foreach ( $elements as $e) {
                if ( $root->$parent_field == $e->$parent_field )
                    $top_level_elements[] = $e;
                else
                    $children_elements[ $e->$parent_field ][] = $e;
            }
        }

        $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );  //added by continent7
        foreach ( $top_level_elements as $e ){  //changed by continent7
            // descend only on current tree
            $descend_test = array_intersect( $current_element_markers, $e->classes );
            if ( !empty( $descend_test ) ) 
                $this->display_element( $e, $children_elements, 2, 0, $args, $output );
        }

        /*
         * if we are displaying all levels, and remaining children_elements is not empty,
         * then we got orphans, which should be displayed regardless
         */
         /* removed by continent7
        if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
            $empty_array = array();
            foreach ( $children_elements as $orphans )
                foreach( $orphans as $op )
                    $this->display_element( $op, $empty_array, 1, 0, $args, $output );
         }
        */
         return $output;
    }
}

Теперь вы можете использовать:

<?php wp_nav_menu( 
   array(
       'theme_location'=>'test', 
       'walker'=>new Selective_Walker() ) 
   ); ?>

Вывод представляет собой список, содержащий текущий корневой элемент и его дочерние элементы (а не их дочерние элементы). Def: Root element: = Элемент меню верхнего уровня, который соответствует текущей странице или является родительским для текущей страницы или родительским для родительского элемента ...

Это не совсем отвечает на первоначальный вопрос, но почти, так как все еще есть элемент верхнего уровня. Это хорошо для меня, потому что я хочу элемент верхнего уровня в качестве заголовка боковой панели. Если вы хотите избавиться от этого, вам, возможно, придется переопределить display_element или использовать HTML-парсер.

davidn
источник
12

Привет @jessegavin :

Меню Nav хранятся в комбинации пользовательских типов записей и пользовательских таксономий. Каждое меню сохраняется как термин (например, «О меню» , найденный в wp_terms) пользовательской таксономии (то есть nav_menu, найденный в wp_term_taxonomy.)

Каждый пункт меню Nav хранится в виде поста post_type=='nav_menu_item'(т. Е. «О фирме» , в wp_postsкотором он находится), а его атрибуты хранятся в виде мета (в wp_postmeta) поста с meta_keyпрефиксом _menu_item_*где _menu_item_menu_item_parent- идентификатор родительского поста пункта меню Nav вашего элемента меню.

Отношение между меню и пунктами меню хранится в том месте, wp_term_relationshipsгде оно object_idотносится к $post->IDпункту меню Nav и $term_relationships->term_taxonomy_idотносится к меню, определенному совместно в wp_term_taxonomyи wp_terms.

Я уверен , что это можно было бы подключить как 'wp_update_nav_menu'и 'wp_update_nav_menu_item'создать фактическое меню в wp_termsи параллельный набор отношений в wp_term_taxonomyи wp_term_relationshipsгде каждом Nav Пункте меню , который имеет суб-Nav пункты меню также становится его собственная Nav меню.

Вы также хотели бы подключить 'wp_get_nav_menus' (который я предложил добавить в WP 3.0 на основе некоторой аналогичной работы, которую я выполнял несколько месяцев назад), чтобы гарантировать, что ваши сгенерированные навигационные меню не будут отображаться для манипуляций пользователем в администраторе, в противном случае они Вы бы очень быстро потеряли синхронизацию, и тогда у вас был бы кошмар с данными.

Звучит как забавный и полезный проект, но это немного больше кода и тестирования, чем я могу себе позволить решить сейчас, отчасти потому, что все, что синхронизирует данные, имеет тенденцию быть PITA, когда дело доходит до устранения всех ошибок (и потому что платящие клиенты настаивают на том, чтобы я что-то сделал. :) Но, вооружившись приведенной выше информацией, я довольно мотивированный разработчик плагинов для WordPress, если захочу.

Конечно, теперь вы понимаете, что если вы делаете код, вы обязаны опубликовать его здесь, чтобы мы могли извлечь выгоду из вашей щедрости! :-)

MikeSchinkel
источник
Я не уверен, что следую тому, что вы говорите. Я ищу решение только для чтения для отображения «подменю», связанного с текущей страницей, на которой находится пользователь. Мы говорим об одном и том же? - Я ценю ваше более глубокое объяснение схемы базы данных, хотя.
Джессегавин
@jessegavin - Да, если вы хотите позвонить, wp_nav_menu()вам нужно будет клонировать меню, потому что wp_nav_menu()оно тесно связано со структурой меню . Другой вариант - скопировать wp_nav_menu()код и внести изменения, необходимые для отображения в качестве подменю.
MikeSchinkel
10

Это расширение Walker, которое должно делать то, что вы ищете:

class Selective_Walker extends Walker_Nav_Menu
{

    function walk( $elements, $max_depth) {

        $args = array_slice(func_get_args(), 2);
        $output = '';

        if ($max_depth < -1) //invalid parameter
            return $output;

        if (empty($elements)) //nothing to walk
            return $output;

        $id_field = $this->db_fields['id'];
        $parent_field = $this->db_fields['parent'];

        // flat display
        if ( -1 == $max_depth ) {
            $empty_array = array();
            foreach ( $elements as $e )
                $this->display_element( $e, $empty_array, 1, 0, $args, $output );
            return $output;
        }

        /*
         * need to display in hierarchical order
         * separate elements into two buckets: top level and children elements
         * children_elements is two dimensional array, eg.
         * children_elements[10][] contains all sub-elements whose parent is 10.
         */
        $top_level_elements = array();
        $children_elements  = array();
        foreach ( $elements as $e) {
            if ( 0 == $e->$parent_field )
                $top_level_elements[] = $e;
            else
                $children_elements[ $e->$parent_field ][] = $e;
        }

        /*
         * when none of the elements is top level
         * assume the first one must be root of the sub elements
         */
        if ( empty($top_level_elements) ) {

            $first = array_slice( $elements, 0, 1 );
            $root = $first[0];

            $top_level_elements = array();
            $children_elements  = array();
            foreach ( $elements as $e) {
                if ( $root->$parent_field == $e->$parent_field )
                    $top_level_elements[] = $e;
                else
                    $children_elements[ $e->$parent_field ][] = $e;
            }
        }

        $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );

        foreach ( $top_level_elements as $e ) {

            // descend only on current tree
            $descend_test = array_intersect( $current_element_markers, $e->classes );
            if ( empty( $descend_test ) )  unset ( $children_elements );

            $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
        }

        /*
         * if we are displaying all levels, and remaining children_elements is not empty,
         * then we got orphans, which should be displayed regardless
         */
        if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
            $empty_array = array();
            foreach ( $children_elements as $orphans )
                foreach( $orphans as $op )
                    $this->display_element( $op, $empty_array, 1, 0, $args, $output );
         }

         return $output;
    }

}

Основываясь на коде mfields, на который я ссылался в своем комментарии ранее. Все, что он делает, - это проверяет при ходьбе меню, чтобы увидеть, является ли текущий элемент (1) текущим элементом меню или (2) предком текущего элемента меню, и расширяет поддерево под ним, только если выполняется одно из этих условий , Надеюсь, что это работает для вас.

Чтобы использовать его, просто добавьте аргумент «ходок» при вызове меню, то есть:

<?php wp_nav_menu( 
   array(
       'theme_location'=>'test', 
       'walker'=>new Selective_Walker() ) 
   ); ?>
goldenapples
источник
Ох ... Я просто перечитала ваш вопрос и поняла, что сначала неправильно поняла. Этот ходок покажет все остальные пункты меню верхнего уровня, только не раскрывайте их. Это было не совсем то, что вы хотели сделать. Тем не менее, этот код может быть изменен любым способом. Просто посмотрите на цикл $top_level_elementsи добавьте свой собственный тест перед вызовом $this->display_element.
Золотые яблоки
Можно ли заставить этот класс показывать глубину текущей подстраницы? То есть .. Если у меня есть глубина трех или более уровней, то третий и последующие уровни показываются для текущей (под) страницы? На данный момент он показывает только A> B, а не> C (C является третьим (уровень)
Zolomon
@Zolomon - я не уверен, что понимаю ваш вопрос. Это должно развернуть все дерево в любом пункте меню с помощью классов 'current-menu-item', 'current-menu-parent' или 'current-menu-ancestor'. Когда я проверяю его, он отображает все уровни подстраниц в меню. Что вы хотите сделать?
золотые яблоки
Может быть, вы хотите передать depthпараметр в вызов wp_nav_menu, если ваша тема каким-то образом переопределяет значение по умолчанию 0 (показать все уровни)?
золотые яблоки
8

Обновление: я превратил это в плагин. Скачать здесь .


Мне нужно было решить это самому, и в итоге я написал фильтр по результатам поиска в меню. Это позволяет использовать wp_nav_menuкак обычно, но выбрать подраздел меню в зависимости от заголовка родительского элемента. Добавьте submenuпараметр в меню следующим образом:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => 'About Us',
));

Вы даже можете пройти несколько уровней, вставив косую черту:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => 'About Us/Board of Directors'
));

Или, если вы предпочитаете с массивом:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => array('About Us', 'Board of Directors')
));

Он использует слаг-версию названия, которая должна позволять прощать такие вещи, как заглавные буквы и знаки препинания.

Маркус Даунинг
источник
Можно ли попасть в подменю через идентификатор? Я имею в виду идентификатор страницы или идентификатор сообщения.
Digerkam
split () устарела, замените ее $loc = split( "/", $loc );в плагине на$loc = preg_split( "~/~", $loc );
Floris
Я также предложил бы сделать подменю $ необязательным. Таким образом, вы все равно можете получить все меню, когда это необходимо. Добавьте это в фильтр вверху: `if (! Isset ($ args-> submenu)) {return $ items; } `
Floris
8

Я собрал следующий класс для себя. Он найдет верхний родительский элемент навигации текущей страницы или вы можете назначить ему целевой верхний идентификатор навигации в конструкторе ходунка.

class Walker_SubNav_Menu extends Walker_Nav_Menu {
    var $target_id = false;

    function __construct($target_id = false) {
        $this->target_id = $target_id;
    }

    function walk($items, $depth) {
        $args = array_slice(func_get_args(), 2);
        $args = $args[0];
        $parent_field = $this->db_fields['parent'];
        $target_id = $this->target_id;
        $filtered_items = array();

        // if the parent is not set, set it based on the post
        if (!$target_id) {
            global $post;
            foreach ($items as $item) {
                if ($item->object_id == $post->ID) {
                    $target_id = $item->ID;
                }
            }
        }

        // if there isn't a parent, do a regular menu
        if (!$target_id) return parent::walk($items, $depth, $args);

        // get the top nav item
        $target_id = $this->top_level_id($items, $target_id);

        // only include items under the parent
        foreach ($items as $item) {
            if (!$item->$parent_field) continue;

            $item_id = $this->top_level_id($items, $item->ID);

            if ($item_id == $target_id) {
                $filtered_items[] = $item;
            }
        }

        return parent::walk($filtered_items, $depth, $args);
    }

    // gets the top level ID for an item ID
    function top_level_id($items, $item_id) {
        $parent_field = $this->db_fields['parent'];

        $parents = array();
        foreach ($items as $item) {
            if ($item->$parent_field) {
                $parents[$item->ID] = $item->$parent_field;
            }
        }

        // find the top level item
        while (array_key_exists($item_id, $parents)) {
            $item_id = $parents[$item_id];
        }

        return $item_id;
    }
}

Навигация по телефону:

wp_nav_menu(array(
    'theme_location' => 'main_menu',
    'walker' => new Walker_SubNav_Menu(22), // with ID
));
Matt
источник
4

@ davidn @hakre Привет, у меня ужасное решение без HTML-парсера или переопределения display_element.

 class Selective_Walker extends Walker_Nav_Menu
    {
        function walk( $elements, $max_depth) {

            $args = array_slice(func_get_args(), 2);
            $output = '';

            if ($max_depth < -1) //invalid parameter
                return $output;

            if (empty($elements)) //nothing to walk
                return $output;

            $id_field = $this->db_fields['id'];
            $parent_field = $this->db_fields['parent'];

            // flat display
            if ( -1 == $max_depth ) {
                $empty_array = array();
                foreach ( $elements as $e )
                    $this->display_element( $e, $empty_array, 1, 0, $args, $output );
                return $output;
            }

            /*
             * need to display in hierarchical order
             * separate elements into two buckets: top level and children elements
             * children_elements is two dimensional array, eg.
             * children_elements[10][] contains all sub-elements whose parent is 10.
             */
            $top_level_elements = array();
            $children_elements  = array();
            foreach ( $elements as $e) {
                if ( 0 == $e->$parent_field )
                    $top_level_elements[] = $e;
                else
                    $children_elements[ $e->$parent_field ][] = $e;
            }

            /*
             * when none of the elements is top level
             * assume the first one must be root of the sub elements
             */
            if ( empty($top_level_elements) ) {

                $first = array_slice( $elements, 0, 1 );
                $root = $first[0];

                $top_level_elements = array();
                $children_elements  = array();
                foreach ( $elements as $e) {
                    if ( $root->$parent_field == $e->$parent_field )
                        $top_level_elements[] = $e;
                    else
                        $children_elements[ $e->$parent_field ][] = $e;
                }
            }

            $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );  //added by continent7
            foreach ( $top_level_elements as $e ){  //changed by continent7
                // descend only on current tree
                $descend_test = array_intersect( $current_element_markers, $e->classes );
                if ( !empty( $descend_test ) ) 
                    $this->display_element( $e, $children_elements, 2, 0, $args, $output );
            }

            /*
             * if we are displaying all levels, and remaining children_elements is not empty,
             * then we got orphans, which should be displayed regardless
             */
             /* removed by continent7
            if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
                $empty_array = array();
                foreach ( $children_elements as $orphans )
                    foreach( $orphans as $op )
                        $this->display_element( $op, $empty_array, 1, 0, $args, $output );
             }
            */

/*added by alpguneysel  */
                $pos = strpos($output, '<a');
            $pos2 = strpos($output, 'a>');
            $topper= substr($output, 0, $pos).substr($output, $pos2+2);
            $pos3 = strpos($topper, '>');
            $lasst=substr($topper, $pos3+1);
            $submenu= substr($lasst, 0, -6);

        return $submenu;
        }
    }
Alp Güneysel
источник
Перепробовав их все, решение Alp оказалось для меня единственным. Однако одна проблема с этим. Он показывает только детей первого уровня, но не показывает детей третьего или четвертого уровня. Я пытался несколько дней, чтобы заставить это сделать. Кто-нибудь знает, как изменить свое решение как таковое? PS. Это не позволит мне добавлять комментарии, поэтому нужно сделать это как ответ.
cchiera
3

Вывод меню навигации включает в себя множество классов для текущего элемента, предка текущего элемента и т. Д. В некоторых ситуациях я смог сделать то, что вы хотите сделать, разрешив вывод всего дерева навигации, а затем с помощью css сократить его до только дети текущей страницы и т. д.


источник
3

Я сделал модифицированный ходок, который должен помочь! Не идеально - он оставляет несколько пустых элементов, но делает свое дело. Модификация - это в основном те биты $ current_branch. Надеюсь, это поможет кому-то!

class Kanec_Walker_Nav_Menu extends Walker {
/**
 * @see Walker::$tree_type
 * @since 3.0.0
 * @var string
 */
var $tree_type = array( 'post_type', 'taxonomy', 'custom' );

/**
 * @see Walker::$db_fields
 * @since 3.0.0
 * @todo Decouple this.
 * @var array
 */
var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

/**
 * @see Walker::start_lvl()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param int $depth Depth of page. Used for padding.
 */
function start_lvl(&$output, $depth) {
    $indent = str_repeat("\t", $depth);
    $output .= "\n$indent<ul class=\"sub-menu\">\n";
}

/**
 * @see Walker::end_lvl()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param int $depth Depth of page. Used for padding.
 */
function end_lvl(&$output, $depth) {
    global $current_branch;
    if ($depth == 0) $current_branch = false;
    $indent = str_repeat("\t", $depth);
    $output .= "$indent</ul>\n";
}

/**
 * @see Walker::start_el()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param object $item Menu item data object.
 * @param int $depth Depth of menu item. Used for padding.
 * @param int $current_page Menu item ID.
 * @param object $args
 */
function start_el(&$output, $item, $depth, $args) {
    global $wp_query;
    global $current_branch;

    // Is this menu item in the current branch?
    if(in_array('current-menu-ancestor',$item->classes) ||
    in_array('current-menu-parent',$item->classes) ||
    in_array('current-menu-item',$item->classes)) {
        $current_branch = true; 
    }

    if($current_branch && $depth > 0) {
        $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';

        $class_names = $value = '';

        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $classes[] = 'menu-item-' . $item->ID;

        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
        $class_names = ' class="' . esc_attr( $class_names ) . '"';

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

        $output .= $indent . '<li' . $id . $value . $class_names .'>';

        $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
        $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
        $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
        $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';

        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'>';
        $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }

}

/**
 * @see Walker::end_el()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param object $item Page data object. Not used.
 * @param int $depth Depth of page. Not Used.
 */
function end_el(&$output, $item, $depth) {
    global $current_branch;
    if($current_branch && $depth > 0) $output .= "</li>\n";
    if($depth == 0) $current_branch = 0;
}

}


источник
3

Проверьте код в моем плагине или используйте его для своих целей;)

Этот плагин добавляет улучшенный виджет «Меню навигации». Он предлагает множество опций, которые можно настроить для настройки вывода пользовательского меню через виджет.

Особенности включают в себя:

  • Пользовательская иерархия - «Только связанные подпункты» или «Только строго связанные подпункты».
  • Начальная глубина и максимальный уровень для отображения + плоский дисплей.
  • Показать все пункты меню, начиная с выбранного.
  • Отображать только прямой путь к текущему элементу или только дочерние
    элементы выбранного элемента (опция для включения родительского элемента).
  • Пользовательский класс для блока виджетов.
  • И почти все параметры для функции wp_nav_menu.

http://wordpress.org/extend/plugins/advanced-menu-widget/

Ян Бочинец
источник