Search code examples
cssperformancepseudo-elementcss-content

Is declaring "content" property on :before and :after for every element a massive performance issue?


As you probably know, if you will to use :before and/or :after pseudoelements without setting text in it, you still have to declare content: ''; on them to make them visible.

I just added the following to my base stylesheet :

*:before, *:after {
    content: '';
}

...so I don't have to declare it anymore further.

Apart from the fact the * selector is counter-performant, which I'm aware of (let's say the above is an example and I can find a better way to declare this, such as listing the tags instead), is this going to really slow things down ? I don't notice anything visually on my current project, but I'd like to be sure this is safe to use before I stick it definitely into my base stylesheet I'm going to use for every project...

Has anyone tested this deeply ? What do you have to say about it ?

(BTW, I do know the correct CSS3 syntax uses double semicolons (::before, ::after) as these are pseudoelements and not pseudoclasses.)


Solution

  • So I ran some tests based on @SWilk's advice. Here's how I did it :

    1) Set up a basic HTML page with an empty <style> tag in the <head> and the simple example he provided in a <script> tag at the bottom of the <body> :

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="utf-8" />
        <title>Performance test</title>
    
        <style>
    /**/
        </style>
    </head>
    
    <body onload="onLoad()">
        <div class="container"></div>
    
        <script>
    function onLoad() {
        var now = new Date().getTime();
        var page_load_time = now - performance.timing.navigationStart;
        console.log("User-perceived page loading time: " + page_load_time);
    }
        </script>
    </body>
    
    </html>
    

    2) Fill up the div.container with loaaads of HTML. In my case, I went to html-ipsum.com (no advertising intended), copied each sample, minified it all together, and duplicated it a bunch of times. My final HTML file was 1.70 MB, and the div.container had 33264 descendants (direct or not ; I found out by calling console.log(document.querySelectorAll('.container *').length);).

    3) I ran this page 10 times in the latest Firefox and Chrome, each time with an empty cache.

    Here are the results without the dreaded CSS ruleset (in ms) :

    Firefox :
    1785
    1503
    1435
    1551
    1526
    1429
    1754
    1526
    2009
    1486
    Average : 1600
    
    Chrome :
    1102
    1046
    1073
    1028
    1038
    1026
    1011
    1016
    1035
    985
    Average : 1036
    

    (If you're wondering why there's such a difference between those two, I have much more extensions on Firefox. I let them on because I thought it would be interesting to diversify the testing environments even more.)

    4) Add the CSS we want to test in the empty <style> tag :

    html:before, html:after,
    body:before, body:after,
    div:before, div:after,
    p:before, p:after,
    ul:before, ul:after,
    li:before, li:after,
    h1:before, div:after,
    strong:before, strong:after,
    em:before, em:after,
    code:before, code:after,
    h2:before, div:after,
    ol:before, ol:after,
    blockquote:before, blockquote:after,
    h3:before, div:after,
    pre:before, pre:after,
    form:before, form:after,
    label:before, label:after,
    input:before, input:after,
    table:before, table:after,
    thead:before, thead:after,
    tbody:before, tbody:after,
    tr:before, tr:after,
    th:before, th:after,
    td:before, td:after,
    dl:before, dl:after,
    dt:before, dt:after,
    dd:before, dd:after,
    nav:before, nav:after {
        content: '';
    }
    

    ...and start again. Here I'm specifying every tag used in the page, instead of * (since it is counter-performant in itself, and we want to monitor the pseudo-element triggering only).

    So, here are the results with all pseudo-elements triggered (still in ms) :

    Firefox :
    1608
    1885
    1882
    2035
    2046
    1987
    2049
    2376
    1959
    2160
    Average : 1999
    
    Chrome :
    1517
    1594
    1582
    1556
    1548
    1545
    1553
    1525
    1542
    1537
    Average : 1550
    

    According to these numbers, we can conclude the page load is indeed slower (of about 400-500 ms) when declaring content: '' on every pseudo-element.

    Now, the remaining question now is : is the extra load time we can see here significative, given the relatively big test page that was used ? I guess it depends on the size of the website/project, but I'll let more web-performance-knowledgeable people give their opinion here, if they want to.

    If you run your own tests, feel free to post your conclusions here as well, as I'm very interested in reading them - and I think I won't be the only one.