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

Inline SVG для пиктограмм

В очередном проекте мы пробуем встраивать на страницы графику в виде SVG. Заказчик не требует полной поддержки старых браузеров, но ожидает в них доступность основного контента. Для некоторых пиктограмм мы выбрали inline SVG, так как можем легко управлять их внешним видом через стили и даже трансформировать их.

Все изображения собраны в одном SVG-файле shapes.svg.

<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
  <defs>
    <rect id="shape-rectangle" x="1" y="1" width="30" height="30"/>
    <circle id="shape-circle" cx="16" cy="16" r="24"/>
  </defs>
</svg>

Тут у нас две фигуры: квадрат и круг. Чтобы в дальнейшем мы могли сослаться на них, у каждой фигуры должны быть уникальные значения id. Забегая вперёд скажу, что их уникальность должна быть не только в пределах SVG, но и в пределах всего документа, где в дальнейшем будет отображаться эта иконка.

На заметку:

Все фигуры определены внутри тега <defs>. Они не будут отображаться, если попытаться открыть этот файл в каком-нибудь редакторе или просмотрщике. На них можно только сослаться. Несколько контуров могут быть объединены в одну фигуру с помощью тега <g>.

Затем в HTML можно сослаться на заготовленные контуры

<svg xmlns="http://www.w3.org/2000/svg"
      style="width: 32px; height: 32px;">
  <use xlink:href="shapes.svg#shape-rectangle"></use>
</svg>

Браузер отобразит квадрат в блоке размером 32 на 32 пиксела. Габариты блока, разумеется, нужно задавать с помощью CSS. В данном примере я указал их в атрибуте style только для наглядности.

<svg xmlns="http://www.w3.org/2000/svg"
      viewBox="-9 -9 50 50"
      style="width: 32px; height: 32px;">
  <use xlink:href="shapes.svg#shape-circle"></use>
</svg>

C кругом чуть сложнее так как он не вписывается в желаемые габариты 32 на 32 пиксела. Поэтому inline SVG нужно указать атрибут viewBox. Браузер сам правильно смасштабирует фигуру до размеров блока.

Подводные камни

При использование inline SVG нужно знать и помнить несколько особенностей.

Поддержка

Все современные браузеры прекрасно справляются с базовыми конструкциями SVG. Если вам нужны какие-то особенные фильтры, то стоит протестировать их отдельно.

Internet Explorer

Мы обнаружили, что ссылка на внешний SVG-файл с контурами не работает, если страница отображается в IE. Это известное поведение и исправить хаками его нельзя. Остаётся только явно включить этот SVG в HTML страницу. Соответственно при использовании в <use xlink:href="#shape-rectangle"></use> остаётся один якорь (для этого и нужна была уникальность id).

Если SVG-файл с картинками стал достаточно большим и его уже обременительно каждый раз передавать на странице, то его специально для IE можно загружать динамически и затем уже вставлять на страницу.

<script id="svg-loader">
  $.ajax({url: "shapes.svg", cache: true, dataType: "html"})
    .done(function (data) {
      $("#svg-loader").before(data);
      $(".svg-icon > use").attr("xlink:href", function (i, href) {
        return href.substr(href.indexOf("#"));
      });
    });
</script>

Важно, чтобы браузер доставал файл из своего кеша, если это возможно.

Ещё важно, чтобы в SVG разметке не было самозакрывающихся тегов. Нужно явно указывать закрывающий тег, если нет контента. Старые парсеры HTML-супа допускали лишь ограниченное количество самозакрывающихся тегов (<hr/>, <br/>, <input/> и т.п.) — поэтому мы не можем писать <div/>. Конструкции SVG могут поломать весь документ при определённом стечении обстоятельств.

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

Охота за последним видимым элементом

Последний элемент среди соседей можно выбрать селектором псевдо-класса :last-child.

.item { display: block; }
.item:last-child { font-weight: bold; }

Разметка:

<div>
  <div class="item item_rank_1">Object “A”</div>
  <div class="item item_rank_1">Object “B”</div>
  <div class="item item_rank_0">Object “C”</div>
</div>

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

@media (max-width: 600px) {
  .item_rank_0 { display: none; }
}

Казалось бы можно использовать селектор .item_rank_1:last-child, но это так не работает. Псевдо-класс :last-child позволяет найти только одного последнего ребёнка своего родителя.

Но мы уже живём в мире HTML5, где можно грабить корованы и придумывать свои собственные теги (оригинал). По этому вместо того, чтобы отличаться классами, элементы будут отличаются названиями тегов.

<div>
  <item-rank-1 class="item">Object “A”</item-rank-1>
  <item-rank-1 class="item">Object “B”</item-rank-1>
  <item-rank-0 class="item">Object “C”</item-rank-0>
</div>

Да, так тоже можно, если очень хочется.

Теперь в медиа-запросе можно использовать селектор :last-of-type для того, чтобы получить последний элемент группы, которая не была скрыта.

@media (max-width: 600px) {
  item-rank-0.item { display: none; }
  item-rank-1:last-of-type { font-weight: bold; }
}

Таким способом можно создавать любые теги и они будут прекрасно поддаваться оформлению через CSS. Для IE < 9 нужно будет их предварительно создать с помощью JavaScript так как делаем это для новых HTML5 элементов.

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

Живой пример

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

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

Разметка «хлебных крошек» с помощью microdata

«Хлебные крошки» (breadcrumbs) помогают пользователям ориентироваться в структуре сайта. Хотя, стоит заметить, что в современном дизайне их встречаешь всё реже и реже. Но сейчас они могут оказаться полезными не только пользователям, но и поисковикам.

Поисковики и сами прекрасно умеют определять структуру сайта. Google, например, может построить «крошки» от главной страницы сайта до целевой страницы из цепочки ссылок и вывести их в сниппете. Но, лучше всего «хлебные крошки» разметить с помощью microdata. В словаре Data-Vocabulary.org есть специальный тип для этого — Breadcrumb.

<div class="breadcrumbs">
  <span itemscope itemtype="http://data-vocabulary.org/Breadcrumb">
    <a href="http://noteskeeper.ru" itemprop="url">
      <span itemprop="title">Свежие заметки</span>
    </a> &#8250;
    <span itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb">
      <a href="http://noteskeeper.ru/topic/markup/" itemprop="url">
        <span itemprop="title">Вёрстка</span>
      </a> &#8250;
      <span itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb">
        <a href="http://noteskeeper.ru/527/" itemprop="url">
          <span itemprop="title">Вёрстка независимыми блоками</span>
        </a>
      </span>
    </span>
  </span>
</div>

Пример в выдаче Google

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

Формат разметки, возможно, ещё будет меняться. На текущий момент, все объекты типа Breadcrumb неявно связываются между собой. Последовательность в цепочке определяется их последовательностью в документе. Для явного связывания объектов есть поле child. В этом случае объекты должны быть вложены друг в друга (как в моём примере).

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

<ul class="breadcrumbs">
  <li itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
          itemref="breadcrumb-1">
    <a href="http://noteskeeper.ru" itemprop="url">
      <span itemprop="title">Свежие заметки</span>
    </a>
  </li >
  <li itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
          id="breadcrumb-1" itemref="breadcrumb-2">
    <a href="http://noteskeeper.ru/topic/markup/" itemprop="url">
      <span itemprop="title">Вёрстка</span>
    </a>
  </li>
  <li itemprop="child" itemscope itemtype="http://data-vocabulary.org/Breadcrumb"
          id="breadcrumb-2">
    <a href="http://noteskeeper.ru/527/" itemprop="url">
      <span itemprop="title">Вёрстка независимыми блоками</span>
    </a>
  </li>
</ul>

Технически, получилась точно такая же структура — чётко связанный список «крошек». Осталось только проверить на практике, как к нему отнесётся Google.

Обновление: Гугл нормально связал между собой крошки, через аттрибут itemref.

Ну, и напоследок напомню, что если «хлебные крошки» всё же не желательны в дизайне страницы, то их можно спрятать классом «visuallyhidden». Поисковик прекрасно воспринимает контент, скрытый такой техникой.

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

Вложенные объекты microdata

Связывание нескольких вложенных в друг друга объектов microdata происходит через атрибут itemprop.

<article itemscope itemtype="http://schema.org/CreativeWork">
  <aside itemprop="author" itemscope itemtype="http://schema.org/Person">
    <link itemprop="url" href="http://noteskeeper.ru/about/">
    <span itemprop="name">Владимир Кузнецов</span>
  </aside>
  <link itemprop="url" href="http://noteskeeper.ru/811/">
  <h1 itemprop="name">Вложенные объекты microdata</h1>
</article>

Получаем такую структуру:

creativework
  itemType = http://schema.org/CreativeWork
  author
    person
      itemType = http://schema.org/Person
      url = http://noteskeeper.ru/about/
      name = Владимир Кузнецов
  url = http://noteskeeper.ru/811/
  name = Вложенные объекты microdata

Если убрать атрибут itemprop="author", то объект CreativeWork потеряет связь с Person и тот, в свою очередь, переместится на корневой уровень в иерархии объектов, хоть, фактически, останется на том же самом месте в документе.

creativework
  itemType = http://schema.org/CreativeWork
  url = http://noteskeeper.ru/811/
  name = Вложенные объекты microdata

person
  itemType = http://schema.org/Person
  url = http://noteskeeper.ru/about/
  name = Владимир Кузнецов

Очевидно, что элементы, которые будут находиться внутри Person для объекта CreativeWork будут не доступны.

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

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

Экспериментируя с микроразметкой и отслеживая реакцию поисковиков на это, я пришел к печальному выводу, что Google не очень-то любит так называемые обратные связи. Если, например, вы размечаете AggregateRating, то он должен быть частью структуры CreativeWork.

Хорошая разметка:

<div itemscope itemtype="http://schema.org/CreativeWork">
  <link itemprop="url" href="http://noteskeeper.ru/807/">
  <meta itemprop="name" content="Обратные связи в микроразметке microdata">
  <span itemprop="aggregateRating" itemscope
      itemtype="http://schema.org/AggregateRating">
    Оценка читателей <span itemprop="ratingValue">4</span> на основе
    <span itemprop="reviewCount">1</span> комментария
  </span>
</div>

Не совсем хорошая разметка:

<div itemscope itemtype="http://schema.org/AggregateRating">
  <span itemprop="itemReviewed" itemscope
      itemtype="http://schema.org/CreativeWork">
    <link itemprop="url" href="http://noteskeeper.ru/807/">
    <meta itemprop="name" content="Обратные связи в микроразметке microdata">
  </span>
  Оценка читателей <span itemprop="ratingValue">4</span> на основе
  <span itemprop="reviewCount">1</span> комментария
</div>

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

Есть подозрение, что по тем же причинам Google игнорирует объекты, соединённые через itemref. Хоть валидаторы и формируют правильную структуру, но поисковик эту связь почему-то не учитывает. Хочется верить в то, что это временное явление.

Оставте свой комментарий