Мне часто доводится вести странные споры с фанатами фреймворков о том, действительно ли <div> «столь же хорош», как и <button>.

Спойлер: нет. И давайте выясним, почему.

Проблема

В среде разработчиков на React, а также у тех, кому нравится HTMX, я часто вижу такое…

<div onclick="showSignIn()">
	Open Modal
</div>
function showSignIn () {
	// Код для отображения модального окна входа.
	// Подробности реализации зависят от стека.
}

Что здесь не так?

  1. Этот элемент не сообщает о себе как об интерактивном элементе пользователям программ для чтения экрана.

  2. Нельзя получить фокус <div> с клавиатуры.

  3. Событие срабатывает только по click, но не при нажатии на клавиши Enter или пробел (клавиатурные пользователи снова страдают).

Я встречал такое во многих кодовых базах. И во многих демо.

Я спорил с очень известным идейным лидером React, имя которого начинается на Р: он настаивал, что <div> обеспечивает «бóльшую accessibility», чем <button>, и что команда Twitter приняла правильное решение, когда стала использовать этот паттерн в своём приложении.

Всё это совсем-совсем неправильно.

«Исправления», которые не исправления

Многие HTML-элементы имеют собственные роли, сообщающие вспомогательным приложениям, например, программам для чтения экрана, что они делают.

Элемент <button> — один из них. У него есть внутренняя [роль] кнопки, и это даёт пользователям программ чтения экрана понять, что с ним можно взаимодействовать и вызывать некое поведение в приложении.

HTML-атрибут [role] можно использовать для добавления и изменения роли элемента. Поэтому люди вроде Рай... лидера мнений React говорят что-то подобное (передаю общий смысл)…

Этот атрибут существует не просто так, можно добавить [role="button"] к div, чтобы придать ему корректную семантику.

Допустим, но это решает только одну проблему.

Эта роль не влияет на возможность фокусировки (или её отсутствие) и клавиатурное поведение. Пользователи с нарушением зрения, выполняющие навигацию с клавиатуры, всё равно не могут ею воспользоваться.

Вам скажут: «Не волнуйтесь! Это тоже можно исправить!»

Можно добавить элементу возможность фокусировки при помощи атрибута [tabindex].

<div 
	onclick="showSignIn()"
	tabindex="0"
>
	Open Modal
</div>

Однако делать этого не стоит! Серьёзно, не надо вмешиваться в порядок фокусировки.

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

И опять-таки, интерактивность с клавиатуры всё ещё отсутствует.

Но не беспокойтесь! Можно и её добавить. Достаточно просто слушать все события keydown, а затем фильтровать их по event.key , чтобы код выполнялся только при нажатии на Enter или пробел (последнее означает проверку на буквальный пробел: ' ').

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

document.addEventListener('keydown', (event) => {

	// Выполняем только при нажатии на Enter и пробел
	if (event.key !== 'Enter' & event.key !== ' ') return;

	// Проверяем, что фокус на элементе, который нам нужен
	const notRealBtn = document.activeElement.closest('[onclick]');
	if (!notRealBtn) return;

	// Каким-то образом выполняем наш код...

});

Хм... допустим, строго говоря, так мы устранили проблему, но...

Так мы просто воссоздали всю функциональность, которая и так имелась

Серьёзно: на кой чёрт нам всё это делать?!?

Вся эта возня ради того, чтобы написать такой HTML…

<div 
	onclick="showSignIn()"
	tabindex="0"
>
	Open Modal
</div>

Когда можно просто написать так:

<button onclick="showSignIn()">
	Open Modal
</button>

У <button>

  1. Изначально имеется нужная [role].

  2. Есть автоматическая возможность фокусировки.

  3. Запускается событие click в ответ на нажатия Enter и Spacebar, если кнопка находится в фокусе.

Послушайте, я ленивый разработчик.

И, подозреваю, если вы любите инструменты наподобие React, то, вероятно, вы тоже ленивы. И это здорово! Лучший код — это код ненаписанный, и всё такое прочее.

Так что будьте ещё ленивее.

Используйте для этой задачи идеально подходящий элемент, ведь тогда вам не придётся писать кучу лишнего кода!

Комментарии (14)


  1. navferty
    02.11.2025 10:38

    Так и не понял, а в чём состоит аргументация тех, кто агитирует за использование div вместо button?


    1. nin-jin
      02.11.2025 10:38

      • Нет необходимости делать ресет стандартных стилей.

      • Стабильная кроссбраузерная стилизация без неожиданных сдвигов на пару пикселей.

      • Нет неожиданной отправки формы любой кнопкой в ней.

      • Можно реиспользовать тот же компонент кнопки для релизации и других кликабельных контролов.


      1. vanxant
        02.11.2025 10:38

        Нет неожиданной отправки формы любой кнопкой в ней

        так не надо лепить тип submit и не будет никаких отправок

        Нет необходимости делать ресет стандартных стилей.

        А кто сейчас не делает ресет стандартных стилей?


        1. dominus_augustus
          02.11.2025 10:38

          Читаю когда ответы создателя лучшего фреймворка не покидает ощущение его оторванности от современной разработки


        1. nin-jin
          02.11.2025 10:38

          https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#type

          Тот, кто не хочет неожиданных сайд эффектов.


    1. muxa_ru
      02.11.2025 10:38

      Так и не понял, а в чём состоит аргументация тех, кто агитирует за использование div вместо button?

      Полагаю, это стандартная аргументация "я подсмотрел в интернете вот такой вариант, по другому не умею и теперь весь мир должен под меня подстроиться."


  1. nin-jin
    02.11.2025 10:38

    можно добавить [role="button"]

    А можно role="tab"

    не надо вмешиваться в порядок фокусировки.

    tabindex="0" и не влияет на порядок.

    Нужно прикрепить это событие к document

    Не нужно.


  1. Kahelman
    02.11.2025 10:38

    Сначала создаем себе проблемы а потом из героически преодолеваем … безумие и отвага !


    1. urvanov
      02.11.2025 10:38

      Это девиз всего современного веба.


  1. moscowman
    02.11.2025 10:38

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


  1. SWATOPLUS
    02.11.2025 10:38

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

    Например есть вот эта таблица c дефолтными стилями: https://www.w3schools.com/cssref/css_default_values.php

    a:link color: (internal value);

    Этот (internal value) может различаться в браузерах, а хочется что бы было все единообразно.
    Плюс эта таблица не раскрывает aria-атрибуты и читалка может по разному работать в разных браузерах, и поэтому приходиться ручками что-то дописывать.

    И вот тут приходит мысль использовать div, самому все прописать и радоваться, что все везде одинаково работает. Но див не панацея, и самая частая задача где он не подходит это router-link. Казалось бы прописал на onclick смену роута, aria и все хорошо, но не работает функция открыть в новой вкладке. Поэтому приходиться использовать a-tag, делать preventDefault и переписывать стили.

    Еще бывает когда используют <a href="#" /> для открывания модалки, но такая кнопка скролит в начало документа. Поэтому надо href="javascript:void(0)" или preventDefault, или вообще button.

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

    Что бы лично я мог предложить:

    1. Резервирование тега e (element) в HTML и гарантирование отсутствия каких либо aria, стилей и всего остального.

    2. Введение дополнительных атрибутов, которые позволяют превратить e-тег в любой другой элемент например urlforopeninnewtab (попмини что все стандартные атрибуты пишутся без разделителей).

    3. Стандартизация всех тегов, составления списка стилей (уже есть), aria и других атрибутов, которые применяются по умолчанию.

    Тогда можно будет почитать этот список и понять, что же нужно будет переопределить для требуемого эффекта.


    1. nin-jin
      02.11.2025 10:38

      приходиться использовать a-tag, делать preventDefault и переписывать стили

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


    1. muxa_ru
      02.11.2025 10:38

      Этот (internal value) может различаться в браузерах, а хочется что бы было все единообразно.

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

      Вы ведь про это однообразие говорили?


      1. nin-jin
        02.11.2025 10:38

        Да-да, синим цветом даже на синем фоне. Они такое любят.