Семантическая разметка

Семантика разметки в Awesomplete строится вокруг идеи максимально чистой и предсказуемой структуры DOM, где автодополнение опирается не на сложные абстракции, а на стандартные HTML-элементы, дополненные минимальным набором ARIA-атрибутов для обеспечения доступности.

Основной контейнер списка предложений формируется как элемент <ul>, внутри которого каждый вариант представлен через <li>. Такая структура не является случайной: она напрямую соответствует ожиданиям вспомогательных технологий, таких как скринридеры, и обеспечивает корректную интерпретацию списка без дополнительной логики.

Awesomplete использует предсказуемую семантику:

<div class="awesomplete">
  <input id="city" autocomplete="off" />

  <ul>
    <li>Almaty</li>
    <li>Astana</li>
    <li>Karaganda</li>
  </ul>
</div>

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

Ключевой момент заключается в том, что список не превращается в кастомный non-semantic контейнер вроде <div>-структур. Это снижает когнитивную нагрузку для браузера и вспомогательных технологий.

ARIA-атрибуты и их роль

Для корректной работы с доступностью Awesomplete дополняет базовую разметку атрибутами ARIA. Основные из них:

  • role="combobox" — обозначает поле ввода с автодополнением
  • aria-autocomplete="list" — указывает, что подсказки представлены списком
  • aria-expanded — отражает состояние раскрытия списка
  • aria-owns — связывает input и список предложений
  • aria-activedescendant — указывает на активный элемент списка

Пример связки:

<input
  id="city"
  role="combobox"
  aria-autocomplete="list"
  aria-expanded="false"
  aria-owns="awesomplete_list_1"
/>

<ul id="awesomplete_list_1" role="listbox">
  <li id="awesomplete_option_1">Almaty</li>
  <li id="awesomplete_option_2">Astana</li>
</ul>

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

Связь input и списка

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

Awesomplete динамически формирует идентификаторы и синхронизирует их через aria-owns. Это позволяет экранным читалкам воспринимать список как продолжение input-элемента.

<input aria-owns="awesomplete_list_42" />
<ul id="awesomplete_list_42"></ul>

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

Роль списка (listbox pattern)

Вся структура списка реализует паттерн listbox, который является частью WAI-ARIA Authoring Practices.

<ul role="listbox">
  <li role="option">Option 1</li>
</ul>

Awesomplete использует этот паттерн как основу, но оптимизирует его под динамическое обновление. Каждый элемент списка получает роль option, что делает его интерпретируемым как выбираемый элемент.

<li role="option" aria-selected="true">Astana</li>

Состояние выбора отражается через aria-selected, что позволяет скринридерам озвучивать текущий активный элемент без визуального контакта.

Управление активным элементом

Семантическая корректность списка напрямую зависит от aria-activedescendant. Вместо постоянного перемещения фокуса по <li> используется единый фокус на input, а активный элемент обозначается атрибутом.

<input aria-activedescendant="awesomplete_option_2" />

Такой подход решает сразу несколько проблем:

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

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

Скрытие и отображение списка

Семантическое поведение списка дополняется состоянием видимости через aria-expanded.

<input aria-expanded="false" />

При открытии списка значение изменяется:

<input aria-expanded="true" />

Это позволяет вспомогательным технологиям понимать, что интерфейс изменил состояние, даже если визуально изменение минимально.

Сам список при этом часто управляется через CSS:

.awesomplete ul {
  display: none;
}

.awesomplete ul[hidden] {
  display: none;
}

.awesomplete.open ul {
  display: block;
}

Важно, что семантика не нарушается: скрытие не удаляет элементы из DOM, а лишь меняет их доступность.

Минимизация нестандартных элементов

Одной из ключевых идей Awesomplete является отказ от избыточной кастомной разметки. Не используются сложные вложенные контейнеры, декоративные div-структуры или искусственные роли.

Вместо этого сохраняется:

  • <input> как точка ввода
  • <ul> как контейнер списка
  • <li> как элемент выбора

Такой подход снижает вероятность конфликтов с браузерной семантикой и улучшает предсказуемость поведения.

Динамическая генерация элементов

При обновлении списка Awesomplete пересоздаёт элементы <li> на основе входных данных. Семантическая структура при этом сохраняется неизменной.

list.innerHTML = "";

items.forEach((item, index) => {
  const li = document.createElement("li");
  li.textContent = item.label;
  li.setAttribute("role", "option");
  li.id = `awesomplete_option_${index}`;

  list.appendChild(li);
});

Ключевой момент — каждый новый элемент сразу получает корректную ARIA-разметку, а не добавляется «пустым» с последующей модификацией.

Семантика состояния выбора

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

<li role="option" aria-selected="true">Karaganda</li>

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

  • предыдущий элемент получает aria-selected="false"
  • новый элемент получает aria-selected="true"

Такая модель исключает неоднозначность в интерпретации состояния списка.

Поведение при отсутствии результатов

Семантика также включает состояние пустого списка. В Awesomplete оно обычно отражается через отсутствие <li> элементов или скрытие списка.

Дополнительно может использоваться ARIA-индикатор:

<ul aria-label="No results found"></ul>

или динамическое сообщение:

<div role="status">No results</div>

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

Интеграция с формой

Awesomplete всегда существует в контексте формы, поэтому семантика input остаётся первичной. Автодополнение не заменяет поле ввода, а расширяет его поведение.

<form>
  <label for="city">City</label>
  <input id="city" role="combobox" />
</form>

Связь через <label> усиливает доступность, так как позволяет однозначно интерпретировать назначение поля.

Согласованность DOM и состояния

Одной из особенностей архитектуры Awesomplete является синхронизация DOM с внутренним состоянием без расхождения между визуальной и семантической моделью.

Любое изменение:

  • ввода текста
  • навигации стрелками
  • выбора элемента
  • закрытия списка

немедленно отражается в:

  • aria-expanded
  • aria-activedescendant
  • aria-selected
  • структуре <ul>/<li>

Эта согласованность является основой корректной работы автодополнения в контексте доступного интерфейса.