Фокус за пределами видимой области
WCAG утверждает, что элемент, имеющий фокус, всегда должен быть виден пользователю. Это особенно сложно, если вообще возможно, в окне чата, где пространство ограничено.
Когда пользователь клавиатуры или средства чтения с экрана переходит к первому параметру кнопки и выбирает его, содержимое прокручивается, и кнопка больше не отображается, нарушая правило WCAG "фокус всегда видимый". Также, если в списке более одной кнопки, фокус остается на кнопке, и если они продолжают переходить на вкладку, окно прокручивается туда, где установлен фокус. Это дезориентирует, и можно возразить, что при выборе кнопки другие параметры не актуальны, поскольку теперь доступны новые параметры.
Пример: https://recordit.co/2jDDvqg98J
Один из вариантов - остановить прокрутку при достижении кнопки, чтобы кнопка была видна. Но я считаю, что это плохой опыт и компромисс для соблюдения правила WCAG. Я провел некоторое исследование, и весь диалоговый интерфейс без исключения прокручивал вниз, когда в чате печатается новый контент. Если я отклоняюсь описанным выше способом, чтобы не нарушать WCAG, я нарушаю Закон Якобса.
Другой вариант - убрать фокус с выбранной кнопки на поле ввода или первую кнопку в следующем / новом списке доступных кнопок. Но я чувствую, что эта воля для слепых снимает все ориентиры.
Есть ли какие-то другие варианты или конструкции, которые вы могли бы придумать, чтобы решить эту проблему доступным способом?
2 ответа
Ваш пример слишком сложен.
Возьмем стандартную HTML-страницу: если вы сфокусируете элемент с помощью клавиатуры, вы все равно сможете прокручивать страницу с помощью мыши (без изменения курсора клавиатуры) и заставить элемент исчезнуть из области просмотра. Это не отменяет никаких критериев WCAG.
Нажатие символьной клавиши на textarea
сделает его снова визуально сфокусированным. Для кнопки или ссылки tab тогда потребуется shifttab, чтобы кнопка снова была видимой и сфокусированной.
Визуальный фокус и фокус клавиатуры - две разные вещи, но если фокус клавиатуры влияет на визуальный фокус, это не отвечает взаимностью
Проблема может быть не в фокусе, а в ограничении времени.
Не прокручивайте, если пользователь не находится в нижней части окна,
Не прокручивайте, если действие было выполнено в течение последних N секунд, или подождите P секунд, если никакое другое действие не выполняется перед прокруткой.
Укажите, что новые сообщения были получены, если окна не прокручиваются.
Я думаю, что в Slack есть хорошая реализация "не прокручивать и указывать новые сообщения", если вы не внизу.
Не полный ответ
Это слишком большой вопрос, чтобы ответить на него полностью, есть сотни вещей, которые нужно учитывать, и только с помощью GIF, чтобы увидеть поведение, я могу только обобщить.
Фокус вне экрана
Вы переборщили с WCAG. Смысл этого момента в том, что, когда я меняю фокус, объект в фокусе становится видимым на экране.
Если я прокручиваю веб-страницу, вы не можете ожидать, что элементы с фокусом останутся на странице (иначе ни одна страница в мире не была бы совместимой, если бы она была длиннее, чем длина экрана!).
Пока я фокусируюсь на следующем элементе, он прокручивается в поле зрения и имеет индикатор фокуса, который я вижу (правильный контраст, не зависит от цвета), вы оцениваете WCAG AAA по этому пункту!
Автоматическая прокрутка содержимого
Здесь все становится "мутным". В вашем примере контент добавляется в окно чата, и поэтому выбранный в данный момент элемент прокручивается за пределы экрана.
Теперь, если мы переместим фокус на последние параметры (появляющиеся кнопки), у нас возникнет проблема, заключающаяся в том, что это может прервать работу программы чтения с экрана, и поэтому они не услышат всю предыдущую информацию и должны вернуться и послушать снова.
Если мы не переместим фокус, у вас возникнет описанная вами проблема, когда я нажимаю Tab(например), и страница возвращается обратно перед контентом, который я только что прослушал.
Предполагая, что у нас не было других вариантов, это все равно было бы предпочтительнее (без управления фокусом), но у нас есть варианты.
Пора найти обходные пути.
Здесь доступность становится интересной, нам нужно найти несколько обходных путей, поскольку здесь нет установленного шаблона.
Я бы сказал, что самая большая проблема здесь в том, что после произнесения нового текста мы перескакиваем обратно в верхнюю часть страницы при нажатии Tab или других сочетаниях клавиш.
Одним из способов решения этой проблемы было бы создание специального div, который фокусируется на втором, когда я выбираю вариант.
Этот div будет иметь tabindex="-1"
поэтому он недоступен с клавиатуры (только программно).
Когда я выбираю вариант, мы фокусируем этот div, а затем начинаем вставлять текст.
Таким образом, когда я нажимаю <kbd>Tab</kbd>
или ярлык для следующей кнопки, он переходит к первой новой опции.
Я создал элементарную скрипку ниже, она потребует некоторых улучшений, но дает вам шаблон для тестирования / работы. (перейдите в полноэкранный режим, иначе вы можете не увидеть добавленные кнопки и т. д.)
var hasLoadedMore = 0;
$('.loadMore').on('click', function(e){
if(hasLoadedMore == 0){ //just a horrible hack to simluate content only loading once when you click an option.
console.log("option chosen");
$('#focusAdjuster').attr('aria-hidden', false);
$('#focusAdjuster').focus();
console.log("focus adjusted");
loadContent();
}
});
function loadContent(){
///ugly way of simulating the content being added dynamically.
$('#chat').append('<p>additional text</p>');
$('#chat').append('<p>more text</p>');
setTimeout(function(){
$('#chat').append('<p>more text</p>');
$('#chat').append('<button>Option 1 new</button>');
$('#chat').append('<button>Option 2 new</button>');
}, 500);
hasLoadedMore = 1;
}
.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chat">
<p>initial text</p>
<button class="loadMore">Option 1</button>
<button class="loadMore">Option 2</button>
<button class="loadMore">Option 3</button>
<div tabindex="-1" aria-hidden="true" id="focusAdjuster" class="visually-hidden">loading</div>
</div>
Объяснение скрипки
Таким образом, ключевыми моментами являются визуально скрытые <div>
и сопутствующая строка в JavaScript, которая фокусирует этот div перед загрузкой дополнительного содержимого.
Div позволяет нам изменить фокус после нажатия кнопки. Я также добавил "загрузку" к тексту внутри этого div, чтобы добавить дополнительную цель, поскольку ваше приложение будет работать на AJAX.
Div имеет tabindex="-1"
поэтому он не может получить фокус. Я также добавилaria-hidden="true"
к этому и отключите это, когда нажата кнопка выбора, непосредственно перед тем, как переключить фокус.
Я бы переключил это обратно, когда фокус покидает этот div в реальном мире, но я не делал этого в быстрой демонстрационной скрипке.
Разве это не "провал" WCAG?Ага! В этом примере я сделал элемент, на который не фокусируется, фокусируемым, и он не имеет действия. Я все еще считаю, что это предпочтительнее, чем<button>
как это подразумевает, что у него есть действие. Это явно не идеально.
Однако ключевая часть WCAG является " G " - они Guidelines. То, что я предлагаю, - это "взлом" или компромисс, основанный на том факте, что я реалистично оцениваю время разработки и технические ограничения.
"Правильный" способ сделать это без div - это осторожное управление фокусом. Имея неограниченное время и бюджет, я обязательно это сделаю.
Но учитывая, что вам не нужно думать только о Tab ключе с программами чтения с экрана (вы можете перемещаться по ссылкам, кнопкам, заголовкам, разделам и т. Д.), Это становится кошмаром при попытке перехвата нажатий клавиш, и поэтому приведенный выше самый простой способ Я мог подумать об этом.
Альтернативный образец.
Поскольку предыдущий пример "проваливает" WCAG, есть другой вариант, но я бы сказал, что это хуже и вызывает множество проблем.
Каждый раз заменяйте текст в div новым текстом.
Обратной стороной является то, что вам нужно будет каждый раз предоставлять кнопки "предыдущий элемент" и отслеживать их, а положительной стороной является то, что это будет совместимо с WCAG (хотя я бы сказал, что это не так удобно, даже если вы "проходите" критерии.)
Кроме того, предоставление этих "предыдущих" кнопок снова создает ряд проблем с управлением фокусом (когда мы делаем их видимыми, получают ли они фокус, добавляют ли они больше путаницы?).
Думайте об этом как о шаблоне мастера форм, каждый набор вопросов - это "шаг", поэтому вы можете вернуться к предыдущим шагам и увидеть на экране только текущий шаг.
Я включил это, поскольку этот шаблон можно было бы расширить с некоторой мыслью, и чтобы дать OP / другим людям некоторые идеи, НЕ используйте его как есть
var hasLoadedMore = 0;
var optionChosen = "";
$('.loadMore').on('click', function(e){
if(hasLoadedMore == 0){ //just a horrible hack to simluate content only loading once when you click an option.
optionChosen = $(this).text();
console.log("option chosen", optionChosen);
loadContent();
}
});
function loadContent(){
$('#chat').html('<button class="previousQuestion">You chose ' + optionChosen + '<span class="visually-hidden">(click here to go back and chose a different option)</span></button>');
$('#chat').append('<h3>' + optionChosen + '</h3>');
///ugly way of simulating the content being added dynamically.
$('#chat').append('<p>additional text</p>');
$('#chat').append('<p>more text</p>');
setTimeout(function(){
$('#chat').append('<p>more text</p>');
$('#chat').append('<button>Option 1 new</button>');
$('#chat').append('<button>Option 2 new</button>');
}, 500);
hasLoadedMore = 1;
$('.previousQuestion').on('click', function(){
console.log("now you would restore the previous question");
});
}
.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="chat">
<p>initial text</p>
<button class="loadMore">Option 1</button>
<button class="loadMore">Option 2</button>
<button class="loadMore">Option 3</button>
<div tabindex="-1" aria-hidden="true" id="focusAdjuster" class="visually-hidden">loading</div>
</div>