Разметка WordPress-меню по БЭМ с помощью волкера

Я верстаю по методологии БЭМ и при разработке WordPress-шаблонов стараюсь там, где это возможно, привести к соответствующему виду встроенные в WordPress CSS-классы.

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

Далее в примерах будет использоваться БЭМ-блок с именем nav.

Особенности

  • За основу взят волкер Walker_Nav_Menu, расположенный в файле /wp-includes/class-walker-nav-menu.php.
  • CSS-классы, добавленные при редактировании меню в админке в поле “Классы CSS”, становятся модификаторами. Например, если вы указали через пробел два класса: “mod-1 mod-2”, будут добавлены модификаторы nav__item--mod-1 и nav__item--mod-2.
  • У ссылки текущей страницы удаляется атрибут href и добавляется класс nav__item--active.
  • Для пункта-родителя добавляется класс .nav__item--active-parent.
  • Для пункта, являющегося предком среди родительских пунктов, добавляется класс .nav__item--active-ancestor.
  • Для списков 2-го и далее уровней вложенности добавляется класс nav__sub-menu и модификатор вида nav__sub-menu--level-1, показывающий уровень вложенности.

Как это выглядит в коде

PHP-код для вывода меню в шаблоне:

wp_nav_menu( [
  'theme_location' => 'header-nav',
  'depth'          => 1,
  'container'      => false,
  'fallback_cb'    => false,
  'echo'           => true,
  'walker'         => new BEM_Walker_Nav_Menu(),
  'bem_block'      => 'nav',
  'items_wrap'     => '
    <nav class="nav">
      <ul class="nav__list">%3$s</ul>
    </nav>
  ',
] );

Здесь важны два параметра:

  1. walker – подключает соответствующий волкер;
  2. bem_block – содержит имя блока.

В результате получится примерно следующая структура HTML-кода:

<nav class="nav">
  <ul class="nav__list">
    <li class="nav__item">
      <a class="nav__link" href="#">Пункт 1</a>
    </li>
    <li class="nav__item">
      <a class="nav__link" href="#">Пункт 2</a>
      <ul class="nav__sub-menu  nav__sub-menu--level-1">
        <li class="nav__item">
          <a class="nav__link" href="#">Подпункт 1</a>
        </li>
        <li class="nav__item">
          <a class="nav__link" href="#">Подпункт 2</a>
        </li>
        <li class="nav__item">
          <a class="nav__link" href="#">Подпункт 3</a>
        </li>
      </ul>
    </li>
    <li class="nav__item">
      <a class="nav__link" href="#">Пункт 3</a>
    </li>
  </ul>
</nav>

Скачать

BEM_Walker_Nav_Menu.zip

Загрузок: 631 | Размер: 2 Кб

Подключение

  1. Поместите файл BEM_Walker_Nav_Menu.php, находящийся в скачанном архиве, в папку с шаблоном, например, сюда: includes/BEM_Walker_Nav_Menu.php.
  2. Подключите его в файле functions.php следующей строкой:

    require_once 'includes/BEM_Walker_Nav_Menu.php';
  3. Используйте параметры walker и bem_block в функции wp_nav_menu(), как указано выше.

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

  1. Александр
    19 августа 2020 г. в 19:54

    С возвращением!!!

  2. Кирилл
    7 марта 2021 г. в 17:35

    Здравствуйте! Я воспользовался вашим walker`ом, но возникла проблема. Как сделать так , что бы класс __sub-menu на более глубоких уровнях менялся на другой, а то получается он на всех уровнях и на втором и на третьем и т.д? С уважением Кирилл.

    1. 9 марта 2021 г. в 16:55 / ответ на коммент Кирилл

      Например, заменив эту строку:

      $submenu_classes = array( $block . '__sub-menu', $block . '__sub-menu--level-' . ( $depth + 1 ) );

      кодом:

      if ($depth == 0) {
        $submenu_classes = array( $block . '__sub-menu-1', $block . '__sub-menu--level-' . ( $depth + 1 ) );
      } elseif ($depth == 1) {
        $submenu_classes = array( $block . '__sub-menu-2', $block . '__sub-menu--level-' . ( $depth + 1 ) );
      } else {
        $submenu_classes = array( $block . '__sub-menu', $block . '__sub-menu--level-' . ( $depth + 1 ) );
      }
      

      Пример показан для 1-го и 2-го уровня вложенности. По аналогии можно сделать для любого уровня.

  3. 13 мая 2021 г. в 18:00

    Гратс. Спасибо. Немного подстроил под себя. Отличное решение!
    А именно, пришлось добавить вывод отдельного класса для LI у которого есть дочерний элемент. Ну и свои классы прописал еще)

  4. 28 января 2022 г. в 04:57

    В debug.log ошибка спамится постоянно

    Подскажите, плиз, как решить.

  5. 28 января 2022 г. в 05:00

    Сорри, забыл приложить ошибку, а сайт не дает второй коммент отправить ни текущий отредактировать :)

    PHP Notice: Trying to get property ‘cat_ID’ of non-object in BEM_Walker_Nav_Menu.php on line 66
    PHP Notice: Undefined offset: 0 in BEM_Walker_Nav_Menu.php on line 66

    1. 28 января 2022 г. в 18:25 / ответ на коммент Alex

      Попробуйте заменить эту часть кода:

      if ( is_single() ) {
        $category = get_the_category();
        $cat_id = $category[0]->cat_ID;
        $ancestors = get_ancestors( $cat_id, 'category' );
        if ( in_array( $item->object_id, $ancestors ) ) {
          $item_classes[] = $block . '__item--active-ancestor';
        }
      }

      на такую:

      if ( is_single() ) {
        $category = get_the_category();
        if ( $category ) {
          $cat_id = $category[0]->cat_ID;
          $ancestors = get_ancestors( $cat_id, 'category' );
          if ( in_array( $item->object_id, $ancestors ) ) {
            $item_classes[] = $block . '__item--active-ancestor';
          }
        }
      }

      У меня проблема не воспроизводится, поэтому не могу протестировать.

Ваш комментарий

Жирный текст

Ссылка

Цитата

Внутристрочный код

CSS-код

HTML-код

JavaScript-код

PHP-код