Статьи

Ajax и Screenreaders: когда это может работать?

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

Google действительно дает некоторые результаты, в частности, статью «AJAX и доступность» в Standard-Schmandards , в которой говорится о том, что приложения работают без JavaScript, а также обсуждается идея использования диалогового окна предупреждений для передачи информации на программы чтения с экрана; но из тона статьи ясно, что автор только догадывается, что этот подход будет работать (как мы увидим позже, возможно, нет). Саймон Уиллисон поднимает тему в блоге SitePoint , но там он говорит о доступности только с точки зрения поддержки JavaScript.

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

В статье «Доступность приложений AJAX (часть 1)» в WebAIM рассматривается этот момент, объясняется, что для обеспечения доступности динамических изменений интерфейса приложение должно активно информировать пользователя о том, что произошло изменение, а затем разрешить прямой доступ к новому контенту. , Это так далеко, как говорится в статье — пока ничего не говорится о том, как это на самом деле может быть сделано. Он уверенно обещает решения во второй части, но обещать легко!

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

И именно поэтому я написал эту статью: представить некоторые данные и анализ, которые я скомпилировал, и посмотреть, не указывает ли это на полезный вывод.

Немного предыстории …

За последние несколько месяцев (и ранее) я принимал участие в исследовании того, как ведущие программы чтения с экрана и другие вспомогательные устройства реагируют на JavaScript: на какие события они генерируют или на что реагируют и при каких обстоятельствах. Исследование основано на Access Matters и координируется Бобом Истоном, Дереком Фезерстоуном, Майком Стенхаусом и мной.

В дополнение к этому, я провел большое количество первичных исследований для своей недавно опубликованной книги «Антология JavaScript» . Исследование было разработано, чтобы выяснить, как вспомогательные устройства реагируют на сценарии, которые периодически или асинхронно обновляют DOM, такие как элементы прокрутки новостей или ответы на XMLHttpRequest.

Мы обнаружили, что поддержка скриптов в программах чтения с экрана невероятно неустойчива и фрагментарна, но это даже не самая большая проблема! Существуют способы и средства, с помощью которых мы можем создавать полезные хуки (например, все проверенные нами программы чтения с экрана генерируют события щелчка по ссылкам и элементам управления формой), но реальная проблема заключается в следующем: как пользователь программы чтения с экрана узнает, что контент изменилось?

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

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

И это вопрос за 64 000 долларов: как нам это сделать? Чтобы ответить на этот вопрос, нам нужно попробовать какую-то другую тактику, а затем увидеть (или, скорее, услышать) результаты!

Тесты

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

Первый тест

Первый тест просто обновляет абзац текста непосредственно под элементом триггера. Вот основной HTML:

<p> 
   <a href="./" id="trigger">This link is the trigger.</a>
</p>

<p id="response">
   This paragraph will update with the response.
</p>

<p>
   This is some text that comes after the response,
   to check continuity.
</p>

А вот и JavaScript:

 window.onload = function() 
{
 var trigger = document.getElementById('trigger');
 var response = document.getElementById('response');

 trigger.onclick = function()
 {
   var request = null;
   if(typeof window.XMLHttpRequest != 'undefined')
   {
     request = new XMLHttpRequest();
   }
   else if(typeof window.ActiveXObject != 'undefined')
   {
     try { request = new ActiveXObject('Microsoft.XMLHTTP'); }
     catch(err) { request = null; }
   }

   if(request != null)
   {
     request.onreadystatechange = function()
     {
       if (request.readyState == 4  
           && /^(200|304)$/.test(request.status.toString()))
       {
         response.innerHTML = request.responseText;
       }
     }

     request.open('GET', 'test.php?msg=Hello+World', true);
     request.send(null);
   }

   return false;
 };
};

Сценарий «test.php» просто выводит сообщение для responseText это могло быть что угодно:

 <?php 

echo "And here's the response - " . $_GET['msg'];

?>

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

Результаты первого теста

Все устройства запускают функцию и большинство обновляют ответный абзац, но ни одно устройство не считывает его автоматически (как и ожидалось). Этот тест просто используется, чтобы убедиться, что обновление контента общепризнано, но, к сожалению, это не так: Windows Eyes не обновляет свой речевой вывод до тех пор, пока не сработает ссылка запуска, что не произойдет, если мы просто позволим читателю читай дальше. Таким образом, в зависимости от взаимодействия с пользователем, они могут вообще не услышать обновленное сообщение.

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

Второй тест

Второй тест почти такой же, как первый, но на этот раз мы предпримем дополнительный шаг, установив document.location Вот дополнение к функции onreadystatechange

 request.onreadystatechange = function() 
{
 if (request.readyState == 4
     && /^(200|304)$/.test(request.status.toString()))
 {
   response.innerHTML = request.responseText;
   document.location = '#response';
 }
}

Результаты второго теста

Эти результаты более запутанные:

  • В Home Page Reader 3.02 текст ответа автоматически считывается, но читатель на этом не останавливается: он продолжает читать остальную часть страницы. Это сделало бы его приемлемым вариантом, если элемент response находится в самом конце страницы.
  • В Home Page Reader 3.04 (обратите внимание, более свежая версия) настройка местоположения больше не работает правильно. Читатель возвращается к началу страницы, а не к абзацу с ответом (я также попробовал это с помощью location.replace, чтобы увидеть, изменится ли это, но это не так).
  • В Hal 6.5 и Connect Outloud 2.0 читатель объявляет о новой загрузке страницы, но затем начинает чтение с элемента после ответа, полностью пропуская ответ.
  • В JAWS 5.0 и 6.2 код не работает, а иногда вообще ничего не делает в ответ; в других случаях он снова перечитывает текст ссылки триггера или заголовок верхнего уровня; иногда он ведет себя так же, как Hal и Connect Outloud.
  • В Windows Eyes 5.0 контент обновляется! Но помимо этого, он ведет себя так, как будто Home Page Reader 3.02: он объявляет о новой загрузке страницы, затем начинает читать (и включая) элемент response. Но это поведение не то, чем кажется: устройство работает так только потому, что Windows Eyes запоминает вашу предыдущую позицию при загрузке страницы, которую вы посещали ранее, и поскольку ответ приходит сразу после триггера, это следующее, что вы услышите , Если бы это было не так, он просто прочитал бы то, что было непосредственно после триггера.
  • Windows Eyes 5.5 (бета) ведет себя точно так же, как Hal и Connect Outloud.

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

 <p> 
   <a name="response" id="response" href="#">
       This link will update with the response.</a>
</p>

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

Результаты для модифицированного второго теста

Обе версии Home Page Reader ведут себя так же, как и раньше, и к ним подключается Connect Outloud, который теперь ведет себя как HPR 3.02 (работает, но продолжает чтение). Обе версии Windows Eyes теперь ведут себя как 5.5 раньше (они начинают читать с элемента после ответа). Но в JAWS и Hal код работает отлично — произносится текст ответа, но дальше ничего не происходит (хотя JAWS может также сначала перечитать заголовок верхнего уровня страницы, прежде чем произносить текст ответа).

Третий тест

В третьем тесте мы заменим настройку местоположения на программный вызов focus () для ссылки ответа, как только ее текст будет обновлен. Новый HTML выглядит так:

 <p> 
   <a href="./" id="response">  
       This link will update with the response.</a>
</p>

Опять же, только небольшая модификация необходима для первоначальной функции onreadystatechange

 request.onreadystatechange = function() 
{
 if (request.readyState == 4
     && /^(200|304)$/.test(request.status.toString()))
 {
   response.innerHTML = request.responseText;
   response.focus();
 }
}

Результаты третьего теста

Этот код не работает ни на одном устройстве, кроме JAWS 5.0 и Connect Outloud (любопытно, что он не работает в JAWS 6.2, учитывая, что он успешно работает в более ранней версии). Отказ работать в большинстве устройств означает, что ничего не происходит вообще; однако в JAWS 6.2 ссылка на триггер будет повторена снова, в то время как Windows Eyes продолжает вести себя точно так же, как и для модифицированного второго теста (начинает чтение с элемента после ответа).

Четвертый тест

Четвертый тест полностью исключает элемент ответа и вместо этого представляет текст ответа в диалоговом окне предупреждения. HTML — это просто триггерная ссылка, а функция onreadystatechange упрощена до следующего:

 request.onreadystatechange = function() 
{
 if (request.readyState == 4
     && /^(200|304)$/.test(request.status.toString()))
 {
     alert(request.responseText);
 }
}

Результаты четвертого теста

Это должно быть безопасно для всех, но удивительно, что это не так: Windows Eyes 5.0 не всегда произносит текст диалога. Иногда он просто объявляет диалог и не говорит вам, что говорит диалог!

Пятый тест

Для пятого теста мы перейдем к элементам формы. Сначала попробуем обновить и сфокусировать текстовое поле:

 <form action=""> 
 <div>
   <input type="text" id="response" size="50"
       value="This field will update with the response">
 </div>
</form>

Вот соответствующая функция onreadystatechange

 request.onreadystatechange = function() 
{
 if (request.readyState == 4
     && /^(200|304)$/.test(request.status.toString()))
 {
   response.value = request.responseText;
   response.focus();
 }
}

Результаты пятого теста

Этот тест не работает в Home Page Reader или Hal (ничего не происходит вообще, хотя есть типичный визуальный ответ). Это также дает сбой в JAWS 6.2, где, как и в третьем тесте, он снова повторяет триггерную ссылку и может также повторно объявить заголовок верхнего уровня.

Этот код также дает сбой в Windows Eyes, который ведет себя так же, как и в третьем тесте (т.е. он начинает чтение с элемента после ответа). Единственными читателями, в которых работает этот код, являются JAWS 5.0 и Connect Outloud, хотя они также говорят «редактировать», чтобы объявить поле редактирования, прежде чем произносить его значение.

Шестой тест

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

 request.onreadystatechange = function() 
{
 if (request.readyState == 4
     && /^(200|304)$/.test(request.status.toString()))
 {
   response.value = request.responseText;
   if (typeof response.createTextRange != 'undefined')
   {
     var range = response.createTextRange();
     range.select();
   }
   else if (typeof response.setSelectionRange != 'undefined')
   {
     response.setSelectionRange(0, response.value.length);
   }
 }
}

Результаты шестого теста

Схема успеха и неудачи здесь идентична предыдущему тесту.
В седьмом и последнем тесте мы будем использовать кнопку для элемента ответа:

 <form action=""> 
 <div>
   <button type="button"
     id="response">This button will update with the response
   </button>
 </div>
</form>

Затем мы изменим текст кнопки и сфокусируем ее, как мы делали для пятого теста:

 request.onreadystatechange = function() 
{
 if (request.readyState == 4
     && /^(200|304)$/.test(request.status.toString()))
 {
   response.firstChild.nodeValue = request.responseText;
   response.focus();
 }
}

Результаты седьмого теста

Этот тест также дает те же результаты, что и пятый и шестой тесты, но с небольшим и ожидаемым отклонением, которое JAWS 5.0 и Connect Outloud (в котором он работает) объявляет виджет ответа, говоря «кнопка» после текста, а не «редактировать» » перед этим.

Вывод

Похоже, не существует надежного способа уведомления программ чтения с экрана об обновлении в DOM. Существуют частичные подходы, которые работают для одного или нескольких устройств, но нет общего подхода или комбинации, которые бы охватывали их все, учитывая, что даже скромное предупреждение может работать некорректно в Windows Eyes.
Итак, что это значит для нас, разработчиков, означает ли это, что мы должны прекратить использовать методы AJAX?

Да?

Посмотрим правде в глаза: многие AJAX-приложения (осмелился сказать «большинство»?) Используют этот подход ради себя и не получают от этого никакой пользы — они также могут использовать традиционный POST и ответ.

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

Взаимодействия — это просто детали, и, возможно, мы действительно проецировали наши собственные желания и предпочтения на пользователей, для которых они не очень актуальны. Возможно, динамические клиентские интерфейсы вообще не приносят пользы читателям экрана; возможно, для них действительно было бы полезно выполнить задачу, для которой изначально был создан читатель: запросы отдельных страниц и взаимодействие HTTP. Это именно тот тип взаимодействия, с которым программа чтения с экрана была рассчитана.

Нет?

Может быть, нам следует просто попросить людей, использующих программы чтения с экрана, отключить JavaScript до тех пор, пока технология не будет готова. Или, возможно, мы должны добавить пользовательские настройки в начале наших приложений, чтобы пользователи могли предварительно выбрать свой выбор интерфейса. Если мы можем быть уверены в том, что пользователь программы чтения с экрана вообще не имеет JavaScript, то мы можем разработать не скриптовую функциональность, которая будет работать для него, опираясь на парадигму POST / response, как и для любого пользователя без сценария.

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

В настоящее время IBM работает с GW Micro (создателями Windows Eyes) и Mozilla Foundation, чтобы представить «роли» и «состояния» (определяемые атрибутами элемента), которые могут передавать информацию о природе и состоянии элемента . Теоретически это полностью решает проблему и означает, что любой соответствующий элемент может передавать всю необходимую информацию: его собственное значение, его поведенческую роль и его текущее состояние.

Но хотя это очень интересные разработки, мы не можем использовать их сейчас, потому что они не имеют обратной совместимости: они вообще не предоставляют никакой функциональности браузерам, кроме Internet Explorer или Firefox, и лишь очень ограничивают функциональность комбинаций устройств, отличных от Firefox. 1.5 плюс Windows Eyes 5.5.

Так?

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

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

Так что, если вы думаете, что я сдаюсь слишком легко, пожалуйста, сочтите это призывом к оружию: давайте найдем способ заставить его работать!