Подсветка класса предка wp_nav_menu() без дочерних элементов в структуре навигации

18 окт. 2010 г., 21:42:13
Просмотры: 31K
Голосов: 32

(Примечание модератора: Изначально называлось "Класс предка wp_nav_menu без дочерних элементов в структуре навигации")

У меня есть wp_nav_menu в шапке с тремя страницами. Когда я нахожусь на одной из этих страниц, элемент li, содержащий эту страницу в меню, получает класс .current_page_item. Эти три страницы имеют шаблоны, и эти шаблоны содержат пользовательские запросы для получения всех записей определенного типа контента. По сути, воспринимаемые "дочерние элементы" этой страницы верхнего уровня на самом деле не являются дочерними, они просто относятся к типу контента, который я связал с этой страницей верхнего уровня, используя шаблон.

Я хотел бы, чтобы пункты меню верхнего уровня получали класс 'current-ancestor', когда пользователь просматривает отдельную страницу определенного типа записи, опять же, связанную с этой страницей только через пользовательский запрос в файле шаблона.

Надеюсь, это имеет смысл - если нет, дайте мне знать, где я вас потерял! Очень признателен за любую помощь.

--Отредактировано для уточнения: Например, у меня есть статическая страница под названием Workshops (Мастерские), использующая шаблон. Её ЧПУ - workshops. Шаблон содержит пользовательскую функцию get_posts и цикл, который извлекает и отображает все записи пользовательского типа контента workshops. Если я нажму на заголовок одной из этих мастерских, я перейду к полному содержанию этой записи. Структура постоянных ссылок для пользовательского типа записей настроена как workshops/postname, поэтому с точки зрения пользователя эти записи являются дочерними элементами страницы Workshops, хотя на самом деле они все относятся к одному типу контента, но не связаны со страницей. Именно этот разрыв мне нужно эффективно закрыть в меню, подсвечивая пункт меню 'Workshops' при просмотре контента типа 'workshop'.

Снова надеюсь, что это имеет смысл. Думаю, я сказал 'workshop' более 20 раз в одном абзаце!

3
Комментарии

@Gavin - Можете уточнить, что именно вы пытаетесь реализовать? Ответить будет проще на конкретный пример, чем обсуждать в абстрактных терминах. Также было бы полезно, если бы вы объяснили структуру URL, связанную с этим вопросом.

MikeSchinkel MikeSchinkel
18 окт. 2010 г. 23:14:46

@Gavin - Так понятнее. Значит, ваш пункт верхнего уровня в меню - это список мастер-классов под названием "Мастер-классы" с путем /workshops/, и когда пользователь находится на странице мастер-класса (например, /workshops/example-workshop/), вы хотите, чтобы пункт меню "Мастер-классы" получил класс current_page_item, верно?

MikeSchinkel MikeSchinkel
19 окт. 2010 г. 07:37:19

wp_nav_menu() добавляет класс current-menu-ancestor

Daniel Sachs Daniel Sachs
14 июн. 2011 г. 00:06:13
Все ответы на вопрос 7
4
30

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

Вместо этого создайте пользовательскую ссылку в разделе Внешний вид → Меню. Просто укажите URL, который будет возвращать ваш пользовательский тип записей, задайте метку и нажмите "Добавить в меню".

http://example.com/workshops/

Или для некрасивых постоянных ссылок:

http://example.com/?post_type=workshops

Это создаст пункт меню, который отображает все записи данного типа, а также добавит класс current-menu-item при клике на этот пункт — но пока что класс не будет добавляться на других URL, кроме этого

После создания пункта зайдите в его настройки и введите слаг типа записи в поле "Атрибут title" (можно также использовать поле описания, но оно по умолчанию скрыто в настройках экрана админки).

Теперь нужно подключить фильтр nav_menu_css_class (который срабатывает для каждого пункта меню) и проверить, соответствует ли просматриваемый контент типу записи, указанному в вашем пользовательском пункте меню:

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2 );
function current_type_nav_class($classes, $item) {
    $post_type = get_query_var('post_type');
    if ($item->attr_title != '' && $item->attr_title == $post_type) {
        array_push($classes, 'current-menu-item');
    };
    return $classes;
}

В этом случае мы проверяем, что поле "Атрибут title" не пустое и соответствует текущему типу записей в запросе. Если условие выполняется, мы добавляем класс current-menu-item в массив классов и возвращаем изменённый массив.

Вы можете модифицировать код для сравнения с заголовком пункта меню, но если вам нужно назвать пункт меню иначе, чем слаг типа записи, использование полей "Атрибут title" или описания даёт такую гибкость.

Теперь при просмотре одиночной записи (или, вероятно, даже архивных списков) типа, соответствующего пункту меню, этому пункту будет присвоен CSS-класс current-menu-item, и ваше выделение будет работать.

Никаких страниц или шаблонов страниц не требуется ;-) URL-запрос отвечает за выборку нужных записей. Ваш шаблон цикла отвечает за отображение вывода запроса. Эта функция отвечает за распознавание отображаемого контента и добавление CSS-класса.

БОНУС

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

$types = get_post_types( array( 'exclude_from_search' => false, '_builtin' => false  ), 'objects' );
foreach ($types as $type) {
    wp_update_nav_menu_item( $menu_id, 0, array(
        'menu-item-type' => 'custom',
        'menu-item-title' => $type->labels->name,
        'menu-item-url' => get_bloginfo('url') . '/?post_type=' . $type->rewrite['slug'],
        'menu-item-attr-title' => $type->rewrite['slug'],
        'menu-item-status' => 'publish'
        )
    );
}
19 окт. 2010 г. 08:25:38
Комментарии

Вот это да! Я использую шаблоны страниц только потому, что макеты для этих страниц довольно сложные и не просто выводят список страниц, но я все равно могу использовать тот фильтр, который вы предоставили, чтобы проверять ID страницы. Особенность этой темы в том, что настройки темы позволяют сопоставлять страницы ('главная' - это эта страница, 'о нас' - эта страница и т.д.), так что это должно отлично сработать. Спасибо за (невероятно подробную) помощь!

Gavin Gavin
19 окт. 2010 г. 14:59:47

мне пришлось убрать current_page_parent из элемента навигации, который был моим блогом - но в остальном все сработало. спасибо

Philipp Kyeck Philipp Kyeck
10 окт. 2011 г. 17:58:54

у меня это не работало, так как $item->attr_title вытаскивал ЗАГОЛОВОК, а я писал заголовок заглавными буквами. поэтому я изменил атрибут на $item->post_name, и теперь все работает как надо.

honk31 honk31
29 нояб. 2013 г. 19:42:24

Я попытался адаптировать код для своей темы, но не могу заставить его работать. Класс не применяется к родительскому элементу меню, когда я нахожусь в пользовательском типе записи portfolio. Я использовал код, приведенный выше. В чем может быть проблема?

Casper Casper
25 мар. 2014 г. 17:51:18
0

вместо использования

$post_type = get_query_var('post_type');

Вы можете попробовать:

$post_type = get_post_type();

Поскольку иногда тип записи не устанавливается в переменной запроса. Это относится к стандартному типу записи "post", поэтому если вы хотите выделить запись, которая была выведена на странице списка, вам нужно использовать эту функцию. get_query_var() просто возвращает пустую строку для типов записей, которые не являются пользовательскими.

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2 );
function current_type_nav_class($classes, $item) {
    $post_type = get_post_type();
    if ($item->attr_title != '' && $item->attr_title == $post_type) {
        array_push($classes, 'current-menu-item');
    };
    return $classes;
}
17 мар. 2011 г. 16:18:20
0

@Somatic - это фантастика! Я немного изменил ваш код, чтобы он также работал с конкретной таксономией (которую я использую только для связанного post_type). Идея в том, чтобы использовать атрибут Title пункта меню для хранения как имени post_type, так и имени таксономии, разделённых точкой с запятой, а затем разделять их с помощью функции.

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2 );
function current_type_nav_class($classes, $item) {

    # Получаем Query Vars
    $post_type = get_query_var('post_type');  
    $taxonomy = get_query_var('taxonomy');

    # Получаем и разбираем атрибут Title пункта меню
    $title = $item->attr_title; // атрибут Title пункта меню в формате post_type;taxonomy
    $title_array = explode(";", $title);
    $title_posttype = $title_array[0];
    $title_taxonomy = $title_array[1];

    # Добавляем класс, если нужно
    if ($title != '' && ($title_posttype == $post_type || $title_taxonomy == $taxonomy)) {
        array_push($classes, 'current-menu-item');
    };
    return $classes;
}
13 апр. 2011 г. 03:12:54
0

Вот мое решение, если вы хотите работать с wp_list_pages.

Добавьте это в functions.php

add_filter('page_css_class', 'my_page_css_class', 10, 2);
function my_page_css_class($css_class, $page){
    $post_type = get_post_type();
    if($post_type != "page"){
        $parent_page = get_option('page_for_custom_post_type-'.$post_type);
        if($page->ID == $parent_page)
            $css_class[] = 'current_page_parent';
    }
    return $css_class;
}

Теперь просто добавьте в таблицу wp_options новую строку с option_name равным page_for_custom_post_type-xxxx и option_value с ID страницы, которую вы хотите связать.

Возможно, вы заметили, что уже существует опция под названием page_for_posts. Если у вас только один пользовательский тип записи, вы можете установить свою страницу в выпадающем списке на странице /wp-admin/options-reading.php, и навигация будет правильно устанавливать current_page.

Я думаю, что ядро WordPress должно расширить этот раздел выпадающим списком для каждого зарегистрированного типа записи.

13 июн. 2011 г. 21:58:05
2

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

add_filter('nav_menu_css_class', 'mbudm_add_page_type_to_menu', 10, 2 );
// Если пункт меню является страницей, добавляем имя шаблона как CSS-класс
function mbudm_add_page_type_to_menu($classes, $item) {
    if($item->object == 'page'){
        $template_name = get_post_meta( $item->object_id, '_wp_page_template', true );
        $new_class =str_replace(".php","",$template_name);
        array_push($classes, $new_class);
        return $classes;
    }   
}

Также я добавил классы body в header.php

<body <?php body_class(); ?>>

Для завершения решения требуется дополнительный CSS для применения состояния selected/active к пунктам меню. Я использую это для отображения архивов таксономий и пользовательских типов записей, связанных со страницей, как дочерних элементов:

/* состояния выбора - включаем подстраницы для всего, связанного с продуктами */
#nav-main li.current-menu-item a,
body.single-mbudm_product #nav-main li.lp_products a,
body.tax-mbudm_product_category #nav-main li.lp_products a,
#nav-main li.current_page_parent a{color:#c00;}
15 окт. 2011 г. 05:28:44
Комментарии

Это вызвало у меня следующую ошибку:

Warning: join() [function.join]: Invalid arguments passed in /home/path/to/wp-includes/nav-menu-template.php on line 76

Есть идеи, что здесь произошло?

Jeff K. Jeff K.
21 окт. 2011 г. 03:04:45

Кажется, я понял в чем дело. Проблема в том, что вы возвращаете $classes внутри оператора if. Простое перемещение return $classes за пределы и после этого if, похоже, решает указанную выше ошибку.

Jeff K. Jeff K.
21 окт. 2011 г. 04:14:08
0

@Somatic - Отличный код! Я внес одно изменение самостоятельно. Я хотел сохранить атрибут Title для его основного назначения, поэтому вместо этого разместил слаг типа записи (Custom Post Type) в поле Link Relationship (XFN) расширенных свойств меню, которые можно включить в настройках экрана. Я изменил

if ($item->attr_title != '' && $item->attr_title == $post_type) {

и заменил на

if ($item->xfn != '' && $item->xfn == $post_type) {
23 сент. 2011 г. 22:30:40
0

Отличная работа, Somatic.

К сожалению, я не понимаю, как можно вывести произвольные типы записей на странице так, как вы объясняете. Если я не использую page-portfolio.php и добавляю это на страницу, я получаю только ошибку 404.

Если делать, как предлагает Gavin, я немного изменил вашу функцию, чтобы также убрать класс "current_page_parent" со страницы блога, вот так:

add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2);
function current_type_nav_class($css_class, $item) {
$post_type = get_query_var('post_type');

if (get_post_type()=='portfolio') {
    $current_value = "current_page_parent"; 
    $css_class = array_filter($css_class, function ($element) use ($current_value) { return ($element != $current_value); } );
}

if ($item->attr_title != '' && $item->attr_title == $post_type) {       
    array_push($css_class, 'current_page_parent');
};
return $css_class;

}

10 дек. 2010 г. 14:36:55