Семантически правильныe Breadcrumbs на pdoCrumbs

Верстая очередной проект для замороченных на SEO клиентов, столкнулся с тем, что pdoCrumbs конечно хорош в своей программной логике, но приведенные в документации примеры не совсем верно построены с точки зрения SEO и архитектуры HTML.

Под катом сам код, мои заметки и несколько полезных комментариев.




[[pdoCrumbs?
            &showHome=`1`
            &outputSeparator=`<li> / </li>`
            &tplWrapper=`@INLINE <ul itemscope="" itemtype="http://schema.org/BreadcrumbList" id="breadcrumbs">{$output}</ul>`
            &tpl=`@INLINE 
                <li><span itemscope="" itemprop="itemListElement" itemtype="http://schema.org/ListItem">
                    <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$menutitle}</span><meta itemprop="position" content="{$idx}"></a>
                </span></li>`
            &tplCurrent=`@INLINE 
                <li><span itemscope="" itemprop="itemListElement" itemtype="http://schema.org/ListItem">
                    <span itemprop="name">{$menutitle}</span><meta itemprop="position" content="{$idx}">
                </span></li>`
        ]]

на FENOM

{'pdoCrumbs' | snippet : [
    'showHome' => 1,
    'outputSeparator' => '<li> / </li>',
    'tplWrapper' => '@INLINE <ul itemscope="" itemtype="http://schema.org/BreadcrumbList" id="breadcrumbs">{$output}</ul>',
    'tpl' => '@INLINE
        <li><span itemscope="" itemprop="itemListElement" itemtype="http://schema.org/ListItem">
             <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$menutitle}</span><meta itemprop="position" content="{$idx}"></a>
        </span></li>',
    'tplCurrent' => '@INLINE
        <li><span itemscope="" itemprop="itemListElement" itemtype="http://schema.org/ListItem">
        <span itemprop="name">{$menutitle}</span><meta itemprop="position" content="{$idx}">
    </span></li>'
]}

Вот два нюанса, которые я добавил.

  1. В разметку кода я добавил семантическую разметку schema.org для хлебных крошек. Здесь думаю комментарии излишни. Поисковики будут счастливы.
  2. Также проходя валидацию стандартного кода из инструкции к pdoCrumbs на validator.w3.org/ я с удивлением заметил ошибку, которая в общем то логична, но почему то я не задумывался. Суть ошибки в том, что разделитесь тоже нужно оформлять как элемент списка (если мы конечно используем списки). Разделитель по умолчанию — это просто \n
Ну вот собственно и вся заметка. Можете смело пролистывать ее и читать комментарии, а если стало совсем скучно, тогда идем наливаем себе очередную порцию кофе и идем работать.
Николай Савин
27 января 2017, 19:12
modx.pro
49
16 044
+20
Поблагодарить автора Отправить деньги

Комментарии: 31

Viktor
27 января 2017, 23:02
1
+4
лично я использую в качестве разделителя css, чтобы не засорять контент ненужными для контента знаками

.breadcrumbs > li + li:before {
  content: "\00bb\00a0";
}
    mngatoff
    30 января 2017, 03:43
    0
    достаточно content: "/"
    из коробки компонент имеет верстку под бутстрап, и там разделитель встроен.
    Андрей Коробков
    28 января 2017, 12:13
    +1
    Думаю {$menuindex} не совсем корректно использовать, правильней будет {$idx}. Так position указывает на позицию ресурса в своем уровне, а вроде как должен на позицию в серии.
    Владимир
    28 января 2017, 13:16
    0
    Подскажи, а по семантической разметке иной навигации, например Neighbors (соседние материалы) есть ли проверенные рецепты?
    {'!pdoNeighbors' | snippet : [ 
    'sortby' => 'menuindex',
    'field' => 'pagetitle',
    'sortdir' => 'asc',
    'toPlaceholder' => 'articleNeighbors',
    'tplPrev' => '@INLINE <nav id="Neighbors-left" itemscope="itemscope" itemtype="http://www.schema.org/SiteNavigationElement" class="pull-left"><i class="fa fa-arrow-left"></i> <span class="link-prev"><a itemprop="url" href="{{+link}}" title="{{+menutitle}}" ><!--noindex-->Предыдущая <!--/noindex--> <span class="metaspan" itemprop="name"> {{+menutitle}} </span></a></span></nav>',
    'tplUp' => '@INLINE  <nav id="Neighbors-Up"></nav>',
    'tplNext' => '@INLINE <nav id="Neighbors-right" itemscope="itemscope" itemtype="http://www.schema.org/SiteNavigationElement" class="pull-right"><span class="link-next"><a itemprop="url" href="{{+link}}" title="{{+menutitle}}"><!--noindex-->Следующая<!--/noindex--> <span class="metaspan" itemprop="name"> {{+menutitle}} </span> </a> </span><i class="fa fa-arrow-right"></i></nav>',
    'id' => $_modx->resource.id,
    ]}
    Я сделал как указал выше, т.е. разметка для навигации и скрытая в CSS подсказка
    .metaspan {position:absolute;left:-99999px;top:-99999px;
    }
    Но валидатор видит так
    sitenavigationelement
    
        itemType = http://www.schema.org/SiteNavigationElement
        url href = https://site.tld/culture/20150623853-чпу-урл
        text = Предыдущая Заголовок [...] статьи
        name = Заголовок [...] статьи
    
    
    sitenavigationelement
    
        itemType = http://www.schema.org/SiteNavigationElement
        url href = https://site.tld/culture/20150625863-чпу-урл
        text = Следующая Заголовок [...] статьи
        name = Заголовок [...] статьи
    Смущают попадающие в микроразметку «Предыдущая/Следующая»
      Николай Савин
      29 января 2017, 01:03
      0
      Соседями пользоваться не приходилось, потому рецептов пока нет.
      Единственное, мне на глаза попалось, что ты несколько раз используешь тэг nav. Это неверно.
      Тэг Nav как и h1 должен быть один на странице. Валидатор по моему даже ругается на дубли.
        Владимир
        29 января 2017, 01:09
        0
        Вроде нет ни где этого указания
        Тэг Nav как и h1 должен быть один на странице.
        . Наоборот, я читал рекомендации как можно чаще использовать nav для того, что бы все блоки навигации обозначить таким образом для поисковиков. И это работает. Т.е. выделенные nav блоки не портят так называемый поисковый сниппет, туда не попадают заголовки не относящиеся к основной статье. Меню на сайте может быть сколько угодно. Ели не так, буду признателен за ссылки на полезный материал.

        PS Собственно, вот: «Тег nav задает навигацию по сайту. Если на странице несколько блоков ссылок, то в nav обычно помещают приоритетные ссылки. Также допустимо использовать несколько тегов nav в документе. Запрещается вкладывать nav внутрь address.»
        Владимир
        29 января 2017, 01:21
        +1
        Валидатор по моему даже ругается на дубли
        — нет, ни один валидатор на дубли nav не ругается, это точно, проверенно.
      Никита
      29 января 2017, 15:37
      0
      Доброго дня! Я новичок в сфере вебмастеров, но стараюсь активно учиться и впитывать знания. Очень надеюсь что автор прочитает меня и ответит.
      Есть такая вещь как циклические ссылки, очень часто встречаются в меню. Можно ли от них избавиться, прописав в tplHere атрибут rel=«nofollow»? Или это не поможет?
        Здоров Александр
        29 января 2017, 15:54
        +1
        &tplCurrent=`@INLINE 
                        <li><span itemscope="" itemprop="itemListElement" itemtype="http://schema.org/ListItem">
                            <span itemprop="name">{$menutitle}</span><meta itemprop="position" content="{$menuindex}">
                        </span></li>`
        если не ошибаюсь этот участок отвечает за последний пункт хлебных крошек, там нет ссылки, так что циклическая ссылка не образуется
        вот ссылка на документацию по чанкам для хлебных крошек docs.modx.pro/components/pdotools/snippets/pdocrumbs#Параметры
        P.S. tplHere не встерчается в доках и на текущей страницы, вы путаете со сниппетом из pdoMenu
        P.S.S Ни разу не встречал чтобы убирали ссылку из текущего меню. но что мешает взять чанк tplHere, туда прописать
        @INLINE <li[[+classes]]><a href="[[+link]]" [[+attributes]]>[[+menutitle]]</a>[[+wrapper]]</li>
        обыкновенный инлайновый код и дописать уже все необходимое
          Oleg Pimanov
          21 ноября 2018, 11:19
          0
          На код, который используется в этой статье, ругается Google валидатор, Яндекс пропускает. Проблема в последней ссылке. Используйте такой код и оба поисковика будут довольны.
          [[pdoCrumbs?
                                     &showHome=`1`
                                     &showAtHome=`0`
                                     &tplWrapper=`@INLINE <ol class="breadcrumb" itemscope="" itemtype="http://schema.org/BreadcrumbList">[[+output]]</ol>`
                                     &tplHome=`@INLINE <li class="breadcrumb-item"><span itemscope="" itemprop="itemListElement" itemtype="http://schema.org/ListItem">
                                              <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$menutitle}</span><meta itemprop="position" content="{$idx}"></a>
                                          </span></li>`
                                     &tpl=`@INLINE <li class="breadcrumb-item"><span itemscope="" itemprop="itemListElement" itemtype="http://schema.org/ListItem"><a href="[[+link]]"  itemprop="item"><span itemprop="name">{$menutitle}</span><meta itemprop="position" content="{$idx}"></a></li>`
                                     &tplCurrent=`@INLINE <li class="breadcrumb-item active"><span itemscope="" itemprop="itemListElement" itemtype="http://schema.org/ListItem">
                                              <a style="pointer-events: none;cursor: default;" title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$menutitle}</span><meta itemprop="position" content="{$idx}"></a>
                                          </span></li>`
                                  ]]
          
          Николай Савин
          29 января 2017, 17:49
          0
          Привет Никита. Что ты понимаешь под циклическими ссылками? Ссылка страницы на саму себя?
            Никита
            30 января 2017, 03:03
            0
            Да, совершенно верно. Часто встречается в меню сайтов, том числе и тут. например на главной странице в меню ссылка на главную кликабельна и ведет на саму себя. Их наличие является небольшим отрицательным фактором
              Василий Столейков
              30 января 2017, 09:19
              0
              Поисковики кажется не учитывают сквозняки (сквозные ссылки в менюшках)…
                Гриборий
                01 февраля 2017, 21:05
                0
                Ну это еще как бы не юзерфреднли.
                  Василий Столейков
                  02 февраля 2017, 23:14
                  1
                  +2
                  А для меня часто именно отсутствие ссылки и есть неюзерфриендли. То есть если я перешел по этой ссылке в меню, то я часто нажимаю туда же чтобы снова загрузить эту ошибку. Не всегда дотягиваюсь до F5 или кнопки перезагрузки. И не я один такой, встречал много таких людей.

                  Вот пример с самого Яндекса: yandex.ru/support/webmaster/controlling-robot/robots-txt.xml — там в левом меню выделен пункт меню с текущей страницей, но он является ссылкой! То есть Яндекс сам активно использует сквозняки и понимает где они.
                    Гриборий
                    02 февраля 2017, 23:23
                    0
                    Этот вопрос, конечно, в целом не однозначный. Но вот более глобальный премер: логотип яндекса на странице поисковой выдачи, впрочем, и на любой другой «внутренней странице» – ссылка на главную. А на главной логотип (неожиданно?) уже не ссылка на главную.
                      Василий Столейков
                      02 февраля 2017, 23:30
                      1
                      0
                      Ты в этом уверен?
                      Возьми другой сервис Яндекса, например Яндекс.Вебмастер.
                      Вот сколько ни нажимаю на логотип Вебмастера joxi.ru/krD9XYbuEGnLkm — меня посылает на список сайтов, и это всегда, даже когда я нахожусь на этой самой страничке списка.
                        Гриборий
                        02 февраля 2017, 23:39
                        0
                        Я говорю именно о главной странице яндекса. А касетаельно других сервисов(вебмастера в том числе) там как и на остальных страницах лого самого ядекса ссылка на yandex.ru, а не на «главную» сервиса, обрати внимание там ссылка из 2 частей.
                          Василий Столейков
                          02 февраля 2017, 23:48
                          1
                          0
                          Вот именно. Лого Яндекса как бренда и лого Вебмастера как текущего сервиса. Но ты прав, что ситуация неоднозначна, думаю мы с тобой останемся каждый при своём мнении ;)
            Гриборий
            01 февраля 2017, 21:10
            0
            В сниппетах, не поддерживающих tplHere, в шаблон можно добавлять проверку из серии:
            <a href="[[+id:is=`[[*id]]`:then=`#`:else=`[[+link]]`]]" </a>
            Константин Ильин
            03 декабря 2018, 10:51
            0
            На ваш код ругался Гугл

            Вот вызов на который ни яндекс ни гугл не ругаются

            {'pdoCrumbs' | snippet : [
                'outputSeparator' => '<li> / </li>',
                'showHome' => 1,
                'tplWrapper' => '@INLINE <ul itemscope itemtype="http://schema.org/BreadcrumbList" class="breadcrumb">{$output}</ul>',
                'tpl' => '@INLINE
                    <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
                         <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$menutitle}</span></a>
                         <meta itemprop="position" content="{$idx}">
                    </li>',
                'tplCurrent' => '@INLINE
                    <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
                         <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$menutitle}</span></a>
                         <meta itemprop="position" content="{$idx}">
                    </li>'
            ]}
            Схема:
            schema.org/BreadcrumbList
            Просветов Игорь
            21 сентября 2019, 09:43
            0
            Вот вчера Гугл навалил ошибок вида: Строки навигации, отсутствует поле «item».
            В коде оно есть, но он его не видит из того, что парсер добавлет в код =""
            и на выходе имеем
            <ul itemscope="" itemtype="https://schema.org/BreadcrumbList">
            Как избавиться от этих назойливых ="" нужно что бы было так
            <ul itemscope itemtype="https://schema.org/BreadcrumbList" class="breadcrumb">
            тогда валидация проходит
              iWatchYouFromAfar
              22 сентября 2019, 00:44
              0
              Тоже прилетело. Вы неверно описали проблему, по ссылке ниже читайте.

              webinmind.ru/modx/packages-extras/pdotools/snippets/pdocrumbs#Вариант-без-пункта-текущей-страницы

              Я просто убрал неактивный пункт меню. В schema.org, в Google и вроде даже в Яндексе, все примеры крошек без неактивного элемента. По всей видимости так модно.
                Игорь
                08 ноября 2021, 22:17
                0
                На данный момент, рабочий вариант для Google такой:
                {'pdoCrumbs' | snippet: [
                'showAtHome' => '0',
                'showHome' => '1',
                'outputSeparator' => '<li> / </li>',
                'tplWrapper' => '@INLINE <ol class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList">{$output}</ol>',
                'tplHome' => '@INLINE
                        <li class="breadcrumb-item" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
                             <a title="{$menutitle}" itemprop="item" href="{$_modx->makeUrl(1)}"><span itemprop="name">{$pagetitle}</span>
                             <meta itemprop="position" content="{$idx}"></a>
                        </li>',
                'tpl' => '@INLINE
                        <li class="breadcrumb-item" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
                             <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$pagetitle}</span>
                             <meta itemprop="position" content="{$idx}"></a>
                        </li>',    
                'tplCurrent' => '@INLINE
                        <li class="breadcrumb-item" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
                             <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$pagetitle}</span>
                             <meta itemprop="position" content="{$idx}"></a>
                        </li>',
                
                ]}
                В предложенном @iWatchYouFromAfar варианте, валидатор микроразметки выдавал предупреждение, что у первого элемента хлебной крошки («Номе») в коде нет атрибута itemprop=«item». Ошибка пропала при замене в 'tplHome'
                href="{$link}"
                на
                href="{$_modx->makeUrl(1)}"
                Возможно, «костыль», но рабочий)
                  iWatchYouFromAfar
                  09 ноября 2021, 02:51
                  0
                  В моем варианте все прекрасно работает, использовал этот сниппет на более чем дватцати сайтах:

                  {'pdoCrumbs' | snippet : [
                  'outputSeparator' => '',
                  'showHome' => 1,
                  'showCurrent' => 0,
                  'hideSingle' => 1,
                  'tplWrapper' => '@INLINE <ul itemscope itemtype="http://schema.org/BreadcrumbList" class="breadcrumbs">{$output}</ul>',
                  'tpl' => '@INLINE
                          <li itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">
                               <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$pagetitle}</span></a>
                               <meta itemprop="position" content="{$idx}">
                          </li>'
                  ]}
                  Я в комментариях и на своем сайте привел аж два варианта генерации ХК сниппетом pdoCrumbs. Просто кто-то читает не внимательно.

                  Вы заменили генерацию урла для первого элемента ХК, это не костыль даже, это просто бесполезное действие, оно ни на что не влияет. Разве что в один чудный день, главная страница вашего сайта вдруг сменит идентификатор и вот тут костыль ваш всплывет. Костыли со скрипом но решают задачи, а вы просто заменили геренацию урла для первого элемента ХК. Это вообще не относится к itemprop=«item…

                  Если уж хочется говнокостылить, то делайте это хоть чуть-чуть правильно:

                  href="{$_modx->makeUrl($_modx->config['site_start'])}"

                  Рекомендую вам вернуть все как было и еще раз прогнать разметку через валидатор schema.org. После внимательно посмотреть, где ошибка.

                  Ошибка «Отсутствует поле „item“» возникает на элементе текущей страницы ХК при генерации ХК этим вариантом сниппета, но на всех нормальных сайтах в ХК просто напросто нет текущего элемента.

                  Именно по этой причине я и написал на своем сайте:

                  "Наиболее правильный вариант, который устраивает поисковых роботов и Google и Yandex:"
                    Игорь
                    09 ноября 2021, 15:03
                    0
                    1. @iWatchYouFromAfar, вы правы — мне следовало написать, что предложенный вами вариант решает задачу формирования семантически правильных ХК («хлебных крошек») в 99,9% случаях (специально проверил на своих и клиентских сайтах). Спасибо, что выложили рабочий код, но заметки на вашем сайте я читаю достаточно внимательно (давно уже в закладках).

                    Тем не менее, предложенный вами вариант формирования ХК на одном сайте «не сработал» (повторно перепроверял … ни в какую… ). Спасибо за предложенный вами вариант замены
                    href="{$_modx->makeUrl(1)}"
                    на
                    href="{$_modx->makeUrl($_modx->config['site_start’])}"
                    но в этом случае получаю ошибку
                    «Unexpected token 'site_start' in expression in pdoCrumbs»
                    Буду благодарен за совет, как от нее избавится.

                    2. По поводу того, что «на всех нормальных сайтах в ХК просто напросто нет текущего элемента» — не соглашусь. Например, на многих «нормальных» сайтах установлен SeoFilter, в котором «стандартный функционал заточен под чанк tpl.SeoFilter.crumbs.current, который идёт в комплекте. Для тех, кто использует pdoCrumbs, достаточно указать параметр в вызове сниппета: &tplCurrent=`tpl.SeoFilter.crumbs.current`»

                    Не стоит забывать, что уровень технических знаний у нас всех разный и кто-то (как, например, я) воспользуется документацией SeoFilter. Правда, чтобы добиться семантически правильных ХК, мне пришлось изменить tpl.SeoFilter.crumbs.current

                    <li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem" class="breadcrumb-item sf_crumb{if !$sflink} active{/if}" data-idx="{$idx}" data-separator="{$outputSeparator|htmlentities}">
                        {if $sflink}
                            <a title="{$menutitle}" itemprop="item" href="{$link}"><span itemprop="name">{$menutitle}</span>
                            <meta itemprop="position" content="{$idx}"></a>
                        {else}
                            <span itemprop="name">{$menutitle}</span>
                            <meta itemprop="position" content="{$idx}"
                        {/if}
                    </li>{if $sflink}{$outputSeparator}<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem" class="breadcrumb-item active sf_crumbs" data-idx="{++$idx}">
                    <span itemprop="name" class="sf_link">{$sflink}</span>
                    <meta itemprop="position" content="{$idx}"
                        {*закомментированный ниже вариант позволит возвращать ссылку *}
                        {*{set $page_link = $link}
                        {foreach ['.html','.php'] as $suffix}
                            {set $msufx = '*'~$suffix}
                            {if $page_link | match : $msufx}
                                {set $r_mask = '/'~$suffix~'$/'}
                                {set $page_link = ($page_link | ereplace: $r_mask:'/')}
                                {break}
                            {/if}
                        {/foreach}
                        <a href="{$page_link}{$sfurl}" class="sf_link">{$sflink}</a>
                        *}
                    </li>
                    {/if}
                    Возможно @Евгений Шеронов или кто-то из «старших товарищей» исправит и этот «говнокод» — к сожалению, других «правильных» вариантов мне найти не удалось.
                      iWatchYouFromAfar
                      09 ноября 2021, 17:24
                      0
                      Не буду тратить время, пытаясь вслепую понять, почему ваш сайт попал в 00.01% сайтов, на котором сниппет не работает.

                      Главное что вы решили вашу проблему. А как, и чем это аукнеться, это уже вопрос для более глубоких тем и обсуждений.
              Дарина
              27 октября 2019, 23:57
              0
              Угу… у меня опять как всегда всё через одно место походу.
              Подскажите пожалуйста, а как мне сделать? в шаблоне вывод вот так
              {'pdoCrumbs_ext' | snippet : ['fromUrl' => ('url_breadcrumbs' | placeholder)]}
                Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                31