CSS delivery optimization: How to defer css loading?
Asked Answered
C

8

68

I am trying to optimize the CSS delivery following the google documentation for developers https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery#example

As you can see in the example of inlining a small CSS file the critical CSS in inlined in the head and the original small.css is loaded after onload of the page.

<html>
  <head>
    <style>
      .blue{color:blue;}
    </style>
    </head>
  <body>
    <div class="blue">
      Hello, world!
    </div>
  </body>
</html>
<noscript><link rel="stylesheet" href="small.css"></noscript>

My question regarding this example:

How to load a large css file after onload of the page?

Club answered 15/10, 2013 at 6:47 Comment(4)
i'm confused why a small css and a large css would be any different. i'm also confused by the <noscript>. how is the small.css loaded if javascript is enabled?Phiona
The small.css is loaded via javascript if enabledClub
After reading developers.google.com/web/fundamentals/performance/… I wonder if it would work just to apply display:none to all of the sections below the fold. Then after the page loads add a css to make the major sections block again. Since " (display: none) removes the element entirely from the render tree such that the element is invisible and is not part of layout."Filum
loadCSS does that for youBenjamin
V
51

If you don't mind using jQuery, here is a simple code snippet to help you out. (Otherwise comment and I'll write a pure-js example

function loadStyleSheet(src) {
    if (document.createStyleSheet){
        document.createStyleSheet(src);
    }
    else {
        $("head").append($("<link rel='stylesheet' href='"+src+" />"));
    }
};

Just call this in your $(document).ready() or window.onload function and you're good to go.

For #2, why don't you try it out? Disable Javascript in your browser and see!

By the way, it's amazing how far a simple google search can get you; for the query "post load css", this was the fourth hit... http://www.vidalquevedo.com/how-to-load-css-stylesheets-dynamically-with-jquery

Vassily answered 15/10, 2013 at 6:55 Comment(6)
Thanks for the jquery function I will definitely use it! For #2 the disable option has been removed from the tools in firefox up to version 23 and I had to use the about:conf entries. I confirm the css is loaded using noscript out from html.Club
@Vassily you have an error in your code it should be $("head").append('<link href="'+src+'" rel="stylesheet" type="text/css">');Fanchan
@KarimSamir Back in the day, you needed to wrap it in $ to instantiate it as a DOM element. Not sure if this is still the case.Vassily
It only solved home page's CSS delivery rule to 100/100. Doesn't work on all other page. any idea? Other pages are still showing "Eliminate render-blocking JavaScript and CSS in above-the-fold content" issue.Downfall
this script may defer CSS, but it render blocks the page with jquery file.so it's not recommended as a good solution.Cavendish
It's 2021, using jQuery should not be the accepted answer.Jehovah
W
39

A little modification to the function provided by Fred to make it more efficient and free of jQuery. I am using this function in production for my websites

        // to defer the loading of stylesheets
        // just add it right before the </body> tag
        // and before any javaScript file inclusion (for performance)  
        function loadStyleSheet(src){
            if (document.createStyleSheet) document.createStyleSheet(src);
            else {
                var stylesheet = document.createElement('link');
                stylesheet.href = src;
                stylesheet.rel = 'stylesheet';
                stylesheet.type = 'text/css';
                document.getElementsByTagName('head')[0].appendChild(stylesheet);
            }
        }
Weichsel answered 7/3, 2014 at 16:14 Comment(2)
This solution works perfectly, but the loaded CSS file is never cached, meaning there's always an 'unstyled' flash before it loads. I am using critical CSS for very basic CSS, but it's still a very drastic change between the two. Any way to cache the file as it's dynamically loaded?Tetrabasic
@Tetrabasic Chrome 80 works fine, on Firefox 74 there is the cache problem you mentionedChilopod
H
25

This is how you do it using the new way, without using any 3rd party library or extra JS:

<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
  • link rel="preload" as="style" requests the stylesheet asynchronously.
  • The onload attribute in the link allows the CSS to be processed when it finishes loading.
  • "nulling" the onload handler once it is used helps some browsers avoid re-calling the handler upon switching the rel attribute.
  • The reference to the stylesheet inside of a noscript element works as a fallback for browsers that don't execute JavaScript.
Henricks answered 30/11, 2020 at 15:26 Comment(0)
M
14

There is a simple way to load CSS asynchronously with only link tag

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">
  1. media="print" tell browser to load CSS asynchronously that intended for print.
  2. Onload set the link's media to all when it loaded.
  3. Comapared to mahmoud answer, this approach doesn't prioritize fetching the css, which maybe not needed

More details are explained here

Mesotron answered 25/6, 2021 at 13:38 Comment(1)
I usually browse StackOverflow anonymously but I went out of my way to login and upvote this because it is so elegant.Catha
C
11

In addition to Fred's answer:

Solution using jQuery & Noscript

<html>
  <head>
    <style>
      .blue{color:blue;}
    </style>
    <script type="text/javascript" src="../jquery-1.4.2.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function(){
        if($("body").size()>0){
                if (document.createStyleSheet){
                    document.createStyleSheet('style.css');
                }
                else {
                    $("head").append($("<link rel='stylesheet' 
                    href='style.css' 
                    type='text/css' media='screen' />"));
                }
            }
        });
    </script>
    </head>
  <body>
    <div class="blue">
      Hello, world!
    </div>
  </body>
</html>
<noscript><link rel="stylesheet" href="small.css"></noscript>

from http://www.vidalquevedo.com/how-to-load-css-stylesheets-dynamically-with-jquery

Using pure Javascript & Noscript

<html>
  <head>
    <style>
      .blue{color:blue;}
    </style>
    <script type="text/javascript">
          var stylesheet = document.createElement('link');
          stylesheet.href = 'style.css';
          stylesheet.rel = 'stylesheet';
          stylesheet.type = 'text/css';
          document.getElementsByTagName('head')[0].appendChild(stylesheet);
    </script>
    </head>
  <body>
    <div class="blue">
      Hello, world!
    </div>
  </body>
</html>
<noscript><link rel="stylesheet" href="small.css"></noscript>
Club answered 12/1, 2014 at 16:8 Comment(5)
i'm confused at how the second one's javascript is different from just putting the link at the end of the head.Phiona
The first solution requiers the jQuery library whereas the second one doesn'tClub
I'm not asking the difference between the first and second. The second doesn't wait for DOMContentLoaded so it seems to be the same as <link href="style.css" rel="stylesheet" type="text/css">.Phiona
@Phiona Your statement is correct. It looks like current solutions accept the trade off of not deferring loading when JavaScript is disabled (under the assumption that it is hopefully uncommon).Ganges
Isn't having anything after a </html> tag invalid HTML?Hollowell
A
9

Try this snippet

The author claims it was published by Google's PageSpeed Team

<script>
    var cb = function() {
    var l = document.createElement('link'); l.rel = 'stylesheet';
    l.href = 'yourCSSfile.css';
    var h = document.getElementsByTagName('head')[0]; h.parentNode.insertBefore(l, h); };
    var raf = requestAnimationFrame || mozRequestAnimationFrame ||
              webkitRequestAnimationFrame || msRequestAnimationFrame;
    if (raf) raf(cb);
    else window.addEventListener('load', cb);
</script>
Alcestis answered 12/10, 2015 at 18:48 Comment(5)
I applied this technical and my homepage passed Optimize CSS delivery rule, however, this not work in details page, I use the same script because this script in layout file.Bushcraft
Same here. It only optimize home page's CSS delivery rule. Doesn't work on all other page. any idea?Downfall
Did you check if other pages have additional stylesheets loaded ? Although i posted this answer I have never used this method. I load my CSS the old fashion way and try not to obsess with Google page speed score. Minify & optimize your css and work towards a better content to rank up.Alcestis
I think the author modified it a bit: see developers.google.com/speed/docs/insights/OptimizeCSSDeliveryArch
Using either of that code actually reduces my score, not improve. Before using their "suggested defer code" Score: 77. After using (either one). Score: 69. So uh... no thanks.Geralyngeraniaceous
S
5

WARNING: body{background-image: url("http://example.com/image.jpg");} in css files will make your css files still render-blocking.

If you tried all the solutions above and you still get the render-blocking warning from PageSpeed insights then you probably have this style rule in your css files. After hours of tests it turns out that this rule is what making ALL of my css to be flagged as render-blocking resources at PageSpeed insights. I found the same issue has been discussed before.

I don't know why body{background-image: url(...) do this for all the css files!, although I have different images resources in the file for the buttons, icons, ...etc .

I fixed this by moving this rule from the .css file and put it in the inline styles. Unfortunately, you will have to break your css plan and put the rule in all of your layouts HTML files instead of being in 1 css file that is imported in all of your HTML layouts, but the 90s and green color in PageSpeed insights deserve it.

Specialist answered 22/8, 2018 at 2:10 Comment(1)
Maybe using Base64 images (for small images like icons and logos) will help.Weanling
N
-1

Fixed mine by introducing placing all css files at the bottom of the page, after the body tag. but this introduces a new problem, the page loads un-styled html for noticeable mili seconds before applying the style. For this I fixed by introducing a splash screen all styled on the page.

Nuriel answered 15/11, 2019 at 10:41 Comment(3)
Similar to #54922849Connecticut
But My Answer was given in 2019, and the link you posted was just 2022Nuriel
I've just provided a link that extends your answer. The comment is not for you, but for any potential readers. Sorry if it caused a confusion.Connecticut

© 2022 - 2024 — McMap. All rights reserved.