Кристиан И. Колсериу · 11 ноября 2010 г.
В моей последней статье я реализовал настраиваемый, совместимый с различными браузерами видео-плеер, использующий элемент <video> из HTML5 (http://dev.opera.com/articles/view/custom-html5-video-player-with-css3-and-jquery/). Он является хорошим решеним по многим причинам, включая доступность – элемент <video> в HTML5 значительно более доступен, чем альтернативные решения на основе плагинов, например, с точки зрения готовой клавиатурной доступности, и легкости настройки, не требуя при этом дорогостоящего IDE.
Но на этом работа не заканчивается. Построенное до сих пор решение, как и другие виджеты на основе JavaScript, по-прежнему имеет ряд проблем доступности с точки зрения семантики и возможности обнаружения, что можно было бы рассмотреть с помощью спецификации
В этой статье я покажу, как решать такие проблемы с помощью
До появления в HTML5 собственных элементов <video> и <audio>, все мультимедийные возможности, применяемые в Web, полагались в браузерах на плагины сторонних разработчиков. Эти плагины создают множество проблем для доступности, от трудностей с клавиатурной навигацией до отсутствия непосредственной доступности для вспомогательных технологий.
Решения для этих проблем постоянно развиваются создателями и разработчиками плагинов, но они не являются стандартными, и не очень широко распространены.
Все последние версии популярных Web-браузеров поддерживают элементы <audio> и <video>, и каждый браузер предоставляет набор кнопок управления для этих элементов. Проблема в том, что не все браузеры предоставляют клавиатурный доступ к этим элементам управления:
(рис 5.1) Собственные элементы управления видео в браузере Opera 10.63, с фокусом на кнопке громкости
Решением этих проблем является создание своего собственного плеера мультимедиа, и отображение доступных с клавиатуры элементов управления, используя API элементов медиа (http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#media-elements).
В этой статье мы сосредоточимся на адаптации специального видео-плеера из моей последней статьи с помощью
Наш плагин
Исходная разметка для элементов управления выглядит следующим образом:
<div class="ghinda-video-controls">
<a class="ghinda-video-play" title="Play/Pause"></a>
<div class="ghinda-video-seek"></div>
<div class="ghinda-video-timer">00:00</div>
<div class="ghinda-volume-box">
<div class="ghinda-volume-slider"></div>
<a class="ghinda-volume-button" title="Mute/Unmute"></a>
</div>
</div>
Первая версия была по большей части проверкой концепции, и разметка, конечно, может быть улучшена в смысле семантики и доступности. Давайте перепишем это сейчас, используя более содержательные элементы для каждого элемента управления:
<div class="acorn-video-controls">
<button class="acorn-play-button" title="Start the playback" aria-controls="video1">Play</button>
<input type="range" class="acorn-seek-slider" title="Video seek control" value="0" min="0"
max="150" step="0.1" aria-controls="video1"/>
<span class="acorn-timer">00:00</span>
<div class="acorn-volume-box">
<button class="acorn-volume-button" title="Mute volume" aria-controls="video1">Mute</button>
<input type="range" class="acorn-volume-slider" title="" value="1" min="0"
max="1" step="0.05" aria-controls="video1"/>
</div>
</div>
Прежде всего, мы заменили элементы <a>, которые ведут себя как кнопки, на реальные элементы <button>, так что разметка показывает их смысл, а считыватели экрана интерпретируют их правильно.
Вместо бессодержательных <div> для ползунков, мы используем собственные ползунки HTML5: <input type="range">.
Можно также видеть новый атрибут, кроме обычно используемых. Атрибут aria-controls является частью спецификации video1, но в реальном продукте мы бы использовали ID для видео, или сгенерировали какое-то уникальное значение, если значение не было бы предоставлено. Основная разметка выглядит как на Рисунке 5.2.
(рис 5.2) Элементы управления индивидуального видео плеера с новой разметкой
Если вы не знакомы с
Все наши кнопки имеют содержательные метки и атрибуты title. Атрибуты title предоставляют также всплывающие подсказки, которые будут особенно полезны, если разработчик решит показывать только кнопки, без меток. Они также полезны для пользователей считывателей экрана, предоставляя им более длинное объяснение, что реально делает элемент управления.
В предыдущей разметке можно увидеть, что метка для кнопки воспроизведения Play была "Play/Pause", а метка для кнопки приглушения
Мы также добавляем различные классы в каждую кнопку после нажатия.
Для ползунков поиска и громкости мы использовали тип элемента формы HTML5 <input type="range">. К сожалению, все не так просто, как мы надеялись:
В связи с этими проблемами мы будем по-прежнему использовать
В предыдущей версии плагина мы вызывали плагин ползунка UI на пустом <div>, но теперь мы собираемся вызывать его на элементе <input>. У нас теперь имеется проблема, так как плагин добавляет классы к ползунку, и присоединяет к нашему элементу другие элементы, такие как регулятор ползунка. Это не будет работать, так как <input> является линейным (inline), поэтому не может включать другие элементы.
Поэтому перед вызовом плагина ползунка мы должны удалить <input>, заменить его элементом <div> (или <p> или любым другим обычным элементом HTML) и вызвать на нем плагин.
При переписывании исходной разметки можно было бы сохранить исходные элементы и добавить к ним роли ARIA, например, добавить role="button" к элементам <a>. Для пользователей вспомогательных технологий это могло бы заставить их вести себя почти как элементы <button>. Однако при таком подходе возникает ряд проблем.
Проблемой могут быть семантические конфликты. Элемент <div>, например, является семантически нейтральным и может получить любую роль, но другие элементы имеют свою собственную внутреннюю семантику, и добавление к ним случайных ролей может приводить к конфликту между ролью ARIA, пытающейся описать элемент, и его врожденной семантикой.
В связи с этими конфликтами была создана концепция "неявной семантики ARIA". Таким способом спецификация описывает примененную семантику, и ограничение внутренней семантики для большинства элементов. Например, элементы <article>, <aside> и <section> имеют следующую примененную семантику: "article role", "note role" и "region role".
Использование элементов с собственной ролью неизмеримо лучше, чем его "подделка", если такой элемент доступен. Об этом можно дополнительно почитать в спецификации HTML5 в разделе, который связан с
Возвращаясь к плееру, мы теперь разрешили проблемы с кнопками, но, принимая во внимание, что мы не всегда используем собственные ползунки браузера, нам по-прежнему требуется кое-что сделать с ползунками
Ползунки UI предоставляют отличную клавиатурную доступность, но не имеют никакой поддержки ARIA (в версии 1.8.4). И, считая, что ползунок является элементом <a>, указывающим на href="#" в том же документе, считыватели экрана будут интерпретировать это как посещенную ссылку, а не как управляющий ползунок. Чтобы обойти эту проблему, мы создадим небольшую функцию, чтобы добавить в ползунок ARIA и возможность получать фокус.
Чтобы элемент интерпретировался вспомогательными технологиями как ползунок, необходимо использовать на элементе роль . Но ползунок состоит из двух элементов: регулятора и направляющей. ARIA требует соединения с атрибутом только одного элемента, поэтому надо выбрать, какой использовать.
С учетом того, что только один элемент (для ползунка) должен получить фокус, и UI
Все элементы формы должны опознаваться пользователями, поэтому нужно пометить ползунок. Для этого можно использовать различные подходы. Можно было бы использовать атрибут aria-labelledby, использовать элемент <label> и заставить его указывать на ползунок, или использовать атрибут title. Для нашего случая атрибут title является, вероятно, лучшим решением.
Кроме атрибута role, ползунок ARIA требует следующие атрибуты:
aria-valuenow
Текущее значение ползунка.
aria-value-min
Минимальное значение.
aria-value-max
Максимальное значение, которое может иметь ползунок.
По умолчанию вспомогательные технологии используют атрибут aria-valuenow, но значение должно быть числом – если бы мы хотели предоставить пользователю более удобное для восприятия значение, мы могли бы использовать вместо этого другое свойство ARIA, aria-valuetext. Это значение является строкой, поэтому, например, когда пользователь использует ползунок поиска, то вместо вывода значения "23", можно было бы выводить "23 секунды".
С учетом всего здесь рассмотренного, наша функция выглядит следующим образом:
var initSliderAccess = function (elem, opts) {
var accessDefaults = {
'role': 'slider',
'aria-valuenow': parseInt(opts.value),
'aria-valuemin': parseInt(opts.min),
'aria-valuemax': parseInt(opts.max),
'aria-valuetext': opts.valuetext,
'tabindex': '0'
};
elem.attr(accessDefaults);
};
Эта функция адаптирована для тех средств, которые используются при вызове ползунка UI
Можно видеть, что кроме атрибутов . Это делается, чтобы гарантировать, что регулятор будет доступен с клавиатуры. Мы используем также функцию parseInt() на некоторых значениях, чтобы не передавать в систему вспомогательных технологий такие значения, как "1.54321".
В предыдущей статье, чтобы фактически заставить ползунки управлять видеоплеером, мы присоединили функции к порождаемым и stop.
И так как мы всегда хотим информировать пользователя о текущем значении ползунка, мы должны изменить значение "aria-valuenow" и "aria-valuetext" на .
Мы добавим небольшой код в функции. Для ползунка поиска мы будем использовать следующий код:
sliderUI.attr("aria-valuenow", parseInt(ui.value));
sliderUI.attr("aria-valuetext", ariaTimeFormat(ui.value));
Объект sliderUI является элементом регулятора ползунка, а ui.value является текущим значением ползунка, возвращаемым плагином. Можно видеть, что мы используем для атрибута aria-valuetext другую функцию, функцию ariaTimeFormat().
Вот как выглядит функция ariaTimeFormat():
var ariaTimeFormat = function(sec) {
var m = Math.floor(sec/60)<10?"" + Math.floor(sec/60):Math.floor(sec/60);
var s = Math.floor(sec-(m*60))<10?"" + Math.floor(sec-(m*60)):Math.floor(sec-(m*60));
var formatedTime;
var min = 'minutes';
var sec = 'seconds';
if(m==1) min = 'minute';
if(s==1) sec = 'second';
if (m!=0) {
formatedTime = m + ' ' + min + ' ' + s + ' ' + sec;
} else {
formatedTime = s + ' ' + sec;
};
return formatedTime;
};
Эта функция получает текущее значение ползунка (число секунд, где расположен регулятор) и преобразует его в понятную человеку строку. Если ползунок расположен на 72 (секунде), например, функция вернет "1 minute 12 seconds".
Это особенно полезно для длинных видеофильмов, где без такой функции смещение указателя поиска на 25 минут будет сообщаться как 1500 секунд, делая это достаточно неудобным, особенно для пользователей считывателей экрана.
Мы будем использовать ту же самую функцию ARIAfication на ползунке громкости, чтобы сделать его доступным, и модифицируем функцию изменения громкости, чтобы изменять значение aria-valuenow и aria-valuetext, когда ползунок перемещается:
video.$volume.$handle.attr("aria-valuenow", Math.round(volume*100));
video.$volume.$handle.attr("aria-valuetext", Math.round(volume*100) + ' percent');
Можно видеть, что мы умножаем наше значение на 100. Это связано с тем, что значение громкости находится между 0 и 1, а не между 0 и 100. Также больше смысла имеет предоставление пользователю "процентного" значения для громкости, а не однозначное число, именно поэтому мы добавляем символ процентов к aria-valuetext, а также умножаем и округляем значение.
Как крайне важное свойство доступности, каждый плеер видео/мультимедиа должен предоставлять поддержку для титров и/или стенограмм. К сожалению, текущая спецификация W3C HTML5 не содержит ничего с этим связанного. С другой стороны спецификация WHATWG HTML5 (да, существует две спецификации HTML5) недавно добавила элемент <track> для титров. Это "позволяет авторам определить явные внешние размеченные по времени дорожки для элементов медиа ". Поэтому, по существу, вы можете определить внешний файл, содержащий титры, подзаголовки, описания или другие размеченные по времени дорожки. Можно определить несколько элементов <track> для различных дорожек, таких как различные языки.
Текущий формат файла называется WebSRT (http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#websrt), и является, по сути, улучшенной версией формата
Можно прочитать больше об этом в спецификации WHATWG HTML5 (http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-track-element).
Этот элемент отсутствует в спецификации W3C, в связи с "политическими" проблемами, в основном, потому что текущий предложенный формат (WebSRT) конфликтует с примерно 50 другими форматами, включая форматы W3C, такие как smilText и
Одной из основных проблем является то, что он не реализован сейчас ни в одном браузере, но не бойтесь – мы собираемся реализовать элемент самостоятельно, используя JavaScript. Это лучший защищенный от будущих изменений способ использовать титры сейчас. Когда однажды проблемы разрешатся, и браузеры реализуют элемент, нам не понадобиться изменять существующую разметку для использования собственных титров.
Техника, которую мы собираемся использовать, является в какой-то степени обратной версией техники создания титров Брюса Лоусона. Если вы не знакомы с ней, прочтите статью Улучшение доступности видео в HTML5 с помощью титров на основе JavaScript (http://dev.opera.com/articles/view/accessible-html5-video-with-javascripted-captions/) на сайте Dev Opera.
Описанная в этой статье техника описывает использование разметки HTML для определения титров, использование специальных атрибутов data для задания смещения времени для каждого титра. Затем выполняется синтаксический анализ элементов и создается объект JavaScript, который используется для отображения каждого титра в правильное время. Она использует также генерируемый с помощью CSS контент для вставки отметок времени в контент.
Мы собираемся изменить технику на прямо противоположную, и интерпретировать элементы <track> таким же образом, как может браузер.
Прежде всего, нам потребуется интерпретатор для файлов, так как мы собираемся использовать анализатор
Теперь в функции инициирования титров мы собираемся найти элементы <track>. Если мы найдем более одного элемента, мы сгенерируем разметку и позволим пользователю выбирать титры из списка. Спецификация включает атрибут label для элемента <track>, определяя его как "читаемый пользователем заголовок дорожки", который "используется
<ul>
<li>
<label>
<input type="radio" name="acornCaptions" checked="checked" />
None
</label>
</li>
<li>
<label>
<input type="radio" name="acornCaptions" />
English <!-- это атрибут "label" элемента <track> -->
</label>
</li>
<li>
<label>
<input type="radio" name="acornCaptions" />
Romani <!-- это атрибут "label" элемента <track> -->
</label>
</li>
</ul>
Мы используем неупорядоченный список с кнопками <input type="radio">.
Мы помещаем элементы <input> и текст в элементы <label>, чтобы вспомогательные технологии ассоциировали метку с кнопкой, не определяя <label> отдельно и присваивая ему уникальный ID и кнопку.
Реальный текст метки является контентом атрибута label элемента <track>.
Когда пользователь выбирает титры, мы загружаем их с помощью вызова Ajax, анализируем, создаем с каждым титром привязанный к времени объект, и генерируем также стенограмму.
Это звучит сложнее, чем является в действительности.
$.ajax({
url: url,
success: function(data) {
// используем анализатор SRT на загруженных данных
captions = parseSrt(data);
// находим управляющую кнопку стенограммы и отображаем ее
video.$transcript = video.$container.next('.acorn-transcript');
video.$transcriptBtn.show();
// генерируем разметку для стенограммы и добавляем ее
var transcriptText = '';
$(captions).each(function() {
transcriptText += '' + this.content.replace("'","") + '';
});
video.$transcript.html(transcriptText);
captionsActive = true;
video.$captions.show();
// в случае паузы видео и
// при отключенном timeUpdate,
// мы будем updateCaption (обновлять титры) вручную
if(video.$self.attr('paused')) updateCaption();
video.$captionsBtn.addClass('acorn-captions-active').removeClass('acorn-captions-loading');
},
error: function() {
// в случае ошибки при загрузке титров
// не отображайте ничего и покажите сообщение
// об ошибке, если присутствует консоль
captionsActive = false;
captions = '';
video.$transcriptBtn.hide();
video.$captionsBtn.removeClass('acorn-captions-active').removeClass('acorn-captions-loading');
if(console) console.log('Error loading captions');
}
});
Код достаточно просто понять, поэтому я собираюсь сосредоточиться больше на стенограммах. Можно видеть, что мы генерируем разметку и используем функцию html(), чтобы добавить их в контейнер стенограмм. Мы используем такую же разметку, как и техника Брюса Лоусона, но теперь для стенограммы, а не для титров.
Таким образом, мы генерируем стенограмму на основе титров, и можем иметь столько версий стенограммы, сколько имеется версий титров.
Большинство доступных плееров мультимедиа и accesskey, либо присваивая сложные комбинации клавиш с помощью JavaScript. Хотя это может показаться отличным решением для большинства разработчиков, обследования и примеры использования говорят об обратном.
Это "свойство", нацеленное на улучшение доступа к страницам и приложениям, оказались плохо спроектированными и реализованными, создавая путаницу у пользователей вспомогательных технологий, а не помогая им.
Именно поэтому я предпочел вообще не реализовывать клавиши доступа, и сделать перемещение по элементам управления плеера с помощью стандартной навигации с помощью клавиши TAB.
Наш плагин берет стандартный элемент <video> из HTML5 и создает для него доступные элементы управления, поддержку титров и стенограммы, и другие средства, но он не предоставляет никакой поддержки для описания видео. Это необходимо сделать без использования плагина.
Я предлагаю использовать элемент < из HTML5 вместе с <figcaption>, и использовать атрибут aria-describedby для соединения элемента <video> с описанием. В этом случае, когда, например, считыватель экрана достигнет видео, он получит также его описание.
<figure>
<video controls="controls" width="300" height="200" preload="metadata" aria-describedby="videodescription">
<source src="/path/to/video.webm" />
<track src="/path/to/caption.srt" kind="captions" srclang="rom" label="Romani" />
</video>
<figcaption id="videodescription">
Трейлер короткого анимационного фильма "Sintel", проекта Durian Open Movie компании Blender Foundation.
Дополнительная информация на сайте http://durian.blender.org. Это описание видео.
</figcaption>
</figure>
Также как и в предыдущей статье о видео плеере, я не собираюсь создавать механизм запасного варианта, так как каждый метод запасного варианта имеет свои собственные проблемы доступности, будет ли это Flash, Java, Silverlight или что-то другое. Разработчик должен определить для себя лучший подход.
Посмотрите демонстрационный пример видео плеера HTML5 с повышенной доступностью (http://dev.opera.com/articles/view/more-accessible-html5-video-player/demos.html), дополненный всем, что было описано в этой статье.
Описанные в этих статьях технические приемы для элемента <video> из HTML5 привели к созданию готового к реальному использованию плагина
По мере того как HTML5 переносится на все большее количество платформ и устройств, крайне важно, чтобы разработчики и создатели учитывали проблемы доступности во время реализации новых свойств, а не впоследствии, после их создания.
Дополнительная информация об элементе <video>
Учебные материалы компании Opera по доступности
Ресурсы
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.