up

Zoompf's Web Performance Blog

Browser Performance Problem with CSS “print” Media Type

 Billy Hoffman on December 7, 2009. Category: Optimization
TwitterLinkedInGoogle+FacebookShare

I ran across an article today that shocked me. Geert De Deckere wrote how you can save an HTTP request by combining the CSS files for the print and screen media types.

Wait, I thought. What? Why do I need to do this? What behavior is this correcting? I was very confused. Maybe you are too.

image of CSS code in an editor

CSS allows you to define styling information for different media types. CSS could tell a browser that is rendering to a TV screen to style the same content differently from a browser rendering on a mobile phone. The HTML content is all the same. Media types simply define which style rules apply for which devices. CSS also defines a print media type which is the style to use when styling a page that is being printed. Browsers should be smart about only downloading the style sheet with the media type for the device they are rendering. Firefox on my laptop is should not fetch the mobile.css style sheet whose media type is handheld. And luckily browsers are smart and don’t download CSS files for media types that they don’t support.

Except for the CSS media type print.

Geert’s article and advice were predicated on the claim that web browsers will download external style sheets with the print media type even if you don’t print the page. Is this true? To find out I built a quick test page:

<html> <head> <title>CSS Media Tests</title> <link rel="stylesheet" type="text/css" href="screen.css" media="screen" /> <link rel="stylesheet" type="text/css" href="print.css" media="print" /> </head> <p> Hello! <img src="new-logo.png"> </p> </html>

Wow! Geert’s claim was true! All major desktop browsers that I tested (Firefox 3.5, IE 7, Chrome 3.0 and Safari 4.0) will download external style sheets whose media type is print even if you don’t print the page! This is hurts performance for no good reason. Currently your browser must make one HTTP request and download screen.css. But then your browser has to make an additional HTTP request to download a file full of content that it does not need. Worst of all, the browser will not start rendering the page until it has grabbed the completely unused print.css file!

This is very silly behavior. Especially given that virtually none of your website visitors are going to print any of your web pages, unless you are a website like Google Maps. Try and remember: “when was the last time your printed a web page?” Unfortunately all 4 browser I tested all downloaded print.css even though I never printed the page. Firefox, IE, and Chrome all downloaded print.css in order as if it was a external CSS file whose media type was screen. Looking through a proxy the request order was:

  1. css-media-test.html
  2. screen.css
  3. print.css
  4. new-logo.png

Safari 4.0 however, downloaded the content in this order:

  1. css-media-test.html
  2. screen.css
  3. new-logo.png
  4. print.css

Safari was smart enough to defer downloading but did still downloaded it. I do not know if Safari delayed firing the window.onload event until after print.css downloaded or not. WebPageTest confirms that IE does not start rendering the page until print.css is downloaded. The fact that Firefox and Chrome both requested content in the same order as IE leads me to think they also delay rendering.

Possible Solution?

Geert proposed a solution to this problem. He recommends combining the two external CSS files into a single CSS file and use @media directives inside the CSS file to separate the style info for screen from the style info for print. You end up with a single CSS file that looks like this

@media screen { /* contents of screen.css here */ } @media print { /* contents of print.css here */ }

This solution does not sit well with me. Yes, by combining the two CSS files and using @media directives you can remove an HTTP request. You now only have to download a single CSS file whose size will be smaller than the sum of the two original file sizes because a single large file will compress better. However your visitors still have to download a large amount of CSS content. 30-40% of that content is printer-centric style information which no one will ever actually use anyway, and the browser will not start to render the page until all this useless data has been downloaded. (Interestingly enough Zoompf free Web Performance Scan checks style blocks and CSS files for @media directives and recommends you break them into separate style sheets to prevent unnecessary rules from being downloaded. I had to modify the check to allow @media print directives when I found this solution.)

A Different Solution

I believe there is a different and perhaps better solution. You can defer downloading print.css by using JavaScript to dynamically add a <LINK> tag pointing to the external CSS file with the print media type after the page has loaded! This solution means the browser only needs to make 1 HTTP request and less CSS content needs to be downloaded to start drawing the page. This will have a faster “Time to Render” than a single CSS file as less data is downloaded. The extremely small number of people who do print your web page will still get the style sheet necessary for them to print. You can also use a <NOSCRIPT> tag in the <HEAD> to link to print.css. This means anyone who has JavaScript turned off will the performance hit all of your visitors are currently taking and request both external style sheets. The deferring print.css solution looks like this:

<html> <head> <title>CSS Media Tests</title> <link rel="stylesheet" type="text/css" href="screen.css" media="screen" /> <noscript> <link rel="stylesheet" type="text/css" href="print.css" media="print" /> </noscript> </head> <p> Hello! <img src="new-logo.png"> </p> <script> window.onload = function() { var cssNode = document.createElement('link'); cssNode.type = 'text/css'; cssNode.rel = 'stylesheet'; cssNode.href = 'print.css'; cssNode.media = 'print'; document.getElementsByTagName("head")[0].appendChild(cssNode); } </script> </html>

You reduce initial request count and download size at the cost of greater complexity and more markup. This code could be improved. A more scalable solution would be for the JavaScript code to look in the <HEAD> and parse any <LINK> tags inside of a <NOSCRIPT> with a print media type and create new LINK elements dynamically.

Solving the problem

A summary of the problems and the two solutions appears below. This table assumes two CSS files (screen.css and print.css) each 30 kilobytes and size and a combined CSS file (all.css) whose size is 55 kilobytes.

MethodHTTP Requests before “Start Render”CSS Downloaded before “Start Rendering”# HTTP Requests after “Onload”Content Download after “Onload”
No Optimization260 Kb00 Kb
Single CSS file all.css155 Kb00 Kb
Deferring print.css130 Kb130 Kb

Which solution works best will vary with your situtation. The status quo is 2 HTTP requests to deliver 60 Kb of content before the browser can start rendering. A single CSS file reduces that to 1 HTTP requests and 55 Kb of content before the browser can start rendering. Deffering print.css also only requires 1 HTTP request before pageload but only sends 30 Kb before the browser can start rendering. If you have a small print.css file it might be better to use a single CSS file with @media directives. The overhead of serving a single larger CSS file containing unused style dat aand the delay that adds until the browser can start rendering might be so small it does not matter. However if you have a larger print.css file deferring the print.css download until after page load would provide a great performance benefit.

The moral of the story here is that the browser creators need to remove this performance defect from their code. Ideally the print CSS media type data should not be downloaded until the print dialog box appears, either from user action or using window.print() in JavaScript. Next best solution would be for the browser to automatically defer the downloading of “print” CSS media type data until after the page has downloaded. In the mean time, you can use either the single CSS file solution or the deferring print.css solution to make your web pages load faster!

Want to see what performance problems you have? An appropriately placed <LINK> tag and proper use of CSS @media directives are just two of the 200+ performance issues Zoompf detects while assessing your web applications for performance. You can sign up for a free mini web performance assessment at Zoompf.com today!

Comments

    December 7, 2009 at 3:20 pm

    Could you also effectively do about the same thing by just putting your tag for your print style at the very bottom of the page, that way it gets a much lower loading priority compared to the screen? That is, at least until we get fully internet-aware printers that surf and print automatically… that’ll be interesting. :)

    December 7, 2009 at 5:02 pm

    Kyle,

    Good question. I just ran a test and putting a tag at the bottom of your HTML, even if it has a “print” media type, delays the “start render” until all the style sheets has been downloaded. This makes everything much was as nothing gets drawn until everything gets downloaded!

    check out the webpagetest results: http://www.webpagetest.org/result/091207_f078bac18b54823e149c6e87aea8c96c/1/details/

    January 7, 2010 at 2:03 am

    “This is very silly behavior. Especially given that virtually none of your website visitors are going to print any of your web pages, unless you are a website like Google Maps. ”

    It was amusing to read this line off of a printed version of this post, during lunch, few days ago :)

    I think the “problem” with print stylesheets is generally not that severe, and some of the advise given here is somewhat harmful when employed on the public web.

    First of all, it’s hard to imagine a print stylesheet of a large size. Not in a typical scenario. In my experience, print stylesheets usually consist of a rather small set of rules; rules that mainly cancel out some of the screen formatting. “A list apart” is a good example of an article-centered site, and its print stylesheet (http://www.alistapart.com/css/print.css) is only a handful of declarations — hiding few things, displaying anchors urls, normalizing positoning, etc. I can’t imagine a print stylesheet 30-40% size of a main one (as you mention). It’s more like 1-5%, or even less. Given proper compression and minification, the additional weight of print-related rules becomes even less significant.

    Second, providing print stylesheet via Javascript is really terrible for accessibility. Why should people with Javascript disabled, not available or stripped off receive worse treatment? NOSCRIPT-based fallback is also a poor solution (see, for example, this comment on my blog — http://perfectionkills.com/optimizing-html/#comment-56958). In a typical scenario, where print declarations are of reasonable size, I really don’t think it’s worth employing Javascript-based solution.

    And speaking of browsers downloading print stylesheets at load time, the default behavior is not that unreasonable. What if a user goes offline after opening a document and then tries to print it (perhaps, reading a blog post first, on screen, and then wanting to finish it while commuting back home from the office — this time in a printed form; something I do pretty often).

    January 7, 2010 at 11:49 am

    Let’s not throw the baby out with the bathwater! Lets discuss the problem and possible solutions separately:

    Many websites do not contain print specific style information. However 100% of visitors to a website with a separate CSS file for print rules will make an HTTP request for a resource (print.css) whose content is actually used less than 100% of the visitors. Thus we have identified a performance problem for websites with a separate printer specific style data: an unnecessasry HTTP request. It does not matter whether print.css contains 3 rules or 300 rules. Its an unnecessary request and we performance junkies love to reduce HTTP reqeusts!

    I believe this post has value if for only bringing this performance problem to the attention of a wider audience.

    I was only able to find one published solution to this problem: concat together the print style information and use @media. This seems like a good idea and it aligns with our existing mindset of “concat resources together to reduce HTTP requests.”

    However this solution doesn’t seem like a good solution if your print.css is larger than “small” or if only a “small” subset of visitor print your webpage onto paper. In fact, it becomes a worse solution as the size of the print specific rules increases and/or as the number of users who are actually printing decreases.

    So, I propose another solution that I think is kind of sexy: Using JavaScript to lazy load printer specific CSS! Which solution works for you will depend on your site design and traffic patterns the same way whether you should inline CSS and JavaScript or use external files depends on your site design and traffic patterns.

    Is there a downside to the second solution? Sure. Its still 2 HTTP requests though 1 is deferred. There could be an accessibility issue (though the impact to accessibility of using JavaScript to lazy load print style information is debatable). The <NOSCRIPT> fallback should work just fine except:

    -If you are on a site with printer specific style information in a separate file…
    -and you have JavaScript enabled…
    -and you are on a network with a device that is selectively removing some JavaScript…
    -and it selectively removes the JavaScript that lazy loads the style sheet…
    -and you decide to print that web page…

    then you will not print it using the proper style rules

    I don’t think there will be much of a loss of life or limb because of that ;-)

    I completely agree with you (and said so in the article) that if you have a small print.css then the JS solution does not make sense. You have convinced me that downloading printer information if its in a separate file at page load makes sense. The “online but then go offline and print” is a valid use case. As I mentioned in the article, browsers should defer downloading external printer specific style sheets until after window.onload has completed.

    January 7, 2010 at 4:47 pm

    Thanks for clarification. It is now clear to me that, for the most part, we agree with each other :)

    The reason I’m recommending to avoid NOSCRIPT elements is because I see them as an inferior alternative to graceful degradation. We have two ways to provide certain content to script-less clients; one — where content is present from the start, and is then hidden/removed/transformed by a script; and another — where content is stuffed into NOSCRIPT, and the job of displaying it is essentially delegated to a browser.

    The problem is that SCRIPT’s are known to be stripped by corporate firewalls and/or proxies, and if Javascript is enabled, NOSCRIPT-based degradation turns out to be unsuccessful. Why prefer solution that has known flaws if there’s another alternative that has none and costs just as much?

    Now, in this particular situation, the problem is not that apparent of course, as you rightfully explained. The subset of people who would suffer from SCRIPT removal _and_ needed to print a page is probably insignificant in a typical scenario. However, we could still try to avoid faulty NOSCRIPT.

    Why not include print stylesheet the usual way (then remove it with script)? Potential problem I see here is that script would need to be positioned before link/style element. I’m also not sure if downloading of stylesheet is delayed while script positioned before is executed. If it isn’t, then removing element is pointless, as request might have already been sent.

    February 3, 2010 at 5:54 pm

    Social comments and analytics for this post…

    This post was mentioned on Twitter by zoompf: All browsers hurt Performance by downloading CSS w/ print media type even if you don’t print! Problem and fix here: http://bit.ly/6pXo5E

    February 16, 2010 at 6:53 am

    [...] Browser Performance Problem with CSS “print” Media Type (December 2009) [...]

    March 15, 2012 at 7:26 am

    [...] it has loaded a style sheet with the media type of print. #seriously #testyours. This article on Browser Performance Problem with CSS “print” Media Type outlines the problem really [...]

    September 28, 2012 at 9:14 pm

    [...] de la imágenes: Ayuda WordPress | Zoompf Etiquetas: Cascading Style Sheets, CSS, Hojas de Estilo, Programación, [...]


Leave a Reply