66

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?

Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
RafaSashi
  • 16,483
  • 8
  • 84
  • 94
  • 1
    i'm confused why a small css and a large css would be any different. i'm also confused by the ` – Jayen May 06 '14 at 07:48
  • The small.css is loaded via javascript if enabled – RafaSashi May 06 '14 at 09:56
  • After reading https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-tree-construction 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." – JacobLett Aug 10 '16 at 15:08
  • 2
    [**loadCSS**](https://github.com/filamentgroup/loadCSS) does that for you – vsync Dec 27 '16 at 22:32

8 Answers8

50

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

iono
  • 2,575
  • 1
  • 28
  • 36
Fred
  • 4,195
  • 2
  • 29
  • 42
  • 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. – RafaSashi Oct 15 '13 at 07:22
  • @Fred you have an error in your code it should be $("head").append(''); – Karim Samir Feb 07 '16 at 22:53
  • @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. – Fred Feb 13 '16 at 01:36
  • 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. – Jonas T Oct 12 '16 at 00:56
  • this script may defer CSS, but it render blocks the page with jquery file.so it's not recommended as a good solution. – Don Dilanga Oct 03 '18 at 11:25
  • It's 2021, using jQuery should not be the accepted answer. – db2 Oct 07 '21 at 13:08
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);
            }
        }
Manube
  • 5,110
  • 3
  • 35
  • 59
Salvi Pascual
  • 1,788
  • 17
  • 22
  • 8
    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? – authorandrew May 03 '17 at 17:01
  • @authorandrew Chrome 80 works fine, on Firefox 74 there is the cache problem you mentioned – nulll Mar 16 '20 at 20:01
23

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.
Mahmoud
  • 456
  • 3
  • 13
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>
RafaSashi
  • 16,483
  • 8
  • 84
  • 94
  • 8
    i'm confused at how the second one's javascript is different from just putting the `link` at the end of the head. – Jayen May 06 '14 at 07:49
  • The first solution requiers the jQuery library whereas the second one doesn't – RafaSashi Jan 21 '16 at 14:33
  • 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 ``. – Jayen Jan 21 '16 at 20:44
  • @Jayen 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). – Taylor D. Edmiston Aug 05 '16 at 04:08
  • 2
    Isn't having anything after a ` – Sparky Oct 13 '16 at 20:10
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>
Raja Khoury
  • 3,015
  • 1
  • 20
  • 19
  • 1
    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. – Huy Nguyen May 31 '16 at 10:35
  • Same here. It only optimize home page's CSS delivery rule. Doesn't work on all other page. any idea? – Jonas T Oct 12 '16 at 00:07
  • 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. – Raja Khoury Oct 12 '16 at 01:55
  • I think the author modified it a bit: see https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery – rolu Oct 12 '16 at 21:05
  • 1
    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. – Shawn Rebelo Mar 30 '17 at 22:33
7

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

zulvkr
  • 137
  • 2
  • 6
  • I usually browse StackOverflow anonymously but I went out of my way to login and upvote this because it is so elegant. – Pascal Roget Nov 26 '22 at 16:41
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.

Accountant م
  • 6,975
  • 3
  • 41
  • 61
-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.

MrJaid
  • 89
  • 6