Рубрики
Без рубрики

Создание пользовательского, доступного выпадающего вниз

Примечание: я все еще изучаю доступность, поэтому, если вы найдете недостаток в моем методе, пожалуйста, дайте мне знать в … Tagged с HTML, A11Y, JavaScript.

Примечание: я все еще изучаю доступность, поэтому, если вы найдете недостаток в моем методе, пожалуйста, дайте мне знать в комментариях ниже!

Создать пользовательские компоненты сложно. Вы должны переопределить много стиля браузера по умолчанию, и часто это может быть утомительным. И в некоторых случаях невозможно стилизовать HTML -элементы. Это так с выбранным выпаданием.

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

Таким образом, мы должны «взломать» наш путь к созданию выпадения. К сожалению, это обычно приводит к отсутствию доступности.

В этом уроке мы узнаем, как создать выбросы настраиваемого выбора, при этом соблюдая стандарты доступности W3C.

Вот выпадение, которое мы будем создавать:

Традиционно, при создании выбора выпадающего вниз, вы будете использовать следующее:


Проблема с использованием Элемент в том, что вы не можете обернуть ребенка <Опция> Элементы в контейнере. Зачем нам обернуть эти элементы в контейнер? Чтобы расположить выпадающий список под окном ввода.

В нашем случае мы хотим, чтобы элементы списка, <Опция> Элементы, которые будут расположены под коробка. Браузер отображает пункты меню по умолчанию в качестве наложения:

Чтобы относительно позиционировать дочерний элемент по отношению к родительскому элементу, так и в случае с пользовательским выпадающим меню, вы должны установить следующие свойства CSS:

    .parent {
        position: relative;
    }

    .child {
        position: absolute;
        top: 0;
        left: 0;
     }

Вы можете задаться вопросом: «Разве вы не можете переписать HTML на следующее (используя CSS выше)?

    

Ответ, к сожалению, Нет Анкет Вы не можете разместить

Внутри Анкет

Поэтому мы должны создать доступный обходной путь.

Создание пользовательского выбора

Поскольку мы не можем использовать Элемент, я решил использовать серию

    и
  • элементы.

    Структура выглядит примерно так:

    
    

    Это довольно просто.

    • У нас есть весь компонент, завернутый в неупорядоченный список.
    • Метка – это элемент списка.
    • Выбор также является элементом списка.
    • Затем у нас есть значок «Стрелка». И, наконец, меню «Список элементов» охватывается в подрядном списке.

    Но … это недоступно. Если пользователь с нарушениями зрения с помощью вспомогательных технологий посещает эту страницу, он не подсказывает, что это выпадает или как взаимодействовать с ней. Кроме того, это полностью недоступно на клавиатуре.

    Сделать пользовательский элемент доступным

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

    Вот что нам нужно, чтобы сделать этот экран обработчиком доступным:

    • У раскрывающейся метки должен быть удостоверение личности. Это потому, что мы будем использовать Aria-Labelledby на
    • который будет функционировать как выброшенный выпуск, и этот атрибут принимает id HTML, который помечает его. Я дам ему идентификатор Выпадающаяся маршрута Анкет
    • Функционирование в виде выбора выпадения должно иметь Роль = "Кнопка" а также aria-labelledby = "раскрывающаяся марка" Анкет
    • Элемент нуждается в дополнительной информации, чтобы описать, что это такое. Таким образом, мы можем добавить Открыть раскрытие Как первый ребенок SVG.
    • Контейнер выпадающего списка должен сообщить пользователю, расширено ли меню или нет. Мы можем добавить aria-expeded = "false" атрибут, чтобы сообщить эту информацию. Это должно быть обновлено с помощью JavaScript по мере изменения состояния.

    Вот что нам нужно, чтобы сделать эту клавиатуру доступной:

    • который функционирует в виде выбора выпадения, нуждается в tabindex = "0" Таким образом, пользователь может сосредоточиться на элементе.
    • Все
    • В выпадающем меню также нужно tabindex = "0" Анкет

    Вот доступный HTML:

      
    

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

    • Пользователь может сосредоточиться на элементе с помощью клавиатуры.
    • Пользователь может открыть выберите «Выбравшись», нажав пробел или ввести клавиши.
    • Пользователь может перемещаться по элементам элемента списка с клавишами стрелка вверх и вниз или клавиш вкладки.
    • Пользователь может изменить выбор, сосредоточившись на элементе списка и нажав Enter.
    • Пользователь может отклонить раскрытие, нажав.
    • Как только пользователь выбирает элемент списка, список должен закрыться.

    Итак, теперь давайте реализуем это.

    Реализация доступности клавиатуры с помощью JavaScript

    Во -первых, нам нужно взять ключевые коды для пробела, ввести клавишу, клавиши стрел вверх и вниз и клавишу Escape. (Я видел, как космическая панель представлена как 0 и 32, поэтому я установил ее на то, чтобы быть в безопасности).

      const SPACEBAR_KEY_CODE = [0,32];
      const ENTER_KEY_CODE = 13;
      const DOWN_ARROW_KEY_CODE = 40;
      const UP_ARROW_KEY_CODE = 38;
      const ESCAPE_KEY_CODE = 27;
    

    Далее, есть несколько элементов, которые мы знаем, нам понадобятся. Я спасу их на постоянные. Мы также захотим отслеживать идентификаторы элементов списка, поэтому я объявлю пустой массив, который мы заполним.

      const list = document.querySelector(".dropdown__list");
      const listContainer = document.querySelector(".dropdown__list-container");
      const dropdownArrow = document.querySelector(".dropdown__arrow");
      const listItems = document.querySelectorAll(".dropdown__list-item");
      const dropdownSelectedNode = document.querySelector("#dropdown__selected"); 
      const listItemIds = [];
    

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

      dropdownSelectedNode.addEventListener("click", e =>
        toggleListVisibility(e)
      );
      dropdownSelectedNode.addEventListener("keydown", e =>
        toggleListVisibility(e)
      );
    
      // Add each list item's id to the listItems array
      listItems.forEach(item => listItemIds.push(item.id));
    
      listItems.forEach(item => {
        item.addEventListener("click", e => {
          setSelectedListItem(e);
          closeList();
        });
    
        item.addEventListener("keydown", e => {
          switch (e.keyCode) {
            case ENTER_KEY_CODE:
              setSelectedListItem(e);
              closeList();
              return;
    
            case DOWN_ARROW_KEY_CODE:
              focusNextListItem(DOWN_ARROW_KEY_CODE);
              return;
    
            case UP_ARROW_KEY_CODE:
              focusNextListItem(UP_ARROW_KEY_CODE);
              return;
    
            case ESCAPE_KEY_CODE:
              closeList();
              return;
    
             default:
               return;
          }
        });
      });
    

    Теперь давайте создадим некоторые из этих функций, которые мы только что позвонили в слушателей событий. SetSelectedListItem принимает событие и обновляет в данный момент выбранным элементом в поле «SELECT».

    function setSelectedListItem(e) {
      let selectedTextToAppend = document.createTextNode(e.target.innerText);
      dropdownSelectedNode.innerHTML = null;
      dropdownSelectedNode.appendChild(selectedTextToAppend);
    }
    

    Близкий Закрывает список и обновляет ария-экспозиция ценность.

    function closeList() {
      list.classList.remove("open");
      dropdownArrow.classList.remove("expanded");
      listContainer.setAttribute("aria-expanded", false);
    }
    

    Togglelistibility принимает мероприятие. Если клавиша Escape была нажата, закройте список. В противном случае, если пользователь нажал или если он нажал на пробел или введите клавишу, переключите расширенное состояние и обновите ария-экспозиция ценность соответственно. Наконец, если были нажаты клавиши со стрелками вниз или вверх, сосредоточьтесь на следующем элементе списка.

    function toggleListVisibility(e) {
      let openDropDown = SPACEBAR_KEY_CODE.includes(e.keyCode) || e.keyCode === ENTER_KEY_CODE;
    
      if (e.keyCode === ESCAPE_KEY_CODE) {
        closeList();
      }
    
      if (e.type === "click" || openDropDown) {
        list.classList.toggle("open");
        dropdownArrow.classList.toggle("expanded");
        listContainer.setAttribute(
          "aria-expanded",
          list.classList.contains("open")
        );
      }
    
      if (e.keyCode === DOWN_ARROW_KEY_CODE) {
        focusNextListItem(DOWN_ARROW_KEY_CODE);
      }
    
      if (e.keyCode === UP_ARROW_KEY_CODE) {
        focusNextListItem(UP_ARROW_KEY_CODE);
      }
    }
    

    FocusNextListitem берет направление, которое либо констант Down_arrow_key_pressed или Up_arrow_key_pressed Анкет Если пользователь в настоящее время сосредоточен на «Select», сосредоточьтесь на первом элементе списка. В противном случае нам нужно найти индекс ориентированного в настоящее время элемента списка. Вот где ListItemsId Массив пригодится. Теперь, когда мы знаем, где в списке целенаправленный элемент в настоящее время мы можем решить, что делать.

    Если пользователь нажал клавишу со стрелками вниз, и он не на последнем элементе списка, сосредоточьтесь на следующем элементе списка. Если пользователь нажал клавишу стрелки вверх, и он не на первом элементе списка, сосредоточьтесь на предыдущем элементе списка.

    function focusNextListItem(direction) {
      const activeElementId = document.activeElement.id;
      if (activeElementId === "dropdown__selected") {
        document.querySelector(`#${listItemIds[0]}`).focus();
      } else {
        const currentActiveElementIndex = listItemIds.indexOf(activeElementId);
        if (direction === DOWN_ARROW_KEY_CODE) {
          const currentActiveElementIsNotLastItem =
          currentActiveElementIndex < listItemIds.length - 1;
          if (currentActiveElementIsNotLastItem) {
            const nextListItemId = listItemIds[currentActiveElementIndex + 1];
            document.querySelector(`#${nextListItemId}`).focus();
          }
        } else if (direction === UP_ARROW_KEY_CODE) {
          const currentActiveElementIsNotFirstItem =
          currentActiveElementIndex > 0;
          if (currentActiveElementIsNotFirstItem) {
            const nextListItemId = listItemIds[currentActiveElementIndex - 1];
            document.querySelector(`#${nextListItemId}`).focus();
          }
        }
      }
    }
    

    Вот и все! Теперь у вас есть полностью совместимое раскрытие для клавиатуры! Я не буду покрывать здесь sass/css, но вы можете проверить это на Codepen Анкет

    Оригинал: “https://dev.to/emmabostian/creating-a-custom-accessible-drop-down-3gmo”