Заметки с тегом «html»

Преобразователь кода символа

В HTML, CSS и JavaScript для представления кода символа используются совершенно разные способы:

  • в HTML чаще всего используется код символа в десятичной системе исчисления, записанный как HTML-сущность (например, —);
  • в CSS используется код в шестнадцатеричной системе исчисления, которому предшествует символ \ (например, \2014);
  • в JS внутри строк используется код в шестнадцатеричной система исчисления, записанный как Unicode-символ (например, \u2014).

Для определения нужного значения я использую Entity Conversion Calculator. Он конвертирует HTML-сущность в код для CSS и JS. Так же с его помощью можно получить код известного символа во всех форматах.

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

То, что скрыто комментарием

Содержимое HTML комментариев никогда не разбирается парсером документа. Однако, они не пропадают бесследно в пучине тегов. Все комментарии можно достать с помощью JavaScript.

Каждый элемент DOM-дерева имеет поле noteType, в котором указан его тип. Для комментария значения поля равно 8.

var children = document.getElementsByTagName("head")[0].childNodes;
var comments = Array.prototype.filter.call(children,
  function (node) { return node.nodeType == 8; });
var content = comments.map(function (node) { return node.nodeValue; });

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

Практическое применение

Условная загрузка

Шикарная идея применять комментарии для формирования разметки на стороне клиента материализовалась в виде проекта Адама Чамберза — Responsive Comments.

Блокам добавляется атрибут data-responsive-comment-media с медиа запросом или атрибут data-responsive-comment-supports с тестом Modernizr. В зависимости от выполнения указанного условия блоки будет появляться или скрывать. А так как изначально контент обвёрнут в комментарий, то это позволит избежать лишних запросов на сервер и лишних элементов в документе.

Поисковая оптимизация

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

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

Данные, которые скрыты от пользователя

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

<title> и <meta name="description">

Эти мета-данные были одними из самых первых, которые помогали роботам понять суть страницы.

В теге <title> размещают заголовок страницы. Для каждой страницы должен быть указан заголовок и лучше всего, если он будет уникальным для всего сайта. На размер строки заголовка нет явных ограничений, но из практических соображений не стоит делать его длиннее 70-80 символов.

В атрибуте content тега <meta name="description"> размещают кратное описание страницы. Длину стоит ограничить 150-160 символами потому, что оно активно используется для построения сниппета в поисковой выдаче и более длинное описание будет автоматически укорочено. Явного ограничения в спецификациях нет. Содержимое, как и в случае с заголовком страницы, должно быть уникальным для всего сайта.

Ещё одним классическим тегом считается <link rel="canonical">. Он нужен для того, чтобы сообщить роботам какая из страниц с одинаковым содержимым, если таковые имеются, считается основной. Адрес задаётся в атрибуте href.

Протокол Open Graph

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

Facebook разработал на основе RDFa упрощенный протокол Open Graph, который работает в одном пространстве имён og и имеет ограниченный набор типов данных (музыка, видео, статья, книга, персона и сайт).

<html prefix="og: http://ogp.me/ns# article: http://ogp.me/ns/article#">
<head>
  <title>Больше семантики для логотипа :: Хранитель заметок</title>
  <meta property="og:title" content="Больше семантики для логотипа">
  <meta property="og:type" content="article">
  <meta property="og:url" content="http://noteskeeper.ru/945/">
  <meta property="og:image" content="http://noteskeeper.ru/wp-content/themes/nk/i/nk-logo-200.png">
  <meta property="og:description" content="Разметке логотипа можно добавить семантики с помощью microdata. Тип Brand позволяет указать логотип для продуктов и людей">
  <meta property="og:site_name" content="Хранитель заметок">
  <meta property="article:published_time" content="2013-08-26T03:31:18+00:00">
  <meta property="article:section" content="Вёрстка">
  <meta property="article:tag" content="microdata">
  ...

Протокол получил поддержку у множества социальных сервисов и поисковых систем. Некоторые из них, например, Twitter и Вконтакте, адаптируют его для своих нужд, используя часть полей Open Graph и дополняя необходимыми данными.

Microdata и RDFa

Почти одновременно в 2009 году появились черновики двух спецификаций, оперирующих со сложными типами объектов и поддерживающие их глубокую вложенность:

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

RDFa позволяет указывать значение свойства с помощью атрибута content у любого элемента:

<span property="dc:modified"
  content="2013-09-16T15:00:00+00:00"
  datatype="xsd:dateTime">16 сентября 2013 года</span>

В то время как парсерам Microdata требуются дополнительные элементы <meta> или <link>:

<span>16 сентября 2013 года</span>
<meta itemprop="dateModified" content="2013-09-16T15:00:00+00:00">

Разметка с помощью Microdata или RDFa позволяют сообщить роботам о семантике контента независимо от того какими тегами он размечен в HTML. Более того, форматы позволяют обозначить данные, невидимые пользователю в силу внешнего дизайна страницы.

“Shaken, not stirred”

При всём разнообразии мета-данных, которые можно впихнуть на страницу, складывается ситуация, когда одни и те же данные будут повторяться в HTML-коде несколько раз.

Например, описание страниц, которое фигурирует во всех форматах, можно передать в одном теге:

<meta content="Разметке логотипа можно добавить семантики с помощью microdata. Тип Brand позволяет указать логотип для продуктов и людей"
  name="description"
  property="og:description"
  itemprop="description"
  id="_articleDescription">

В этом примере свойство description типа http://schema.org/Article подключается в объект с помощью ссылки по id в атрибуте itemref. Если это позволяет разметка, то тип самого важного объекта на странице можно объявить у корневого элемента документа. Тогда все теги meta естественным образом окажутся в области видимости этого объекта.

<html prefix="og: http://ogp.me/ns# article: http://ogp.me/ns/article#" itemscope itemtype="http://schema.org/Article">
<head>
  <title>Больше семантики для логотипа :: Хранитель заметок</title>
  <meta property="og:title" content="Больше семантики для логотипа" itemprop="name">
  <meta property="og:type" content="article">
  <meta property="og:url" content="http://noteskeeper.ru/945/" itemprop="url">
  <meta property="og:image" content="http://noteskeeper.ru/wp-content/themes/nk/i/nk-logo-200.png" itemprop="image">
  <meta property="og:description" content="Разметке логотипа можно добавить семантики с помощью microdata. Тип Brand позволяет указать логотип для продуктов и людей" itemprop="description">
  <meta property="og:site_name" content="Хранитель заметок">
  <meta property="article:published_time" content="2013-08-26T03:31:18+00:00" itemprop="dateCreated">
  <meta property="article:section" content="Вёрстка" itemprop="articleSection">
  <meta property="article:tag" content="microdata" itemprop="keywords">
  ...

Так как привязка нового объекта к «родителю» осуществляется только наличием у него атрибута itemprop, то при его отсутствии новый объект помещается на самый верхний уровень иерархии. Получается, что на странице можно безопасно размещать несколько независимых объектов.

Атрибуты role и aria-*

Особняком стоят атрибуты Web Accessibility Initiative (WAI). Они используются вспомогательными технологиями для обеспечения доступности контента для пользователей с ограниченными возможностями.

Скринридерам часто приходится специально сообщать о назначении элемента (атрибут role), его названии (атрибуты aria-label, aria-labelledby, aria-describedby) или состоянии, так как у него нет другого способа передать эту информацию пользователю.

Заключение

  • Старайтесь сообщить как можно больше полезной информации о содержимом страницы для роботов даже если она останется невидимой для человека.
  • Избегайте повторения одних и тех же мета-данных в разных форматах. Чаще всего их можно объединить, используя разные техники.
  • Проверяйте качество разметки с помощью соответствующих парсеров и валидаторов.
Комментарии к заметке: 4

Эксперимент: Доступность 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.

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

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

Стили для разных версий IE

Чтобы не использовать хаки для IE соответствующие стили стоит вынести в отдельные файлы и подключать их через условный комментарий. Так и основные CSS файлы не будут загромождаться мусором и можно отделить особенные стили для разных версий.

Но можно использовать несколько иной подход.

<!--[if lt IE 7]><html class="ie6"><![endif]-->
<!--[if IE 7]><html class="ie7"><![endif]-->
<!--[if IE 8]><html class="ie8"><![endif]-->
<!--[if IE 9]><html class="ie9"><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html><!--<![endif]-->

В этом случае через условный комментарий корневому элементу документа назначаются разные классы в зависимости от версии браузера. А затем этот класс используется в селекторе с каскадом.

.block {
    padding-bottom: 0;
}
.ie6 .block,
.ie7 .block {
    padding-bottom: 1em;
}

Например, для всех браузеров нижний отступ у элемента с классом block будет нулевым, а в IE6 и IE7 он будет равен 1em.

Стоит заместить, что IE10 игнорирует любые условные комментарии.

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