The <button> element is considerably slower to create in JavaScript, slower to render by the browser and notoriously difficult to style consistently cross browser. Despite being the ‘right’ choice for button duties, it’s not necessarily the ‘best’ choice.

Picture the scene. I’m trying to convince a principal software engineer to use more semantic elements in markup rather than purely generic div and span elements. Semantic elements like section, header, footer, and nav. The conversation went something like this. “We’ll need to test they aren’t any slower to create with JavaScript”, he points out. “Sure, no problem”, I reply, brimming with confidence. “And buttons”, he added, “You’ll need to test buttons, as we’d need to use them all over the place”.

I knew it wasn’t going to end well for the button right then. You see, button is a bit different. It’s like radio boxes and fieldsets: it does ‘things’.

Do you have a few minutes? I’m going to tell you why arguing the case for using the button element can be a losing wicket for complex web projects.

Testing the performance of buttons via createElement in JavaScript

First of all, I needed data to ensure that creating semantic elements (section, nav, a, button etc) in JavaScript was no slower than creating a div or a span.

I don’t write smart tests. I’m not clever enough. I certainly wouldn’t get through a FizzBuzz test. So I created something simplistic but hopefully illustrative instead. I made an almost identical page for each of the elements in question. Apart from an h1 tag for a title, there was no markup. Instead, a snippet of JavaScript at the bottom of each page created 200,000 of the element in question and appended them to the body of the page. For each page, that function looks like this:

;(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 = 'this is button number ' + index;
                document.body.appendChild(makeDiv);
            }
            console.log("JS took: " + ( (t.loadEventEnd - t.responseEnd) / 1000) + " seconds to create the elements");
        }, 0);
    };
})();

This is clearly a ridiculous number of elements to add to a page. However, it enables us to clearly see (you’ll be able to see the difference with your own eyes) the performance difference between these elements.

Timing the creation of elements with JavaScript

I also used navigation timing to measure how long it took the JavaScript to create the elements. That value is logged to the console in your dev tools. However, I don’t think that tells us a lot (other than the fact that Safari is BLAZINGLY fast compared to Chrome/Firefox). What’s more illustrative is that after the elements are created, they need to be rendered to the page. This is where the real gaps open up between button and, well, everything else.

The createElement tests for each element

Click any of the following links, and as you do, start a stopwatch. You’ll want to stop it when you see content appear beneath the h1 tag. Repeat this test and I expect you will get the same result as me: button is slow. Almost always twice as slow than any other element. In Chrome on my local machine any of the structural elements (div, section, nav etc) takes around 7 seconds. A button takes around 14 seconds.

Alternatively, if you would like to view some results I ran through Web Page Test, you can view a comparison of the div and button here:

Whhhattttt?????? Go on – do it. Do it now:

If you’d rather peruse them on GitHub they are here: https://github.com/benfrain/css-performance-tests

button, WTF? Is it your styles?

My initial reaction was that it must be due to styles. In any browser, the button gets so much visual baggage from the User Agent, it must surely add to the render time? In an effort to mitigate the User Agent style sheet in (I was testing mainly in Chrome at this point) I added all the following overrides:

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;
}

All that, and still I can’t figure out how to get the text starting vertically at the same place as the other elements (just to normalise the layout between the tested elements)!

By the way, in case you missed it, appearance: none; is back on the standards track. It’s defined in the CSS Basic User Interface Module Level 4. However, it doesn’t currently, and probably won’t ever give us full visual control over buttons.

This is the double-edged sword of User Agent style sheets for the button element. All UAs refuse to let certain elements controlled entirely with CSS. However, here’s the duality of the situation that creates:

The user agent wants a button to always look like a button so that people can always see it is a button (accessibility is good, M’kay).

Authors want buttons to look how they want, so they don’t look like an obviously UA styled button (aesthetics are important, M’kay).

Also – last time I checked, Firefox won’t let button be a flex container either (and it’s probably correct according to the specs).

We can dance around this all day but I venture it’s the primary reason people don’t use button elements; they look awful and casual CSSers don’t know how to make them behave.

I was initially convinced that the inherent styles were single biggest contributing factor to the speed issue but when I reached out to the Chrome Dev team folks they added further possibilities:

Talking about button in relation to Flex, Daniel Holbert, also had some interesting insight in this comment on bugzilla

<button> is not implementable (by browsers) in pure CSS, so they are a bit of a black box, from the perspective of CSS. This means that they don’t necessarily react in the same way that e.g. a <div> would.

Hmmm. Mo info, mo problems: I want to use a button where is makes sense in terms of the DOM, semantically. However, I don’t want the default styles for states. I want to add them myself. I’m guessing this is an impasse?

At this point, I imagine hippy semantic purists shaking their disapproving heads, stroking their beard (including the women) and sipping their ethically sourced Mocha-chocca-latte-chino. I know what they are thinking: I’ve been beaten so easily, I’ve capitulated with ease to the div-soup army.

I don’t know what to tell you. I don’t feel great about it but the truth is that the button, in this scenario, is just a whole load of pain.

Here’s an analogy. It makes me feel like going out for drinks with my brother. Towards the end of the night he’s drunk too much, started acting up and gets us both into a brawl. I back him up because he’s my brother (girls, I’m assuming you’d have more sense) but privately I’m acknowledging he’s been a complete dick. Next time, when my brother asks if I want to go out on the town, I’ll decline.

But I digress.

Summary

Look, I accept that button does things, by default, that other elements don’t. It has states, default styling, and most importantly semantic (accessible) meaning. For the vast majority of ‘buttony’ use cases, the button element is the ‘right thing to use’.

But the specification and user agents make no provision for when we want the semantics of the element without the baggage. I’m sure there’s a reason we can’t but that doesn’t solve the problem at hand.

We’ve all read articles telling us that we should use the button element for things that are buttons (as opposed to links or otherwise). I totally agree with their sentiment; that is my default position. And realistically, you won’t want 200,000 button elements on your page (unless you’re designing the full page inventory for ‘Buttons r us’) so this is a none issue right?

No. It’s a real issue. It’s entirely possible that businesses/products that need to decide between using the ‘right’ element, against using the fastest element, are going to opt for the fastest. When every millisecond adds up, conceding speed on an element can be the start of ‘death by a thousand cuts’.

I’m in no way advocating giving up on button. If you can use it, you should. Instead I’m hoping that in the future there will be a way to have the button element from a semantic perspective, without the UA baggage and speed penalty. The best we can probably achieve in this scenario currently is ensuring that at least role="button" is on the element in question for screen readers.

Unless the W3C provides something akin to button-style: none;, there will be times when authors continue to choose a different element. The difficulty in amending a button visually and the comparative performance difference makes it more difficult to wholeheartedly argue, “We should be using a button for this”.