Заметки с тегом «html5» (страница 2)

Эксперимент: Доступность vs. Поисковая оптимизация

Дано: Макет облака тегов

Макет облака тегов

Задача: Сверстать его. Помимо визуального отображения обеспечить доступность виджета для скринридеров. Скрыть от поисковых роботов.

Очевидное решение

Изначально виджет был свёрстан примерно так:

<section class="widget tags-cloud">
  <h4 class="widget__title">Облако тегов</h4>
  <ul class="tags-cloud__list">
    <li class="tags-cloud__item tag-item tag-item_rank_9">
      <a class="tag-item__link" href="http://noteskeeper.ru/tag/jquery/">
        jquery
      </a>
    </li>
    <li class="tags-cloud__item tag-item tag-item_rank_8">
      <a class="tag-item__link" href="http://noteskeeper.ru/tag/css/">
        css
      </a>
    </li>
    <li class="tags-cloud__item tag-item tag-item_rank_2 tag-item_position_last">
      <a class="tag-item__link" href="http://noteskeeper.ru/tag/html5/">
        html5
      </a>
    </li>
  </ul>
</section>

Облако тегов — это, очевидно, список. Пункты списка выводятся как строчные элементы, а разделители (запятые) после каждого тега расставляются через CSS-свойство content.

В таком виде виджет просуществовал достаточное количество времени. Сайт индексировался поисковыми роботами. И я заметил, что поисковики активнее выдают страницы-концентраторы статей (архивы), на которые ведут ссылки из облака тегов, чем сами страницы со статьями. По этому таки страницы я закрыл от индексации <meta name="robots" content="noindex, follow">, а самим ссылкам добавил атрибут rel="nofollow".

Всё бы было хорошо, но так как эти теги повторялись на каждой странице, то поисковые системы выдавали этот виджет в качестве сниппета для некоторых страниц.

Сниппет, никак не относящийся к содержанию статьи

Поэтому я решил каким-либо образом скрыть содержимое этого блока от поисковых роботов.

Улучшения

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

<section class="widget tags-cloud" role="navigation">

Ранее заголовок блока был скрыт с помощью display: none. Но это так же делало его невидимым и для вспомогательных технологий. Чтобы его скрыть только при отображении на экране применим класс visuallyhidden.

<h4 class="widget__title visuallyhidden">Облако тегов</h4>

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

Тут возникла идея вообще избавиться от текста внутри ссылки. По сути, нам это очень даже играет на руку. Ведь мы хотим скрыть его от поисковиков. В итоге получилась такая разметка:

<section class="widget tags-cloud" role="navigation">
  <h4 class="widget__title visuallyhidden">Облако тегов</h4>

  <span class="tags-cloud__item tag-item tag-item_rank_9"><a
    class="tag-item__link" rel="nofollow"
    href="http://noteskeeper.ru/tag/jquery/"
    title="Заметки с тегом &laquo;jquery&raquo;"><span
    data-name="jquery" class="tag-item__title"></span></a></span>

  <span class="tags-cloud__item tag-item tag-item_rank_8"><a
    class="tag-item__link" rel="nofollow"
    href="http://noteskeeper.ru/tag/css/"
    title="Заметки с тегом &laquo;css&raquo;"><span
    data-name="css" class="tag-item__title"></span></a></span>

  <span class="tags-cloud__item tag-item tag-item_rank_2 tag-item_position_last"><a
    class="tag-item__link" rel="nofollow"
    href="http://noteskeeper.ru/tag/html5/"
    title="Заметки с тегом &laquo;html5&raquo;"><span
    data-name="html5" class="tag-item__title"></span></a></span>

</section>

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

Ключевые стили виджета:

.tag-item {
  /* чтобы запятая не отделялась от названия тега */
  white-space: nowrap;
}
.tag-item:after {
  /* запятая после каждого названия */
  content: "\2c";
}
.tag-item_position_last:after {
  /* у последнего тега нет запятой */
  content: "";
}
.tag-item__link {
  /* без этого ссылка не кликабельна */
  display: inline-block;
}
.tag-item__title:after {
  /* выводит содержимое атрибута на экран */
  content: attr(data-name);
}

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

Скринридер VoiceOver при переходе от тега к тегу зачитывает то, что выводится из атрибута и с некоторой паузой произносит содержимое атрибут title. Это звучит вполне естественно и не кажется повторением одного и того же. Проверить в других скринридерах пока не удалось. Если они окажутся не такими умными, то будут зачитывать только атрибут title.

Так же не понятно пока как к этим изменениям отнесутся поисковики. Буду ждать обновление индекса.

Комментарии к заметке: 12

Особенности микроразметки microdata

Микроданные (microdata) становятся очень популярны для оформления структурированных данных благодаря активной поддержки формата со стороны W3C и крупнейших поисковиков, разрабатывающих словари. Сама разметка предельно проста и, в основном, осуществляется при помощи атрибутов:

itemscope

Группа свойств ключ-значение.

itemtype

Тип объекта. Фактически это ссылка на страницу с описанием в свободной форме всех названий ключей, которые применимы к описываемому объекту. Этот атрибут неприменим к элементам без атрибута itemscope.

itemprop

Свойство объекта. Может быть строкой или другим объектом. Значение, которое будет извлечено для указанного ключа, зависит от элемента, для которого применяется этот атрибут.

А теперь расскажу о чрезвычайно полезных особенностях разметки, которые не слишком хорошо освещены в документации и примерах.

Несколько свойств у одного элемента

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

<p itemscope itemtype="http://schema.org/Person">
  Автор:
  <a itemprop="name url" href="http://noteskeeper.ru/about/">
    Владимир Кузнецов
  </a>
</p>

Ссылка на свойства

Иногда так получает, что данные, относящиеся к размечаемому объекту, находятся за пределами «корневого» элемента. Специально для этого случая предусмотрен атрибут itemref. Он применяется к элементу с itemscope и содержит id другого DOM-элемента, где находятся остальные свойства. Можно указать через пробел идентификаторы нескольких элементов.

Например, из-за особенностей оформления страницы, автор и комментарии к статье физически не могут находиться внутри <article>.

<div itemscope itemtype="http://schema.org/Person" itemprop="author" id="author">
  <a itemprop="name url" href="http://noteskeeper.ru/about/">
    Владимир Кузнецов
  </a>
</div>

<article itemscope itemtype="http://schema.org/Article" itemref="author comments">
  <header>
    <h2 itemprop="name">Особенности микроразметки microdata</h2>
    <link itemprop="url" href="http://noteskeeper.ru/758/">
  </header>
  <div itemprop="articleBody">
    ... статья ...
  </div>
</article>

<section id="comments">
  <div itemprop="comment" itemscope itemtype="http://schema.org/UserComments">
    <div itemprop="name commentText">
      ... комментарий 1 ...
    </div>
  </div>
  <div itemprop="comment" itemscope itemtype="http://schema.org/UserComments">
    <div itemprop="name commentText">
      ... комментарий 2 ...
    </div>
  </div>
</section>

Тем не менее, в результате получилась отлично структурированная разметка. Автор статьи и комментарии примешались в основной поток свойств статьи так как, если бы они были фактически размещены там.

article
  itemType = http://schema.org/Article
  author
    person
      itemType = http://schema.org/Person
      name
        href = http://noteskeeper.ru/about/
        text = Владимир Кузнецов
      url
        href = http://noteskeeper.ru/about/
        text = Владимир Кузнецов
  comment
    usercomments
      itemType = http://schema.org/UserComments
      name = ... комментарий 1 ...
      commenttext = ... комментарий 1 ...
  comment
    usercomments
      itemType = http://schema.org/UserComments
      name = ... комментарий 2 ...
      commenttext = ... комментарий 2 ...
  name = Особенности микроразметки microdata
  url = http://noteskeeper.ru/758/
  articlebody = ... статья ...

Порядок следования свойств не оказывает никакого влияния на структуру. Парсер просто перечисляет их по мере обхода документа.

Примешивать свойства с помощью itemref можно даже для элементов, которые не имеют содержимого. Например,

<article itemscope itemtype="http://schema.org/Article">
  <meta itemprop="aggregateRating" itemref="rating" itemscope
      itemtype="http://schema.org/AggregateRating">
</article>
<meta itemprop="ratingValue" content="5" id="rating">

Объект AggregateRating требует обязательного наличия свойства ratingValue, но его нельзя передать в атрибуте content. Зато можно указать ссылку на другой элемент с нужными атрибутами.

На практике, пожалуй, этот случай лучше разметить с помощью обычных вложенных элементов с соответствующими атрибутами.

Комментарии к заметке: 11

Исправляем структуру HTML5-документа в старых браузерах

В старых версиях Internet Explorer теги, которых нет в HTML 4.01, правильно будут стилизоваться только, если они предварительно созданы через JavaScript.

document.createElement("article");

Если JavaScript будет отключен, то для таких тегов не только не будут применяться стили, но и сама структура документа будет нарушена.

Пример правильной структуры документа в Firefox 8

Правильная структура HTML5-документа

А так этот же документ будет разобран в Internet Explorer 6

Структура документа нарушена из-за новых тегов

Из-за нарушения общей структуры в таком документе становятся бессмысленными и каскадные правила CSS.

Наверное, сейчас верстка под IE6 с отключенным JS уже не является актуальнейшей проблемой. Тем не менее, поправить структуру документа можно с помощью дополнительных блочных или строчных (в зависимости от контекста) тегов на каждый тег, которого не было в спецификации HTML 4.01.

Исправленная структура документа

Комментарии к заметке: 3

Навигация по ссылкам в списках

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

Список авторов статей в блоге

Каждый элемент в списке содержит имя, фотографию и краткую биографию автора. Имя и фотография обрамлены ссылками на архив статей этого автора.

<ul class="authors">
  <li class="author">
    <div class="author-userpic">
      <a class="author-archive-link" title="Vicki Moyes" href="/author/vicki/">
        <img class="author-pic photo" alt="Vicki Moyes" src="/img/users/vicki-moyes.jpg">
      </a>
    </div>
    <p class="author-title">
      <a href="/author/vicki/" class="author-title-archive-link">Vicki Moyes</a>
    </p>
    <p class="author-bio">Director of Moyes Gliders.
      Organizer of annual Forbes competition.</p>
  </li>
</ul>

Используем tabindex=-1

Элементы, обрамленные ссылками

При переключении фокуса с помощью клавиши «Tab» пользователь каждый раз сначала попадает на ссылку с фотографии, затем на ссылку с имени.

Проектируя интерфейс, первостепенно внимание уделяется пользователям, работающим с мышью или touch-интерфейсом. Кому-то из них привычнее и удобнее кликать по картинкам, а кто-то кликает только по явно обозначенным ссылкам. Поэтому удалить ссылку, обрамляющую фотографию, нельзя. Но зато можно сделать так, чтобы она не участвовала в последовательности переключения фокуса с клавиатуры. Для этого добавим ей атрибут tabindex="-1".

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

Такой нехитрый прием позволяет значительно улучшить впечатления пользователя от интерфейса и сделать его поведение более предсказуемым.

Одна ссылка

В этом примере два находящиеся рядом объекта (фотография и имя автора) должны вести на одну и ту же страницу. Технически их можно обернуть одной ссылкой. В HTML5 внутри строчного элемента a могут находиться как строчные, так и блочные элементы.

<a href="page.html">
    <div><img src="img1.jpg" alt="Page image"></div>
    <p>Page title</p>
</a>

Фрагмент кода валиден на 100%, только если используется в HTML5 документе.

Комментарии к заметке: 4

Оглавление HTML5 документа

Механизм построения оглавления (outline) страницы базируется на тегах, используемых при разметке документа. Например, главная страница моего блога может иметь такое оглавление:

  1. Свежие заметки :: Хранитель заметок
    1. Свежие заметки
      1. Убираем неоднородности на повторяющейся текстуре
      2. Deferred Object
      3. Возвращаясь к проверке типа данных
      4. Тень у полей ввода в мобильном Safari
        1. Очень простое решение
      5. Таблица без таблицы или display: table-cell для всех браузеров
    2. Навигация по сайту
      1. Облако тегов
      2. Категории
      3. Ссылки
    3. Архив заметок по месяцам

Только два типа тегов влияют на оглавление страницы: заголовки (h1-h6 и hgroup) и структурные теги (section, article, aside и nav).

Влияние заголовков на построение оглавления

<div>
    <h1>Свежие заметки :: Хранитель заметок</h1>
    <!-- начинаем новый подраздел -->
    <h2>Свежие заметки</h2>
    <!-- начинается статья -->
    <h3>Убираем неоднородности на повторяющейся текстуре</h3>
    <!-- статья про неоднородности и как их убрать -->
    <p>Повторяющиеся текстуры бумаги или ткани могут быть с
        разными неоднородностями по площади.</p>
    ...
    <!-- статья закончилась и начинается новая -->
    <h3>Deferred Object</h3>
    <!-- статья про отложенные объекты -->
    <p>Термин «отложенный объект» тесно связан с событийной
        моделью создания компонент и модулей приложения.</p>
    ...
    <h3>Возвращаясь к проверке типа данных</h3>
    ...
    <h3>Тень у полей ввода в мобильном Safari</h3>
    ...
    <h4>Очень простое решение</h4>
    ...
    <h3>Таблица без таблицы или display: table-cell
        для всех браузеров</h3>
    ...
    <!-- раздел свежих заметок закончился и начинается новый раздел -->
    <h2>Навигация по сайту</h2>
    <h3>Облако тегов</h3>
    <ul>
        <li class="rank-9">jquery</li>
        <li class="rank-6">ui</li>
        <li class="rank-4">utility</li>
        <li class="rank-4">hint</li>
        <li class="rank-4">css</li>
    </ul>
    <h3>Категории</h3>
    <ul>
        <li>Вёрстка</li>
        <li>JavaScript</li>
        <li>Apple</li>
        <li>Разное</li>
    </ul>
    <h3>Ссылки</h3>
    ...
    <h2>Архив заметок по месяцам</h2>
    ...
</div>

Оглавление, построенное по заголовкам, подразумевает наличие неявных разделов. Каждый заголовок начинает неявный раздел документа. Заголовок меньшего уровня начинает подраздел внутри текущего и так далее. Неявный раздел документа заканчивается на заголовке того же или более высокого уровня.

Учет разделов при построении оглавления

Добавим в разметку страницы некоторые структурные элементы.

<div>
    <h1>Свежие заметки :: Хранитель заметок</h1>
    <section>
        <h2>Свежие заметки</h2>
        <article>
            <h3>Убираем неоднородности на повторяющейся текстуре</h3>
            <p>Повторяющиеся текстуры бумаги или ткани могут быть с
                разными неоднородностями по площади.</p>
            ...
        </article>
        <article>
            <h3>Deferred Object</h3>
            <p>Термин «отложенный объект» тесно связан с событийной
                моделью создания компонент и модулей приложения.</p>
            ...
        </article>
        <article>
            <h3>Возвращаясь к проверке типа данных</h3>
            ...
        </article>
        <article>
            <h3>Тень у полей ввода в мобильном Safari</h3>
            ...
            <h4>Очень простое решение</h4>
            ...
        </article>
        <article>
            <h3>Таблица без таблицы или display: table-cell
                для всех браузеров</h3>
            ...
        </article>
    </section>
    <nav>
        <h2>Навигация по сайту</h2>
        <section>
            <h3>Облако тегов</h3>
            <ul>
                <li class="rank-9">jquery</li>
                <li class="rank-6">ui</li>
                <li class="rank-4">utility</li>
                <li class="rank-4">hint</li>
                <li class="rank-4">css</li>
            </ul>
        </section>
        <section>
            <h3>Категории</h3>
            <ul>
                <li>Вёрстка</li>
                <li>JavaScript</li>
                <li>Apple</li>
                <li>Разное</li>
            </ul>
        </section>
        <section>
            <h3>Ссылки</h3>
            ...
        </section>
    </nav>
    <nav>
        <h2>Архив заметок по месяцам</h2>
        ...
    </nav>
</div>

Фактически структура оглавления ни сколько не поменялась. Но теперь разделы заданы явно с помощь тегов section, article, nav.

Одним из широко обсуждаемых нововведений HTML5 стало то, что в документе разрешено использовать несколько тегов h1. Из-за того, что деление на разделы происходит явно, внутри каждого раздела можно формировать свою структуру из заголовков.

В спецификации HTML5 указано:

Sections may contain headings of any rank, but authors are strongly encouraged to either use only h1 elements, or to use elements of the appropriate rank for the section’s nesting level.

Разделы могут содержать заголовки любого уровня. Но авторам настоятельно рекомендуется использовать только элементы h1 или элементы, соответствующие уровню вложенности раздела.

Лично я не стал бы использовать несколько элементов h1 до тех пор пока такой принцип разметки не получит поддержку в поисковых системах и читалках с экрана. Гораздо безопаснее структурировать документ, с использованием тегов явной разметки секций и заголовки такого же уровня, как если бы секции задавались неявно. С появлением полной поддержки вам не придется менять разметку страницы, так как она уже будет готова для обработки другим алгоритмом.

Смешанный подход

Неявные разделы могут появляется внутри явных разделов (но не наоборот).

<div>
    <h1>Свежие заметки :: Хранитель заметок</h1>
    <section>
        <h2>Свежие заметки</h2>
        <article>
            <h3>Тень у полей ввода в мобильном Safari</h3>
            ...
            <h4>Очень простое решение</h4>
            ...
        </article>
    </section>
</div>

Этот фрагмент документа, очевидно, выдаст следующую структуру:

  1. Свежие заметки :: Хранитель заметок
    1. Свежие заметки
      1. Тень у полей ввода в мобильном Safari
        1. Очень простое решение

Заголовок h4 образует неявный раздел внутри статьи, заданной явным образом тегом article.

Разделы без названия

Название явного раздела документа берется из ближайшего заголовка. Чаще всего без названия в оглавлении остаются разделы nav и aside. В спецификации нет жестких указаний на использование в них заголовков. Если все же такие разделы появляются по каким-либо причинам, то это не стоит считать плохой разметкой. Тем не менее, именование всех возможных разделов повышает понятность и доступность их содержимого.

Оставлять же без названия разделы section и article не стоит. Наличие такого раздела, например, может быть связано с не правильным выбором тега между div, section и article. Если для блока нельзя придумать заголовок, то его стоит просто разметить с помощью тега div.

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

<body>
    <article>
        <h1>Blog post title</h1>
        <p>Blog post content</p>
    </article>
</body>

Такой документ останется без названия, так как появился явный раздел, размеченный тегом article. Чтобы решить эту проблему достаточно перед article поставить еще один тег h1 с аналогичным содержимым. Этот прием никак не повиляет на поисковую оптимизацию, так как заголовки будут одинаковыми (или почти одинаковыми).

hgroup

Элемент hgroup может содержать только заголовки и его назначение – это убрать все заголовки из оглавления кроме заголовка самого высокого уровня.

Инструменты и документация

Комментарии к заметке: 2