25
Май
2015

Когда <button> не лучший выбор (она медленнее создается, отображается и с трудом стилизуется)

Перевод статьи When <button> isn’t the best choice (it’s slow to create and render and problematic to style) с сайта benfrain.com, автор — Бен Фрейн.

Элемент <button> значительно медленнее при создании в JavaScript, он медленнее отображается в браузере и крайне трудно стилизуется кроссбраузерно. Несмотря на то, что <button>, это «правильный» выбор для задач кнопки, это не обязательно должен быть «лучший» выбор.

Представьте картину. Я пытаюсь убедить главного инженера программного обеспечения использовать более семантические элементы в разметке, а не только общие элементы, типа div  и span. Такие семантические элементы, как sectionheaderfooter и nav. Разговор выглядел примерно следующим образом. «Нам нужно проверить, что они не медленнее при создании в JavaScript», — говорит он. «Конечно, нет проблем», — отвечаю я, с полной уверенностью.

Тогда-то я и понял, что для кнопки это добром не кончится. Видите ли, button немного особенный. Он как радиокнопки или группы полей: он делает «нечто».

У вас есть несколько минут? Я хочу рассказать, почему ратовать за использование элемента button  может быть гиблым делом для больших веб-проектов.

Тестирование производительности кнопок при помощи createElement в JavaScript

Прежде всего мне нужны были данные, чтобы гарантировать, что создание семантических элементов (sectionnavabutton и т.д.) в JavaScript не медленнее чем создание div или span.

Я не пишу умные тесты. Я не достаточно умён для этого. Я определённо не прошёл бы тест FizzBuzz. Поэтому я создал что-то более простое, но ,надеюсь, достаточно наглядное. Я сделал почти одинаковую страницу для каждого рассматриваемого элемента. Кроме тега h1 для заголовка, больше разметки не было. Вместо этого сниппет на JavaScript внизу каждой страницы создавал 200000 рассматриваемых элемента и добавлял их в тело страницы. Для каждой страницы эта функция выглядела так:

;(function TimeThisMother() {

 window.onload = function(){

  setTimeout(function(){

  var t = performance.timing;

   for (var index = 0; index < 200000; index++) {

 var makeDiv = document.createElement("button");

 makeDiv.classList.add('a-button');

 makeDiv.textContent = 'это число кнопок ' + index;

 document.body.appendChild(makeDiv);

   }

   console.log("JS took: " + ( (t.loadEventEnd - t.responseEnd) / 1000) + " секунд, требующихся на создание элемента");

 }, 0);

 };

})();

Ясно, что добавлять столько элементов на страницу нелепо. Однако, это позволяет нам чётко видеть (вы сможете увидеть разницу своими глазами) разницу в производительности между этими элементами.

Время загрузки создания элементов при помощи JavaScript

Я также использовал «тайминги навигации», чтобы измерить, сколько времени потребовалось JavaScript для создания элементов. Это значение записывается в консоли в инструментах разработчика. Однако, я не думаю, что это о многом говорит (помимо того, что Safari ЧЕРТОВСКИ быстрее по сравнению с Chrome/Firefox). Намного показательнее то, что после того, как элементы созданы, они должны отобразиться на странице. Вот где открывается настоящая пропасть между button и, всем остальным.

Тесты с createElement для каждого элемента

Нажмите любую из следующих ссылок, и как только вы это сделаете, запустите секундомер. Остановите его, когда увидите контент, который появится под тегом h1. Повторите этот текст и я думаю, вы получите такой же результат, что и я: button тормозит. Почти всегда в два раза медленнее чем любой другой элемент. В Chrome у меня на локальной машине любые из структурных элементов (divsectionnav и т.д.) занимают около 7 секунд. button занимает около 14 секунд.

Кроме того, если вам угодно взглянуть на результаты, которые я запустил через Web Page Test, то вот сравнение div и button:

  • div отображается за 9.6 секунд (среднее по 3 запускам)
  • button отображается за 15.38 секунд (среднее по 3 запускам)

Чёёё? Вот, попробуйте сами. Попробуйте сейчас же:

Если вы желаете посмотреть эти примеры на GitHub, то вы можете сделать это здесь: https://github.com/benfrain/css-performance-tests

Кнопка, чё за фигня? Это из-за твоих стилей?

Моей первой мыслью было, что это должно быть из-за стилей. Во всех браузерах на <button> навешивается столько визуального багажа из браузерных стилей, что это наверняка сказывается на времени отрисовки? В попытке нейтрализовать браузерную таблицу стилей (на данный момент я проверял в основном в Chrome), я добавил следующие переопределения:

button.a-button {

 height: 30px;

 width: 100%;

 display: block;

 font: inherit;

 text-align: initial;

 padding: 0;

 box-sizing: content-box;

 border: 0;

 outline: 0;

 background-color: wheat;

 border-bottom: 1px solid #f90;

 text-transform: inherit;

 text-rendering: inherit;

 letter-spacing: inherit;

 text-indent: inherit;

 -webkit-appearance: none;

 -moz-appearance: none;

 appearance: none;

}

Кроме этого, я ещё до сих пор не могу понять, как добиться, чтобы текст начинался вертикально в том же самом месте, где и у других элементов (просто привести к единому виду раскладку между протестированными элементами)!

Кстати, если вы вдруг не знали, appearance: none; возвращается в стандарты. Оно определено в CSS-модуле базового пользовательского интерфейса 4 уровня. Однако, ни сейчас, а может, и никогда в будущем он не даст нам полного визуального контроля над кнопками.

Это палка о двух концах браузерных таблиц стилей для элемента button. Все браузеры не дают некоторым элементам полностью управляться через CSS. Тем не менее, вот к какой двойственной ситуации это приводит:

Браузер хочет, чтобы button всегда выглядела как button, так что люди могут всегда видеть, что это button (доступность — это хорошо, окей).

Разработчики хотят, чтобы button-ы выглядели так, как им хочется, а не так, как явные button-ы с дефолтными браузерными стилями (эстетика важна, окей)

Кроме того, если я не ошибаюсь, Firefox даже не позволяет button быть flex-контейнером (и это, наверное, правильно, согласно спецификации)

Мы можем плясать вокруг этого целый день, но я всё-таки осмелюсь предположить, почему люди не используют элементы button; они выглядят ужасно и никакие эпизодические CSS-кодеры не знают, как заставить их вести себя как надо.

Изначально я был убеждён, что браузерные стили были единственным важнейшим источником проблемы со скоростью, но когда я связался с ребятами из команды разработчиков Chrome, они добавили еще версии:

Ben Frain @benfrain

Тест показывает, что элемент button в два раза медленнее создавать + отображать, чем div при помощи createElement(). Это нормально?.

@benfrain @paul_irish

Можно мне увидеть тест? Я могу представить, что теневая DOM для button гораздо сложнее, чем для простого div, так что да.

Говоря о button в связи с Flex, Даниел Холберт высказал интересную догадку в этом комментарии на bugzilla.

<button> не реализуется (браузерами) на чистом CSS, поэтому с точки зрения CSS они немного «чёрные ящики». Это означает, что они не обязаны вести себя так же, как, например, и <div>.

Хм, больше информации, больше проблем: я хочу использовать button там, где это имеет смысл с точки зрения DOM, семантически. Однако, я не хочу дефолтных стилей для состояний. Я хочу добавлять их сам. Видимо, это тупик?

В этом момент, я представляю хипповатых сторонников чистой семантики, неодобрительно покачивающих головами, поглаживая свою бороду (в том числе женщины) и потягивая шоколадное мокачино из экологически чистых зерен с молоком. Я знаю, что они думают: я так легко был побеждён, я сдался без боя армии супа из div.

Не знаю, что вам сказать. Я не горжусь этим, но правда в том, что button в этом сценарии — сплошная боль.

Вот аналогия. Это напоминает мне, как я хожу выпить с моим братом. К концу вечера он нажирается сверх меры, начинает дурить и втягивает нас обоих в переделку. Я защищаю его, он же мой брат (девушки, я полагаю, вам надо быть благоразумнее), но между нами — он полный балбес, признаю. В следующий раз, когда брат позовет меня по городским заведениям, я откажусь.

Но я отвлёкся.

Заключение

Смотрите, я допускаю, что кнопка по умолчанию делает такие вещи, которые другие элементы не делают. У неё есть состояния, оформление по умолчанию, и самое главное — смысловое (понятное) значение. Для подавляющего большинства «кнопочных» сценариев использования, button — «правильный выбор».

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

Мы все читали статьи, в которых нам говорили, что мы должны использовать button конкретно для кнопок (в отличие от ссылок или иных элементов). Я полностью согласен с этим утверждением; это моя позиция по умолчанию. Вам ведь не понадобится 200000 элементов button на странице (если только вы не делаете полный каталог для фирмы «Мир кнопок»), так что это не проблема?

Нет, это реальная проблема. Вполне возможно, что организации/продукты, которым приходится выбирать между использованием «правильного» или самого быстрого элемента, сделают выбор в пользу самого быстрого. Когда каждая миллисекунда на счету, любой проигрыш в скорости для элемента может стать началом медленного мучительного конца.

Я никоим образом не призываю отказываться от button. Если можно использовать её, используйте. Вместо этого я надеюсь, что в будущем появится способ использовать элемент button с семантической точки зрения, без браузерного багажа и не жертвуя скоростью. Наверное, лучшее, чего мы можем добиться в этой ситуации сейчас — обеспечить, чтобы у нашего элемента хотя бы стоял role=«button» для скринридеров.

До тех пор, пока W3C не предоставит что-нибудь типа button-style:none;, разработчики будут снова и снова выбирать другой элемент. Из-за трудностей при визуальном изменении button и сравнительной разницы в производительности становится труднее искренне утверждать: «Для этого нужно использовать button».

Share

Тебе может это понравится...