Как да напишете достъпен JavaScript

Вие обгръщате уеб проект и точно когато поставяте финалните щрихи, получавате списък с грешки за достъпност, предавани ви от вашия ръководител на проекти. Неадекватен цветен контраст. Липсват alt тагове. Този интерактивен компонент трябва да е достъпен от клавиатурата.

Може да не бихме искали да го признаем, но всички сме били там: в края на проект, който се опитва да върне инженерната достъпност в нашия сайт. Това е разочароващо за разработчици, дизайнери, ръководители на проекти и клиенти.

Въпреки че достъпността може да бъде разочароваща, можете да настроите себе си, своя екип и клиент за успех, като планирате достъпността от самото начало. Ето четири техники, които ви спестяват време и проблеми при изграждането на достъпни уебсайтове и приложения с активиран JavaScript.

1. Използвайте елемента

Като цяло използването на семантични HTML елементи ще бъде предимство за достъпността на вашия уеб проект. Когато работите с интерактивност, <бутонът> е семантичният маркер за нещата, които потребителите кликват върху тези, които не са връзки или други входове. Това е семантичен начин да се обозначи, че елементът може да се кликва и ще бъде вашият нов най-добър приятел.

Елементът HTML
- Мрежа за разработчици на Mozilla

Когато използвате елемента

        
  • Примерна връзка
  •     
  • Примерна връзка 2
  •     
  • Примерна връзка 3
  • Сега трябва да превключим разширеното състояние. Начинът, по който обикновено правя това, е с метода setAttribute ().

    const listExpander = document.querySelector ('. списък-разширител');
    const list = document.querySelector ('# extendable-list-1');
    listExpander.addEventListener ("щракване", (д) ​​=> {
        if (list.getAttribute ('aria-prošireни') === "true") {
            list.setAttribute ('aria-prošireни', 'false');
        } else {
            list.setAttribute ('aria-prošireни', 'true');
        }
    });

    Обърнете внимание, че когато проверявам да видя стойността на атрибута с разширени арии, използвам === "true". Това е така, защото getAttribute връща или низа "true" или "false", а не действителната вярна или невярна стойност. (Това ме задейства в началото).

    Можете да използвате същия такъв тип мислене с други истински / неверни атрибути на ARIA. Най-често използвам това със скрита ария за показване и скриване на модални диалози.

    4. Управление на фокуса

    Последното нещо, което ще разгледаме в това ръководство, е управлението на фокуса. Фокусът се отнася до единствения елемент в браузъра, който може да се действа чрез клавиатурата. Елементите често получават фокус, когато потребителят кликне върху тях, използва клавиша TAB, за да премине през фокусирани елементи или използва екранен четец. На основно ниво трябва да сте сигурни, че потребителите могат да визуално да кажат по всяко време какъв елемент е на фокус.

    Най-честото място, на което в крайна сметка управлявам фокуса, е в модалните компоненти.

    Ето примерен проблем, който трябва да разрешим. Имаме страница с информация, която съдържа биография на човек и бутон с надпис „Свържете се с този човек“. Този бутон отваря модал, който съдържа контактна форма. Но ако формата не е в естествения ред на раздела на страницата (както е обичайно при модалите), когато потребителят натисне раздела, фокусът на клавиатурата му е зад модалния. Обичайно е потребителите на клавиатурата и помощните технологии да се забиват и да се смущават от лошо проектирани модали.

    За да разрешим това, искаме да направим няколко неща:

    1. Когато модалът се отвори, преместете фокуса върху първия фокусируем елемент вътре в модала.
    2. Уверете се, че потребителите могат лесно да затворят модала чрез клавиатурата, когато е отворен.
    3. Когато модалът се затвори, върнете фокуса върху елемента, който е бил активен, когато модалът се е отворил.
    4. Ако искаме да бъдем наистина внимателни, можем да хванем TAB напред и назад вътре в модала, така че потребителите да не могат да избягат, освен ако не затворят модала.

    Вземете първия фокусируем елемент.

    Имам няколко помощни метода, които да ми помогнат да определям всички фокусирани елементи и първия фокусиращ елемент в даден контекст. Ето как намирам всички фокусирани елементи на страницата (з / т към Крис Фердинанди).

    / **
      * Вземете всички фокусирани елементи вътре в зададения контекст.
      *
      * @param {String} [context = 'document'] DOM контекстът, в който искате да търсите.
      * @return {Array} Масив от фокусирани елементи
      * /
    функция getFocusable (контекст = 'документ') {
        нека focusable = Array.from (context.querySelectorAll ('бутон, [href], изберете, textarea, input: not ([type = "скрит"]), [tabindex]: not ([tabindex = "- 1"]) "));
        връщане фокусирано;
    }

    Тази функция използва querySelectorAll със списък на селектори, които обикновено са фокусирани: <бутон>, връзки с атрибут href, входове и неща, които имат набор от tabindex (това не е -1). Също така филтрирам селектора , като премахвам всеки вход, който е скрит, тъй като те не са фокусирани. Правя същия вид филтриране за елементи с атрибут tabindex, зададен на -1, тъй като тези елементи трябва да бъдат фокусирани само чрез JavaScript метод, а не в нормалния индекс на раздела. Използвам Array.from, за да създам масив от NodeList, върнат от querySelectorAll.

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

    Намирането на първия фокусиращ елемент е тривиално сега, става въпрос за изхвърляне на първия елемент от нашия масив. Обикновено имам друга помощна функция, която да ми даде първия фокусиран елемент и всъщност не го извиквате директно. Това е така:

    / **
     * Вземете всички фокусируеми елементи от зададения контекст.
     *
     * @param {String} [context = 'document'] DOM контекстът, в който искате да търсите.
     * @return {Array} Масив от фокусирани елементи
     * /
    функция getFocusable (контекст = 'документ') {
        нека focusable = Array.from (context.querySelectorAll ('бутон, [href], изберете, textarea, input: not ([type = "скрит"]), [tabindex]: not ([tabindex = "- 1"]) "));
        връщане фокусирано;
    }

    Преминавате в контекст и той извиква нашата оригинална функция getFocusable () и връща първия елемент в масива. Сега можем да извикаме фокус () върху този елемент, за да фокусираме програмно първия фокусируем елемент. Ще изглежда така:

    getFirstFocusable (модален) .focus ();

    Уверете се, че потребителите могат лесно да затворят модала чрез клавиатурата, когато е отворен

    Ние частично се спряхме на това по-рано, когато обсъждахме планирането на общи взаимодействия с клавиатурата. Това е перфектен пример за време, когато искате потребителят да може да ESC извън компонент.

    Можете също така да добавите наслагване между модалното и съдържанието на сайта, което може да се кликва и да се фокусира със събития на кликване, които затварят модала.

    Когато модалът се затвори, върнете фокуса върху елемента, който е бил активен, когато модалът се е отворил.

    В нашия пример потребителят натисна бутон и след това фокусът им скочи до модалния. Когато затворят модала, искаме да върнем фокуса си върху бутона, който задейства модала. Това всъщност е доста тривиално с помощта на свойството document.activeElement.

    Когато открием, че един модал трябва да се отвори и преди да прехвърлим фокуса към този модал, можем да запишем текущия активен елемент на променлива като тази:

    нека previousActiveElement = document.activeElement;

    Тогава можем да прехвърлим фокуса към нашия първи фокусируем елемент и всеки път, когато потребителят се свърши с модала и реши да го затвори, прехвърляме фокуса обратно в нашия запазен елемент:

    previousActiveElement.focus ();

    И сега потребителят е отново там, където са започнали!

    Закачете TAB и SHIFT + TAB вътре в модала

    Както споменах по-горе, ако искаме да бъдем наистина внимателни, можем да хванем TAB напред и назад вътре в модала, така че потребителите да не могат да избягат, освен ако не затворят модала.

    За да направим това, трябва да слушаме събитието за клавиатура, докато модалът е активен, и ето функцията, която използвам за улавяне на фокуса (зависи от нашата функция getFocusable () отгоре:

    / **
     * Прихваща клавиша на раздела вътре в контекста, така че потребителят да не може случайно да стигне
     * остана зад него.
     *
     * Имайте предвид, че това не работи за потребители на VoiceOver, които навигират
     * командите VoiceOver, само за действия по подразбиране. Ще трябва да го направим
     * внедрете нещо като инертен атрибут за това (вижте https://github.com/WICG/inert)
     * @param {object} e обектът на събитието
     * /
    функция за износ trapTabKey (e, контекст) {
        if (e.key! == 'Tab') се върне;
    
        нека focusableItems = getFocusable (контекст);
        нека focusItem = document.activeElement;
    
        нека focusItemIndex = focusableItems.indexOf (focusItem);
    
        ако (напр. ShiftKey) {
            ако (фокусиранItemIndex == 0) {
                focusableItems [focusableItems.length - 1] .фокус ();
                e.preventDefault ();
            }
        } else {
            ако (focusItemIndex == focusableItems.length - 1) {
                focusableItems [0] .focus ();
                e.preventDefault ();
            }
        }
    }

    Първо, ние трябва да преминем в обекта на събитието, за да можем да открием кой клавиш се натиска и контекст за потребителя, който трябва да бъде „хванат“ вътре в.

    Ако клавишът, който натиснаха, не беше клавишът TAB, можем спокойно да се върнем и да не направим нищо.

    Ако беше ключът TAB, получаваме всички фокусируеми елементи в модала и елемента, върху който в момента са фокусирани. След като имаме тези две неща, можем да използваме метода indexOf, за да кажем къде се намира потребителят в реда на раздела в този контекст.

    Ако държаха клавиша shift (e.shiftKey === true), те вървяха назад, така че искаме да ги спрем, когато стигнат до първия фокусиран елемент в модалния модул и да се съсредоточат върху последния фокусиран елемент: focusableItems [focusableItems . дължина - 1] .фокус ().

    Ако те вървяха напред и стигнаха до последния фокусиран елемент в модала (focusItemIndex == focusableItems.length - 1), трябва да фокусираме първия фокусиран елемент.

    Трябва да извикаме e.preventDefault () и за двата случая, за да предотвратим активирането на функцията TAB по подразбиране. За всички останали случаи обаче можем да ги оставим TAB нормално.

    Ще искате да сте сигурни, че премахвате слушателя на вашето събитие за клавиатура, когато потребителят затвори модала, за да остави функционалността им TAB да се върне в нормално състояние.

    заключение

    Тук сме обхванали много, но трябва да бъде наистина добро начало да започнете да разработвате достъпни интерактивни JavaScript сайтове и приложения и да ви дадем рамка за мислене как можете да програмирате други джунджурии и компоненти. Не забравяйте:

    1. Използвайте <бутон> за елементи с възможност за кликване
    2. Планирайте общи взаимодействия с клавиатурата като ESC, стрелки, Enter и TAB.
    3. Помислете и управлявайте всички подходящи състояния на ARIA.
    4. Управлявайте фокуса, когато е необходимо.

    Като имате предвид тези техники от самото начало, ще спестите време и проблеми и вашите потребители ще ви благодарят!

    И ако искате да видите още примери за това как да изградите достъпни уебсайтове, запишете се за моя безплатен курс за електронна поща: 9 общи грешки в достъпността на уебсайтове и как да ги поправите. Получете достъп до курса, като се регистрирате тук!

    Първоначално публикуван в benrobertson.io.