Описание пунктов меню? Собственный Walker для wp_nav_menu()

6 апр. 2011 г., 01:25:50
Просмотры: 113K
Голосов: 111

Обычное меню WordPress выглядит так:

Главная | Блог | О нас | Контакты

Но я видел много страниц с описаниями под этими ссылками:

Главная | Наш Блог | О нас | Контакты
....домой...| читать далее| основная информация| форма связи

Как этого добиться?

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

0
Все ответы на вопрос 4
5
126

Вам понадобится пользовательский walker для навигационного меню.

По сути, вы добавляете параметр 'walker' в опции функции wp_nav_menu() и вызываете экземпляр расширенного класса:

wp_nav_menu(
    array (
        'menu'            => 'main-menu',
        'container'       => FALSE,
        'container_id'    => FALSE,
        'menu_class'      => '',
        'menu_id'         => FALSE,
        'depth'           => 1,
        'walker'          => new Description_Walker
    )
);

Класс Description_Walker наследует Walker_Nav_Menu и изменяет функцию start_el( &$output, $item, $depth, $args ), чтобы использовать $item->description.

Базовый пример:

/**
 * Создает HTML-список пунктов навигационного меню.
 * Замена нативного Walker, использующая описание.
 *
 * @see    https://wordpress.stackexchange.com/q/14037/
 * @author fuxia
 */
class Description_Walker extends Walker_Nav_Menu
{
    /**
     * Начинает вывод элемента.
     *
     * @param  string $output Передается по ссылке. Используется для добавления дополнительного контента.
     * @param  object $item   Объект данных пункта меню.
     * @param  int $depth     Глубина пункта меню. Может использоваться для отступов.
     * @param  array|object $args    Дополнительные строки. На самом деле всегда экземпляр 
                                     stdClass. Но это WordPress.
     * @return void
     */
    function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 )
    {
        $classes     = empty ( $item->classes ) ? array () : (array) $item->classes;

        $class_names = join(
            ' '
        ,   apply_filters(
                'nav_menu_css_class'
            ,   array_filter( $classes ), $item
            )
        );

        ! empty ( $class_names )
            and $class_names = ' class="'. esc_attr( $class_names ) . '"';

        $output .= "<li id='menu-item-$item->ID' $class_names>";

        $attributes  = '';

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

        // вставляем описание только для элементов верхнего уровня
        // вы можете изменить это
        $description = ( ! empty ( $item->description ) and 0 == $depth )
            ? '<small class="nav_desc">' . esc_attr( $item->description ) . '</small>' : '';

        $title = apply_filters( 'the_title', $item->title, $item->ID );

        $item_output = $args->before
            . "<a $attributes>"
            . $args->link_before
            . $title
            . '</a> '
            . $args->link_after
            . $description
            . $args->after;

        // Поскольку $output передается по ссылке, нам не нужно ничего возвращать.
        $output .= apply_filters(
            'walker_nav_menu_start_el'
        ,   $item_output
        ,   $item
        ,   $depth
        ,   $args
        );
    }
}

Или, альтернативно как прокомментировал @nevvermind, вы можете наследовать все функции родительского метода start_el и просто добавлять описание к $output:

function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) 
{
    parent::start_el( $output, $item, $depth, $args );
    $output .= sprintf( 
        '<i>%s</i>', 
        esc_html( $item->description ) 
    );
}

Пример вывода:

Пример вывода меню с описанием

Теперь включите поле описания в wp-admin/nav-menus.php, чтобы получить возможность редактировать это поле. Если вы этого не сделаете, WP просто поместит туда весь контент записи.

Включение поля описания в админке

Дополнительные материалы:

И это всё.

6 апр. 2011 г. 03:15:46
Комментарии

Если для вас наследование != переписывать весь метод, а просто сохранить то же имя, попробуйте так: public function start_el(&$output, $item, $depth, $args) { parent::start_el($output, $item, $depth, $args); $output .= sprintf('<i>%s</i>', esc_html($item->description)); }

nevvermind nevvermind
14 дек. 2011 г. 15:15:57

@nevvermind Вам стоит хотя бы проверить, есть ли содержание в описании. ;) Позиция описания в моем примере кода — просто самый простой способ проиллюстрировать решение. Если вам нужно вставить описание в якорь, вам придется полностью переписать функцию.

fuxia fuxia
16 дек. 2011 г. 12:38:44

да, вам пришлось бы писать весь метод, без сомнений, но для людей, которым нужно (скажем...) добавить его, это может сэкономить кучу головной боли. И во всем виноват WP. Аааа!

nevvermind nevvermind
16 дек. 2011 г. 16:47:54

Отличное решение, я использовал его в этом ответе, немного модифицировав. Возможно, вы сможете улучшить его, если я что-то упустил, спасибо.

The Alpha The Alpha
28 авг. 2012 г. 17:24:30

Мне действительно нужен был wp_nav_menu, но мне требовалось изменить параметр 'container_class' для моего конкретного случая, где при определенных условиях я заменял главное меню на другое, но мне нужно было, чтобы классы оставались согласованными для css.

D. Dan D. Dan
25 янв. 2018 г. 17:15:25
1
42

Начиная с WordPress 3.0, вам больше не нужен кастомный walker!

Доступен фильтр walker_nav_menu_start_el, см. https://developer.wordpress.org/reference/hooks/walker_nav_menu_start_el/

Пример:

function add_description_to_menu($item_output, $item, $depth, $args) {

   if (strlen($item->description) > 0 ) {
      // добавляем описание после ссылки
      $item_output .= sprintf('<span class="description">%s</span>', esc_html($item->description));
    
      // или... вставляем описание как последний элемент внутри ссылки ($item_output заканчивается на "</a>{$args->after}")
      // $item_output = substr($item_output, 0, -strlen("</a>{$args->after}")) . sprintf('<span class="description">%s</span >', esc_html($item->description)) . "</a>{$args->after}";
   }   
   return $item_output;
}
add_filter('walker_nav_menu_start_el', 'add_description_to_menu', 10, 4);
23 февр. 2015 г. 17:31:03
Комментарии

Отлично! Я использовал решение nav walker от @toscho, но это гораздо чище и проще в поддержке. Этот ответ должен быть принятым, это намного лучше с точки зрения практики.

Ronaldt Ronaldt
8 июл. 2015 г. 13:29:04
0

Этот способ не лучше и не хуже других предложений; он просто другой. Он также короткий и простой.

Вместо использования поля описания, как @toscho предлагает, вы можете заполнить поле "Заголовок" для каждого пункта меню текстом, который вам нужен, а затем использовать этот CSS:

.menu-item a:after { content: attr(title); }

Также можно легко использовать jQuery для добавления текста, но поскольку текст носит декоративный характер, CSS кажется более подходящим вариантом.

18 апр. 2012 г. 08:52:12
1

Вы также можете добавить элемент <span> после метки навигации в меню и использовать следующее CSS-правило, чтобы изменить его свойство display (по умолчанию оно имеет значение inline):

span {display:block}
8 сент. 2011 г. 11:58:03
Комментарии

Это простое и легкое решение, но зачем использовать span, если вы все равно делаете его блочным? XHTML/HTML4 не разрешают блочные элементы внутри ссылок, однако HTML5 разрешает, так что просто используйте div, и никакого CSS не понадобится!

James Mitch James Mitch
5 мар. 2013 г. 23:02:16