WordPress: удаляем ссылку у активного пункта меню

Бытует мнение, что страница не должна ссылаться сама на себя. Если вы захотите узнать, что стало причиной возникновения такой точки зрения, то, скорее всего, будете постоянно натыкаться на СЕО форумы, где четких комментариев никто не дает, но все убеждены, что это хорошая практика. Где-то я встречал упоминание трудов Артемия Лебедева как первоисточника, но думаю, что первоисточником является буквальная трактовка того, что написано в спеке html5, в разделе описания ссылок.

Вольный перевод:

Ссылки представляют собой концептуальные конструкции, созданные с помощью элементов: а, area и link, которые демонстрируют связь между двумя ресурсами, одним из которых является текущий.

Не могу сказать, имеет ли смысл заострять внимание на этой формулировке, но ситуация такова, что в ТЗ регулярно встречается требование исключать ссылку «саму на себя» для элементов навигации.




Как удалить ссылку на текущую страницу в меню WordPress

Стандартная WordPress функция генерации меню wp_nav_menu() упомянутое выше требование не удовлетворяет. Я видел несколько решений данной задачи, но все они мне не нравятся по причине того, что используют достаточно громоздкий класс Walker_Nav_Menu, регулярные выражения и проверку значения REQUEST_URI. Получается не самое изящное и гибкое решение (в принципе, и не самое надежное).

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

Код функции:

/**
 * Extension for wp_nav_menu()
 * Remove element "a" from current menu item
 *
 * Optional $args contents additional arguments
 * string replace_a_by - Whether to wrap the link text node, and what to wrap it with. Default 'span'.
 * string xpath        - xPath expression.
 *
 * @param $args
 * @see wp_nav_menu()
 * @return mixed Menu output if $echo is false, false if there are no items or no menu was found.
 */
function wp_nav_menu_extended($args = array()) {
    $_echo = array_key_exists('echo', $args) ? $args['echo'] : true;
    $args['echo'] = false;

    $menu = wp_nav_menu($args);

    // Load menu as xml
    $menu = simplexml_load_string($menu);

    // Find current menu item with xpath selector
    if (array_key_exists('xpath', $args)) {
        $xpath = $args['xpath'];
    } else {
        $xpath = '//li[contains(@class, "current-menu-item") or contains(@class, "current_page_item")]';
    }

    $current = $menu->xpath($xpath);

    // If current item exists
    if (!empty($current)) {
        $text_node = (string) $current[0]->children();

        // Remove link
        unset($current[0]->a);

        // Create required element with text from link
        $element_name = $args['replace_a_by'] ? $args['replace_a_by'] : 'span';

        $dom = dom_import_simplexml($current[0]);
        $n = $dom->insertBefore(
            $dom->ownerDocument->createElement($element_name, $text_node),
            $dom->firstChild
        );

        $current[0] = simplexml_import_dom($n);
    }

    $xml_doc = new DOMDocument('1.0', 'utf-8');
    $menu_x = $xml_doc->importNode(dom_import_simplexml($menu), true);
    $xml_doc->appendChild($menu_x);

    $menu = $xml_doc->saveXML($xml_doc->documentElement);

    if ($_echo) {
        echo $menu;
    } else {
        return $menu;
    }
}

Функция принимает стандартный для wp_nav_menu() массив аргументов с двумя дополнительными ключами:

  • replace_a_by — имя тэга, которым будет заменён тэг ссылки. По умолчанию используется span;
  • xpath – селектор выбора текущего элемента меню в формате xpath. По умолчанию //li[contains(@class, "current-menu-item") or contains(@class, "current_page_item")].

Как использовать?

Достаточно просто заменить вызов wp_nav_menu() на wp_nav_menu_extended(). Например:

<?php wp_nav_menu_extended( array( 'theme_location' => 'primary', 'menu_class' => 'nav-menu') ); ?>

Корректно работает с любыми уровнями вложенности.

UPD (21.10.14)
Исправлена ошибка, которая приводила к тому, что после удаления ссылки, пункт меню оказывался после списка в пунктах меню, где есть второй уровень.

Комментарии (17)

  1. Эдуард

    Привет, Олег. А куда этот код писать? в какой файл?

  2. Роман

    Олег, подключил функцию.
    Выводит стандартное меню.

  3. Александр

    Здравствуйте. У меня меню с подрубриками. Когда нажимаю на подрубрику или рубрику без дочерних подрубрик все нормально. Но если перейти на родительскую рубрику, в которой есть подрубрики, эта родительская рубрика (с тегом span) перемещается по коду ниже ее дочерних подрубрик. Как можно исправить?
    Вот код:

    Подрубрика1
    Подрубрика2
    Подрубрика3
    Подрубрика4

    Родительская рубрика

  4. Александр

    Спасибо. Все работает. Но такая штука. Когда подключаю функцию, у меня слетают стили для этих меню. Может в коде можно прописать, чтобы стили оставались? Подскажите, пожалуйста, что можно сделать.

  5. Александр

    Спасибо за статью! А как сделать аналогию для рубрик и подрубрик? Можно наглядный пример.

    • А чем вам этот пример не нравится? По-моему, более наглядный сложно придумать.

      • Александр

        Пример-то отличный, спасибо за него! Но тут описан процесс для меню, а вот как сделать чтобы это также и на рубрики (в виджете рубрики) распространялось? Я не силён в программировании, поэтому и решил спросить))

        • Тогда вам нужен не наглядный пример, а готовое решение. К сожалению, сейчас нет времени на это.

          • Александр

            Олег, Вас понял. Когда появится время, то обязательно опубликуйте, просто в интернете этого нет. А тема я думаю, востребована!

  6. Антон

    Олег, добрый день!

    Я что-то делаю не так? Когда вывожу меню через wp_nav_menu_extended выдает ошибки и ломает сайт. Можете помочь?

    Ошибки: https://dl.dropboxusercontent.com/u/10751600/wp_menu.fw.png
    Вывожу меню вот так:

    • Антон

      Ой, видимо пхп код нельзя в комментариях.
      Вот так вывожу меню: wp_nav_menu_extended(‘menu=top’)

      • Первое сообщение об ошибке намекает вам, что функция должна принимать не строку в качестве параметра, а массив, в данном случае ассоциативный. Точно такой же массив, как принимает стандартная функция работы с меню.

  7. Константин

    Добрый день, сделал всё как описано, но работает почему то только для первого уровня пунктов меню. Для вложенного подменю уже не работает.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *