Заметки за сентябрь 2013 года

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

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

<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) или состоянии, так как у него нет другого способа передать эту информацию пользователю.

Заключение

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

Операции над коллекциями объектов Backbone.js

Для работы с коллекциями в Backbone.js импортируется 28 методов из библиотеки Underscore.js. Среди них, пожалуй, самыми востребованными являются .forEach(), .filter(), .reject(), .find(), .without().

Предположим, что нужно найти в коллекции элементы, соответствующие определённому условию и уничтожить их.

var boxes = new Boxes(); // коллекция объектов
// предположим, что она уже заполнена нужными данными
var boxesToDestroy = boxes.filter(function (box) {
  return box.isReadyToDestroy();
});
_.forEach(boxesToDestroy, function (box) {
  box.destroy();
});

Выглядит громоздко. Всё это по тому, что методы Underscore.js возвращают массив, а не новый экземпляр коллекции.

Благо в Underscore.js есть метод .chain(), который позволяет всё значительно упростить.

boxes.chain()
  .filter(function (box) { return box.isReadyToDestroy(); })
  .forEach(function (box) { box.destroy(); });

Этот код можно ещё упростить, воспользовавшись методом .invoke().

boxes.chain()
  .filter(function (box) { return box.isReadyToDestroy(); })
  .invoke("destroy");

Так можно стоить цепочки из любых операций над элементами коллекции. Если по завершению цепочки нужно будет сохранить результат выборки в переменную, то последним вызовем метод .value().

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

Заголовок для поля ввода и его доступность

Подпись или заголовок для поля ввода размечается тегом <label>. Наличие связанного с ним элемента <input>, <textarea> или <select> не является обязательным условиям. Хотя, при отсутствии такого элемента, сам заголовок приобретает другой смысл и может быть размечен другим тегом.

<label> может быть как обёрткой для поля ввода, так и быть связанным с ним с помощью атрибутов. Вот несколько типовых вариантов разметки:

Вариант №1:

<label>
  Фамилия <input type="text" name="family-name">
</label>

Вариант №2:

<label for="family-name-field">Фамилия</label>
<input id="family-name-field" type="text" name="family-name">

Клик по надписи в обоих случаях приведёт к том, что связанное с этой надписью поле ввода получит фокус. В первом случае связь инпута и лейбла определяется явно (фокус получает первое дочернее поле ввода). Во втором случае связь осуществляется с помощью атрибута id у поля ввода и аналогично значения у атрибута for лейбла.

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

Подсказка в поле ввода

В современном дизайне отдельно стоящие подписи всё чаще стали заменяться подсказками, которые располагаются прям внутри поля ввода.

<input type="text" placeholder="Фамилия" name="family-name">

Но не стоит путать подсказку и подпись.

Доступность

Но совсем отказываться от <label> нельзя из-за того, что вспомогательные технологи не зачитывают подсказки. Тег нужно оставить в разметке, но скрыть его от пользователя.

<label for="family-name-field" class="hidden">Фамилия</label>
<input id="family-name-field" type="text"
    placeholder="Фамилия" name="family-name">

Скрыв подпись к полю ввода с помощью стиля display: none, мы не решили проблему с доступностью текста подписи. Вспомогательные технологии игнорируют элементы, к которым применён такой стиль.

Вернуть видимость текста можно опять же несколькими способами.

Способ №1

<label>
  <span class="hidden">Фамилия</span>
  <input type="text" placeholder="Фамилия" name="family-name">
</label>

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

Скринридеры понимает, что <label> видим для пользователя и зачитывает его содержимое, не обращая внимания на то, что оно оказывается скрытым.

Способ №2

<label id="family-name-label"
    for="family-name-field" class="hidden">Фамилия</label>
<input aria-labelledby="family-name-label"
    id="family-name-field" type="text"
    placeholder="Фамилия" name="family-name">

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

Как вариант, явно указать подпись можно с помощью атрибута aria-label.

Способ №3

<label for="family-name-field" class="visuallyhidden">Фамилия</label>
<input id="family-name-field" type="text"
    placeholder="Фамилия" name="family-name">

Класс visuallyhidden выключает элемент из потока и делает его размеры 1×1 пиксель. Формально подсказка остаётся видимой и зачитывается ридерами, но фактически её не видно на экране.

У всех способов есть свои плюсы и минусы:

  • первый вариант оказался в итоге самым компактным и универсальным, но появился дополнительный элемент в разметке;
  • второй вариант самый громоздкий (требуется слишком много атрибутов и два уникальных идентификатора), но в то же время и самый надёжный;
  • третий вариант выглядит хорошим компромиссом, если не позаботиться о том, чтобы id инпута не повторялся на странице.
Комментарии к заметке: 2

Истинные размеры изображения

Когда картинка появилась на странице, то к ней сразу же начинают применяться стили и узнать её истинные габариты через свойства width и height уже нельзя.

Во всех современных браузерах (включая IE9 и старше) есть свойства naturalWidth и naturalHeight, которые после полной загрузки картинки принимают значения ширины и высоты изображения, соответственно.

var img = document.getElementById("gallery-image"),
    width = img.naturalWidth,
    height = img.naturalHeight;

В IE8 и младше этих свойств нет. Получить не модифицированные размеры можно, если создать новый элемент с тем же самым значением атрибута src и уже у него прочитать свойства width и height.

function getDimensions(sourceImage) {
    var img = new Image();
    img.src = sourceImage.src;
    return {
        width: img.width,
        height: img.height
    };
}
Комментарии к заметке: 2

Ридер для документации популярных проектов и технологий

DevDocs представляет собой оболочку для просмотра документации популярных проектов: Angular.js, Backbone.js, Ember.js, jQuery, jQuery UI, jQuery Mobile, Lo-Dash, Underscore.js. Так же в проекте есть документация по самым базовым технологиям: HTML, CSS, JavaScript, HTTP, и проектами, созданных на их основе: Node.js, Less, Sass.

Ридер имеет удобный быстрый поиск по выбранным проектам и продуманную навигацию с клавиатуры.

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