Статьи

Практическая доступность JavaScript

Эта статья покажет вам несколько простых вещей, которые вы можете сделать прямо сейчас , чтобы сделать ваш JavaScript более доступным. Это не передовая технология, а то, чем мы занимаемся годами. Этот пост расширяет нашу вводную статью « Доступность JavaScript 101 ».

Три основных принципа

Доступность JavaScript сводится к трем основным принципам:

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

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

Текстовая интерпретация

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

Зрячий пользователь не всегда подвержен семантике — меню — это меню, потому что оно похоже на меню, независимо от того, построено оно из списка или из <div> . Но слепой пользователь может понять только то, что может интерпретировать его программа чтения с экрана — меню — это меню, потому что это иерархия структурированных ссылок. Например, если бы мы создавали вкладку, она могла бы выглядеть следующим образом. Обратите внимание, как используются различные семантические теги.

 <div class="box"> <section class="panel"> <h3 class="tab" tabindex="0">Section 1</h3> <p class="content">This is the content for Section 1</p> </section> <section class="panel"> <h3 class="tab" tabindex="0">Section 2</h3> <p class="content">This is the content for Section 2</p> </section> </div> 

Также важно сделать информацию программно доступной. Это означает использование стандартных функций DOM для добавления нового содержимого на страницу, а не использование document.write() или innerHTML .

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

Чтобы решить эту проблему, добавьте HTML через промежуточный элемент без добавления, как показано ниже. Чтобы использовать эту функцию, передайте ей строку HTML и ссылку на целевой элемент. Функция создает виртуальный DOM, затем добавляет свой узел (узлы) к цели.

 function appendHTML(target, html) { var fragment = document.createElement('div'); fragment.innerHTML = html; while(fragment.hasChildNodes()) { target.appendChild(fragment.firstChild); } return target; } в function appendHTML(target, html) { var fragment = document.createElement('div'); fragment.innerHTML = html; while(fragment.hasChildNodes()) { target.appendChild(fragment.firstChild); } return target; } 

Доступность клавиатуры

Делая интерактивный контент доступным для клавиатуры, придерживайтесь основного набора клавиш: Tab , Enter , четыре клавиши со стрелками и Escape . Эти клавиши должны использоваться без изменений, т.е. без необходимости удерживать нажатой клавишу Shift или другие клавиши-модификаторы. Если вам действительно нужно использовать другие клавиши или нажатия клавиш-модификаторов, вы должны предоставить инструкции пользователю. Хотя лучше избегать комбинаций Alt , потому что они используются для ярлыков собственного меню.

Большинство ключей также имеют действие браузера по умолчанию, и иногда необходимо заблокировать значение по умолчанию, чтобы предотвратить поведенческий конфликт. Например, при использовании клавиш « Стрелка вверх» и «Стрелка вниз» в раскрывающемся меню не требуется, чтобы они одновременно прокручивали страницу. Стандартный способ сделать это состоит в том, чтобы использовать preventDefault() , как в этом примере, взятом из сценария меню:

 menu.addEventListener('keydown', function(e) { if(/^(3[789]|40)$/.test(e.keyCode.toString())) { switch(e.keyCode) { case 37: //... handle left-arrow break; case 38: //... handle up-arrow break; case 39: //... handle right-arrow break; case 40: //... handle down-arrow break; } e.preventDefault(); } }, false); 

Если keyCode соответствует keyCode со стрелкой, функция обрабатывает клавишу, как применимо, и затем вызывает preventDefault() . Если нажать любую другую клавишу, событие игнорируется, сохраняя поведение по умолчанию. Будьте осторожны, никогда не блокируйте клавишу Tab , иначе вы поймаете внимание пользователя!

Обратите внимание, что в приведенном выше keydown используется keypress , а не keypress , поскольку большинство браузеров не (и не должны) keypress событие keypress клавиш для управляющих клавиш. Событие keydown также запускается постоянно, если удерживать клавишу нажатой, поэтому могут быть ситуации, когда вы предпочитаете использовать keyup вместо этого — который не повторяется, но не может блокировать действия по умолчанию.

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

Богатая подсказка, расположенная над связанным термином.

Богатая подсказка, расположенная над связанным термином.

Вы можете видеть, как основной текст всплывающей подсказки окружен квадратными скобками, а ссылки внизу имеют квадратные скобки и символ-разделитель. Текст также обернут в <em> чтобы добавить смысловой акцент. Когда CSS отключен, содержимое выглядит так:

Та же самая всплывающая подсказка просматривалась без CSS, показывая объяснение в виде текста в скобках.

Та же самая всплывающая подсказка просматривалась без CSS, показывая объяснение в виде текста в скобках.

Вот HTML-код для этого примера:

 <blockquote> <p> Assistive technologies can derive information from their attributes and text; for example, a dynamic menu would be made using links organised into nested lists, in which the menu levels are denoted by the hierarchy, and by the use of <span id="context"> <a href="http://www.maxdesign.com.au/2006/01/17/about-structural-labels/" title="descriptive headings used to indicate the main components of a web page, such as global site navigation, local navigation and footer information"> structural labels </a> </span> around each top-level link. </p> </blockquote> 

<span> вокруг ссылки обеспечивает цель вставки, поэтому всплывающую подсказку можно добавить сразу после ссылки; это также обеспечивает относительный контекст для абсолютного позиционирования подсказки:

 #context { position:relative; } #context > span.tooltip { position:absolute; bottom:1.7em; right:0; } #context > span.tooltip { width:18em; padding:6px 8px; white-space:normal; border:1px solid #555; font:normal normal normal 0.85em/1.4 arial,sans-serif; text-align:right; background:#ffd; box-shadow:1px 2px 3px -1px rgba(0,0,0,0.5); } #context > span.tooltip > em { display:block; padding:4px 4px 8px 4px; text-align:left; font-style:normal; } 

Вот JavaScript для этого примера:

 var infotext, tooltip = null, context = document.getElementById('context'), trigger = context.getElementsByTagName('a').item(0); trigger.addEventListener('click', function(e) { if(tooltip === null) { infotext = trigger.getAttribute('title'); trigger.removeAttribute('title'); tooltip = document.createElement('span'); tooltip.className = 'tooltip'; var info = tooltip.appendChild(document.createElement('em')); info.appendChild(document.createTextNode(' (' + infotext + ') ')); tooltip.appendChild(document.createTextNode('[ ')); var more = tooltip.appendChild(document.createElement('a')); more.setAttribute('href', trigger.getAttribute('href')); more.appendChild(document.createTextNode('reference')); tooltip.appendChild(document.createTextNode(' | ')); var google = tooltip.appendChild(document.createElement('a')); google.setAttribute('href', 'http://www.google.com/search?q=Structural+Labels'); google.appendChild(document.createTextNode('search')); tooltip.appendChild(document.createTextNode(' ]')); tooltip = context.appendChild(tooltip); } else { trigger.setAttribute('title', infotext); context.removeChild(tooltip); tooltip = null; } e.preventDefault(); }, false); 

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

Контроль над временной деятельностью

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

 <div id="ticker"> <ol> <li><a href="...">This is the first news item</a></li> <li><a href="...">This is the second news item</a></li> </ol> </div> <div id="buttons"> <button id="pause-button" type="button">Pause</button> <button id="expand-button" type="button">Expand</button> </div> 

Разметка является упорядоченным списком, потому что новости обычно упорядочиваются вовремя, с последними заголовками вверху. CSS для этого примера будет выглядеть примерно так:

 #buttons { display:none; } #buttons.script-enabled { display:block; } #ticker.script-enabled, #ticker.script-enabled > ol { position:relative; } #ticker { white-space:nowrap; overflow:hidden; margin:5px 0 0 0; padding:5px 10px; border:2px solid #555; background:#f2f2f2; } #ticker.script-enabled > ol, #ticker.script-enabled > ol > li { margin:0; padding:0; list-style-type:none; } #ticker.script-enabled > ol > li { display:inline-block; margin-right:20px; } #ticker.script-enabled.expanded, #ticker.script-enabled.expanded > ol { position:static; } #ticker.script-enabled.expanded { white-space:normal; overflow:hidden; } 

Хотя следующий JavaScript объединяет все это:

 var container = document.getElementById('ticker'), list = container.getElementsByTagName('ol').item(0), buttons = document.getElementById('buttons'), pauser = document.getElementById('pause-button'), expander = document.getElementById('expand-button'); buttons.className = 'script-enabled'; container.className = 'script-enabled'; var scrollwidth = 0; var items = list.getElementsByTagName('li'); for(var i = 0; i < items.length; i ++) { scrollwidth += items[i].offsetWidth + 20; } var scrollstart = container.offsetWidth; list.style.left = scrollstart + 'px'; var timer = null, moving = false, scrolloffset = scrollstart; function pause() { moving = false; window.clearInterval(timer); timer = null; } function resume() { moving = true; timer = window.setInterval(function() { scrolloffset -= 5; if(scrolloffset < (0 - scrollwidth)) { scrolloffset = scrollstart; } list.style.left = scrolloffset + 'px'; }, 100); } pauser.addEventListener('click', function() { if(moving) { pause(); pauser.firstChild.nodeValue = 'Resume'; } else { resume(); pauser.firstChild.nodeValue = 'Pause'; } }, false); expander.addEventListener('click', function() { pause(); container.className = 'expanded'; pauser.parentNode.removeChild(pauser); expander.parentNode.removeChild(expander); }, false); resume(); 

Важно отметить, что контейнер buttons по умолчанию скрыт и отображается только при добавлении класса с script-enabled . Это делается для того, чтобы при просмотре страницы без JavaScript у пользователя не осталось кнопок, которые ничего не делают.

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

Кнопка « Pause останавливает прокрутку, чтобы вы могли прочитать каждый элемент в свое время, затем меняется на « Resume чтобы вы могли запустить его снова. Кнопка « Expand также останавливает прокрутку, но затем добавляет expanded класс, который переопределяет overflow и другие стили макета. Это заставляет контент превращаться в статический список ссылок.

Вывод

Это был вихревой тур по практической доступности JavaScript! Но если есть одна вещь, которую я хотел бы отнять, это то, что доступность JavaScript не сложна . Требуется лишь немного внимания к трем основным принципам. В следующий раз я tabindex к более продвинутым методам, таким как tabindex , перетаскивание и доступный Ajax!