Казалось бы, простая задача - сверстать список пар ключ-значение. Бери <div>
и делай. Но что, если захотелось подушнить? Этим и займёмся в статье...
Рассмотрим три подхода к решению этой задачи: <div>
, <dl>, <dt>, и <dd>
, и <table>
. Обсудим преимущества, недостатки, взглянем на примеры.
Цель - разобраться с выбором наиболее подходящего подхода для решения задачи.
Задача, дано
<Input>
, принимающий ИНН организации. Стучится в дадату, возвращает данные, создает выпадающий список и даёт пользователю выбрать нужное.
Далее определяется несколько пар ключ-значение для каждой организации. Простой объект:
{
companyName: "ООО "Моя оборона",
inn: 1234567890,
kpp: 1234567890,
address: "Улица Пушкина дом Колотушкина"
}
Решение №1 <div>
Это самый простой и прямолинейный подход, который легко реализовать.
<div class="company">
<div class="inn"><span>ИНН</span>: <span>1234567890</span></div>
<div class="kpp"><span>КПП</span>: <span>1234567890</span></div>
<div class="address"><span>Адрес</span>: <span>Улица Пушкина дом Колотушкина</span></div>
</div>
Помимо простоты, это самый легковесный вариант. 5 строк и несколько элементов. Стилизовать такой вариант получается намного быстрее следующих.
Однако, смотрим в документацию и видим, что <div> используется когда ни один другой элемент не подходит, элемент div используется в качестве крайней меры. В противном случае это приводит к снижению доступности для читателя. Также Согласно руководству W3C по доступности веб-контента (WCAG):
информация, структура и отношения, передаваемые через презентацию, должны быть доступны программно. Это означает, что структура пар ключ-значение должна быть ясно выражена в разметке, а не только в визуальном представлении
Выходит, данное решение не удовлетворяет принципу устойчивости (robust) WCAG 2, использованию правильной семантической верстки. Хорошо, тогда попробуем второе решение.
Решение №2 <dl> <dt> <dd>
Элементы <dl>
, <dt>
, <dd>
предназначены для представления пар ключ-значение. Список - термин - описание могут смутить, поэтому обратимся к документации w3.org
<dl>
элемент предоставляет ассоциативный список, состоящий из нуля или более групп имя-значение.
...
Группы "Имя-значение" (<dt>
и<dd>
соответственно) могут представлять собой термины и определения, разделы метаданных и значения, вопросы и ответы или любые другие группы данных "имя-значение"
В контексте HTML, “термин” в <dt>
может быть более широко интерпретирован, чем просто академический или словарный термин. Он может обозначать любой элемент данных, который имеет связанное с ним значение. Так что использование ИНН и номера в качестве “терминов” вполне приемлемо.
<dl>
<dt>ООО "Моя оборона"</dt>
<dd>
<dl>
<dt>ИНН</dt>
<dd>1234567890</dd>
<dt>КПП</dt>
<dd>1234567890</dd>
<dt>Адрес</dt>
<dd>Улица Пушкина дом Колотушкина</dd>
</dl>
</dd>
</dl>
Принцип устойчивости WCAG тоже соблюдён. А скринридер распознает эти элементы как определения, причем читая их последовательно:
Термин "ООО Моя оборона" -> термин "ИНН" -> 1234567889 -> термин "КПП" -> 1234567890 -> термин "Адрес" -> Улица Пушкина дом Колотушкина.
Стилизовать такой вариант немного сложнее, чем первое решение. Также с недавних пор группы терминов можно оборачивать в <div>. Однако, здесь возникает еще один вопрос - не табличные ли это данные? Ведь здесь есть её признаки:
Структура таблицы включает в себя две базовые части:
Подлежащее — это признак, по которому данные группировали и обрабатывали.
Сказуемое — это данные, которые раскрывают суть подлежащего.
Решение №3 <table>
Элемент <table>
предназначен для представления табличных данных. С одной стороны, можно представить решение задачи как список таблиц вида:
ИНН |
1234567890 |
КПП |
1234567890 |
Адрес |
Улица Пушкина дом Колотушкина |
Несмотря на то, что выше представлена простая таблица, данные в ней не группированы. По сути это выборка данных из общей таблицы, которая должна бы выглядеть иначе. Взглянем на верстку:
<table>
<thead>
<tr>
<th colspan="2">ООО "Моя оборона"</th>
</tr>
</thead>
<tbody>
<tr>
<td>ИНН</td>
<td>1234567890</td>
</tr>
<tr>
<td>КПП</td>
<td>1234567890</td>
</tr>
<tr>
<td>Адрес</td>
<td>Улица Пушкина дом Колотушкина</td>
</tr>
</tbody>
</table>
Получилось громоздко. Скринридер увидит, что к "ООО Моя оборона" относится целая табличная часть, но читать он ее будет криво:
ООО "Моя оборона" -> ИНН -> ООО "Моя оборона" -> 1234567890 > ООО "Моя оборона" -> КПП -> ООО "Моя оборона" -> ....
Как исправить ситуацию? Переверстать всё в одну таблицу и сгруппировать данные:
Название компании |
ИНН |
КПП |
Адрес |
ООО "Моя оборона" |
1234567890 |
1234567890 |
Улица Пушкина дом Колотушкина |
ООО "Рога и копыта" |
2234567890 |
2234567890 |
Улица Гагарина в гостях у Татарина |
Теперь это действительно таблица со сгруппированными данными. У таблицы есть "шапка" и "тело" и у скринридера не возникнет неполадок. Также в таком случае получится удовлетворить и требования WCAG. Однако, визуально задача будет решена неверно, ведь необходимостью было присвоить каждому элементу найденного списка отдельную сущность.
UPD: В комментариях указали, что заголовок таблицы задается через <caption> и в таком случае таблица получается правильной. Скринридер распознает такую таблицу тоже адекватно:
Таблица "ООО "Моя оборона" -> ИНН 1234567890 -> КПП 1234567890 -> .....
<table>
<caption>ООО "Моя оборона"</caption>
<thead>
<tr>
<th>ИНН</th>
<th>КПП</th>
<th>Адрес</th>
</tr>
</thead>
<tbody>
<tr>
<td>1234567890</td>
<td>1234567890</td>
<td>Улица Пушкина дом Колотушкина</td>
</tr>
</tbody>
</table>
В отладке поддержки доступности появляется лишний row
, однако он необходим, чтобы соотнести название столбца и его значение.
Недостаток такого способа остаётся всё тем же - типичное для <table>
большое количество DOM-узлов.
Заключение
Таким образом, в случае задачи сверстать список пар ключ-значение, удовлетворив требования семантики и доступности, необходимо выбирать Решение №2 <dl>
<dt>
<dd>
.
Подойдет также и решение №3, если не смущает большее количество DOM-узлов, чем в предыдущем решении.
А если нужно грубо и быстро, то стоит выбрать классический и самый популярный вариант с <div>
Комментарии (15)
s0k0108
23.12.2023 09:23Если вы пишите в дано что у вас возвращается список, почему бы не использовать список? Берете ul, каждая карточка отдельная li, дальше заголовок с названием организации, и p, span, address для структуры, если надо что то добавить для скринридера есть aria.
Libiros Автор
23.12.2023 09:23Целью является соответствие семантике. Вот выдержка из WAI-ARIA:
WAI-ARIA is intended to augment semantics in supporting languages like [HTML] and [SVG2], or to be used as an accessibility enhancement technology in other markup-based languages that do not explicitly include support for ARIA. It clarifies semantics to assistive technologies when authors create new types of objects, via style and script, that are not yet directly supported by the language of the page, because the invention of new types of objects is faster than standardized support for them appears in web languages.
It is not appropriate to create objects with style and script when the host language provides a semantic element for that type of object. While WAI-ARIA can improve the accessibility of these objects, accessibility is best provided by allowing the user agent to handle the object natively. For example, it's better to use an
h1
element in HTML than to use theheading
role on adiv
element.Не стоит использовать ARIA, если в этом нет необходимости. HTML5 предоставляет широкий спектр семантических элементов.
nin-jin
23.12.2023 09:23Элементов вёрстки печатной страницы. К семантике ваших данных они не имеют никакого отношения. Фактически, выбор тегов в хтмл определяется лишь тем, как вы хотите, чтобы всё выглядело без стилей. Именно поэтому у вас в хтмл есть заголовки, параграфы, списки и таблицы, но нет организаций, пользователей, договоров и платёжных поручений. И вам приходится выбирать как вторых утрамбовать в первых, ибо при должной фантазии сделать это можно любым способом. Например, сделать каждое свойство секцией, его имя - заголовком, а значение - цитатой.
meettya
23.12.2023 09:23Простите, но у меня пара вопросов:
1) а разве надо верстать не так, как нарисовал дизайнер?
2) а вот в рамках реального проекта вот этих пользователей, которые используют screen reader - их сильно отличное от нуля количество?Libiros Автор
23.12.2023 09:23Именно так. Статья этому не противоречит. Вариантов сверстать одно и то же довольно много. Однако, в статье я ищу вариант сделать это еще семантически верно + доступно.
Довольно мало, но это еще зависит от рода проекта. Полагаю, что на сайте посвященном инклюзивности, привлекающим людей с ограниченными возможностями, процент будет повыше, чем в рядовом интернет-магазине.
torbasow
23.12.2023 09:23Как нарисовал дизайнер — это дело CSS. А вёрстка в HTML должна соответствовать природе данных.
ImagineTables
Если хочется семантическую таблицу, но чтобы визуально это был список, в чём проблема?
В DevTools у
tbody
меняемdispaly
наflex
, и вперёд. Я заголовочную строку оставил как есть для сравнения, а строки с данными оформил как вложенные флексбоксы. В оригинале они не заполняют контейнер, а идут друг за другом, и я сделал так же, но нет проблем задатьflex-wrap
, и визуально будут полноценные карточки, заполняющие контейнер.Так что, можно и не ограничивать себя рамками №2. А вот разумность всего этого — отдельный вопрос. Разве бухгалтеры пользуются скринридерами?
ImagineTables
Тогда это выглядит вот так:
Libiros Автор
В начале статьи я сразу предупредил, что цель подушнить и разобраться "как правильно". Фактор разумности уже долгое время остаётся холиварным, хотя, на мой взгляд, здорово, когда разработчик задумывается о поддержке доступности.
Если стилизовать таблицу предложенным вами способом, то скринридер читает только значения, не повторяя необходимые ключи, так как их, по заданию задачи, не должно быть сверху (я добавил туда
display: none
). В инструментах отладки поддержки доступности предложенный вариант определяется как таблица со значениями. В итоге, мы не поймем, где написано про ИНН, а где про КПП и так далее.Можно доработать таблицу старым хаком, который позволяет сжимать широкую таблицу до мобильных размеров. Но тогда встает вопрос о целесообразности таких изощренных методов с большим количеством DOM-узлов и повторящихся псевдоэлементов. Проще вернуться к варианту с
<dl>
,<dt>
,<dd>
ImagineTables
Где про это было в условиях задачи? Вот что я прочитал:
Я и спросил, если нужна «действительно таблица», которая бы визуально выглядела как список, почему просто её соответствующе не оформить.
gmtd
Не увидел никакой душноты в статье. Полезная интересная аналитика.
Спасибо
ifap
Проблема в том, что для "семантического" заголовка в таблице используется
caption
, а неthead
+tr
+th
Libiros Автор
Вы правы, я дополню статью. Скринридер распознает это тоже "почти" удобно. Хорошее замечание, спасибо