66

I need to check (in Javascript) if a CSS file is loaded and if not then to load it. jQuery is fine.

Lance Roberts
  • 22,383
  • 32
  • 112
  • 130
foreline
  • 3,751
  • 8
  • 38
  • 45

14 Answers14

94

Just check to see if a <link> element exists with the href attribute set to your CSS file's URL:

if (!$("link[href='/path/to.css']").length)
    $('<link href="/path/to.css" rel="stylesheet">').appendTo("head");

The plain ol' JS method is simple too, using the document.styleSheets collection:

function loadCSSIfNotAlreadyLoadedForSomeReason () {
    var ss = document.styleSheets;
    for (var i = 0, max = ss.length; i < max; i++) {
        if (ss[i].href == "/path/to.css")
            return;
    }
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = "/path/to.css";

    document.getElementsByTagName("head")[0].appendChild(link);
}
loadCSSIfNotAlreadyLoadedForSomeReason();
Andy E
  • 338,112
  • 86
  • 474
  • 445
  • 2
    how crossbrowser is `document.styleSheets` ? – jAndy Jan 18 '11 at 13:47
  • 50
    I understood the question as "how to check whether a css file is loaded or not" rather than "how to check if the `` element exists". The `` element may exist (and the path may be correct too) but it doesn't mean that the css file was successful loaded. So, re-wording the question : if a css file isn't loaded (communications problems with the cdn server for instance), how to load a (local) css (fallback) file instead? – JFK Mar 24 '14 at 17:40
  • @JFK: that probably deserves its own question on Stack Overflow. There's the `load` and `error` events, but they're not widely supported (IE and Safari don't support them, according to MDN). I'd probably look into the `document.styleSheets` collection again; you could check the number of rules in each stylesheet in the collection, if it's `0` then it probably failed to load. – Andy E Mar 25 '14 at 08:19
  • 16
    This is not the answer of this question. – Shnd Jan 17 '15 at 02:07
  • 1
    The question said "loaded", not "linked to". This answer checks if the stylesheet is linked to, but not if it actually loaded. See @RafaSashi 's answer for the correct answer to the question. – jmc Apr 24 '15 at 12:26
  • @jmc: I can't imagine a use case for that – if it's linked to and not loaded, it's likely that a) it's not the only resource on the page that failed to load, or b) reloading it won't help. I answered what I thought was being asked and, evidently, that was correct. – Andy E Apr 24 '15 at 12:57
  • @AndyE A downed CDN (or other server) results in this situation. Ideally the missing CSS and JS would be loaded from our own servers if the CDN doesn't load them. I agree it's unusual, but it's happened to me twice this week. – jmc Apr 24 '15 at 13:36
  • 1
    @AndyE just fyi - Like jmc, I occasionally encounter a random single script/css file that doesn't load, when all the others on the page do. Yes, they all come from the same domain, are loaded at the same time in a batch, and no, it's not some compatibility issue with the contents of the file. If the visitor reloads the page, the file in question successfully loads(in other words, a reload worked). My point is that it can and does happen, and the need to detect if a specific resource has loaded is legitimate. – goat Oct 29 '15 at 01:45
  • This doesn't work. The condition passes if the stylesheet is linked within the document whether the stylesheet has actually loaded or not. – Vince Dec 03 '19 at 11:21
16

I just had to write something like that and I wanted to share it. This one is prepared for multiple cases.

  • If there is no request for the css file (css file isn't linked ...)
  • If there is a request for the css file, but if it failed (css file is no longer available ...)

var styles = document.styleSheets;
for (var i = 0; i < styles.length; i++) {
    // checking if there is a request for template.css
    if (styles[i].href.match("template")) {
        console.log("(Iteration: " + i + ") Request for template.css is found.");
        // checking if the request is not successful
        // when it is successful .cssRules property is set to null
        if (styles[i].cssRules != null && styles[i].cssRules.length == 0) {
            console.log("(Iteration: " + i + ") Request for template.css failed.");
            // fallback, make your modification
            // since the request failed, we don't need to iterate through other stylesheets
            break;
        } else {
            console.log("(Iteration: " + i + ") Request for template.css is successful.");
            // template.css is loaded successfully, we don't need to iterate through other stylesheets
            break;
        }
    }
    // if there isn't a request, we fallback
    // but we need to fallback when the iteration is done
    // because we don't want to apply the fallback each iteration
    // it's not like our css file is the first css to be loaded
    else if (i == styles.length-1) {
        console.log("(Iteration: " + i + ") There is no request for template.css.");
        // fallback, make your modification
    }
}

TL;DR version

var styles = document.styleSheets;
for (var i = 0; i < styles.length; i++) {
    if (styles[i].href.match("css-file-name-here")) {
        if (styles[i].cssRules != null && styles[i].cssRules.length == 0) {
            // request for css file failed, make modification
            break;
        }
    } else if (i == styles.length-1) {
        // there is no request for the css file, make modification
    }
}

Update: Since my answer got a few upvotes and this led me to revise the code, I decided to update it.

// document.styleSheets holds the style sheets from LINK and STYLE elements
for (var i = 0; i < document.styleSheets.length; i++) {

    // Checking if there is a request for the css file
    // We iterate the style sheets with href attribute that are created from LINK elements
    // STYLE elements don't have href attribute, so we ignore them
    // We also check if the href contains the css file name
    if (document.styleSheets[i].href && document.styleSheets[i].href.match("/template.css")) {

        console.log("There is a request for the css file.");

        // Checking if the request is unsuccessful
        // There is a request for the css file, but is it loaded?
        // If it is, the length of styleSheets.cssRules should be greater than 0
        // styleSheets.cssRules contains all of the rules in the css file
        // E.g. b { color: red; } that's a rule
        if (document.styleSheets[i].cssRules.length == 0) {

            // There is no rule in styleSheets.cssRules, this suggests two things
            // Either the browser couldn't load the css file, that the request failed
            // or the css file is empty. Browser might have loaded the css file,
            // but if it's empty, .cssRules will be empty. I couldn't find a way to
            // detect if the request for the css file failed or if the css file is empty

            console.log("Request for the css file failed.");

            // There is a request for the css file, but it failed. Fallback
            // We don't need to check other sheets, so we break;
            break;
        } else {
            // If styleSheets.cssRules.length is not 0 (>0), this means 
            // rules from css file is loaded and the request is successful
            console.log("Request for the css file is successful.");
            break;
        }
    }
    // If there isn't a request for the css file, we fallback
    // But only when the iteration is done
    // Because we don't want to apply the fallback at each iteration
    else if (i == document.styleSheets.length - 1) {
        // Fallback
        console.log("There is no request for the css file.");
    }
}

TL;DR

for (var i = 0; i < document.styleSheets.length; i++) {
    if (document.styleSheets[i].href && document.styleSheets[i].href.match("/template.css")) {
        if (document.styleSheets[i].cssRules.length == 0) {
            // Fallback. There is a request for the css file, but it failed.
            break;
        }
    } else if (i == document.styleSheets.length - 1) {
        // Fallback. There is no request for the css file.
    }
}
akinuri
  • 10,690
  • 10
  • 65
  • 102
  • 1
    In Firefox, if the CSS hasn't finished being parsed yet, trying to call `cssRules` will fail with the error _InvalidAccessError: A parameter or an operation is not supported by the underlying object_. – Scott Martin Oct 18 '18 at 16:10
  • accessing cssRules in Firefox will throw a [security error](https://stackoverflow.com/questions/21642277/security-error-the-operation-is-insecure-in-firefox-document-stylesheets) – Aurovrata Nov 27 '18 at 10:36
  • There is a workaround for Firefox here https://www.phpied.com/when-is-a-stylesheet-really-loaded/. – Chrillewoodz Apr 17 '19 at 15:36
  • This doesn't work. If the stylesheet is linked within the document, you'll get a match whether it has loaded or not. If it has not loaded, you can't check the cssRules value against `null`, or check its length. You'll get `Uncaught DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules` – Vince Dec 03 '19 at 11:25
6

Something like this will do (using jQuery):

function checkStyleSheet(url){
   var found = false;
   for(var i = 0; i < document.styleSheets.length; i++){
      if(document.styleSheets[i].href==url){
          found=true;
          break;
      }
   }
   if(!found){
       $('head').append(
           $('<link rel="stylesheet" type="text/css" href="' + url + '" />')
       );
   }
}
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • This won't work. If a stylesheet is linked within a document, it'll be represented in `document.styleSheets` whether it has loaded or not. Test it by linking to a stylesheet with an invalid URL. It'll still be there. – Vince Dec 03 '19 at 11:31
5

Peer to the comment made by JFK about the accepted answer:

I understood the question as "how to check whether a css file is loaded or not" rather than "how to check if the element exists".

The element may exist (and the path may be correct too) but it doesn't mean that the css file was successful loaded.

If you access a link Element via getElementById you'll not be able to check/read the style defined inside the CSS file.

In order to check if a style has been successfully loaded we have to use getComputedStyle (or currentStyle for IE).

HTML

//somewhere in your html document

<div id="css_anchor"></div>

CSS

//somewhere in your main stylesheet

#css_anchor{display:none;}

JAVASCRIPT

//js function to check the computed value of a style element

function get_computed_style(id, name){

        var element = document.getElementById(id);

        return element.currentStyle ? element.currentStyle[name] : window.getComputedStyle ? window.getComputedStyle(element, null).getPropertyValue(name) : null;

}

 //on document ready check if #css_anchor has been loaded

    $(document).ready( function() {

            if(get_computed_style('css_anchor', 'display')!='none'){

            //if #css_anchor style doesn't exist append an alternate stylesheet

                var alternateCssUrl = 'http://example.com/my_alternate_stylesheet.css';

                var stylesheet = document.createElement('link');

                stylesheet.href = alternateCssUrl;
                stylesheet.rel = 'stylesheet';
                stylesheet.type = 'text/css';
                document.getElementsByTagName('head')[0].appendChild(stylesheet);

            }
    });

Part of the answer comes from: myDiv.style.display returns blank when set in master stylesheet

Demo here: http://jsfiddle.net/R9F7R/

Community
  • 1
  • 1
RafaSashi
  • 16,483
  • 8
  • 84
  • 94
4

Apart from all the nice answers above, you can simply put a dummy element inside your markup and in your css file, give it any style other than default. Then in the code check if the attribute is applied to the dummy element, and if not, load the css. Just a thought though, not a neat way to do that thing you want done.

Samnan
  • 653
  • 1
  • 5
  • 13
3

My 2 cents. This checks whether there are any rules set on the css or not, meaning that it was or not successfully loaded

if(jQuery("link[href='/style.css']").prop('sheet').cssRules.length == 0){
    //Load the css you want
}
jpbourbon
  • 151
  • 1
  • 5
  • this will not work in FireFox, as accessing the cssRules will throw a [security error](https://stackoverflow.com/questions/21642277/security-error-the-operation-is-insecure-in-firefox-document-stylesheets). – Aurovrata Nov 27 '18 at 10:35
  • It may have changed in the meantime, this has been written some years ago. – jpbourbon Nov 28 '18 at 20:38
  • yes, that's most likely the case, and I tried several of the solutions on this page but most failed due to this. Have solved this problem in recent months? – Aurovrata Dec 03 '18 at 11:23
  • 1
    Nah, just went backend full time and stopped killing my neurons with messy frontend :) – jpbourbon Dec 06 '18 at 19:01
2

The document object contains a stylesheet collection with all the loaded stylesheets.

For a reference see http://www.javascriptkit.com/domref/stylesheet.shtml

You can loop this collection to verify that the stylesheet you want to verify is in it and thus loaded by the browser.

document.styleSheets[0] //access the first external style sheet on the page

There are some browser incompatibilities you should look out for though.

Peter
  • 14,221
  • 15
  • 70
  • 110
1

For a nice consistent and repeatable experience, I've written these two jQuery plugins that mimic the $.getScript(url, callback) jQuery method (however they will NOT force reloading from the server like $.getScript(). There are two methods: one that will load a CSS file anytime it's called, and one that will only load it once. I find the former handy during development when I'm making changes, and the latter great for a speedy deployment.

/**
 * An AJAX method to asynchronously load a CACHED CSS resource
 * Note: This removes the jQuery default behaviour of forcing a refresh by means
 * of appending a datestamp to the request URL. Actual caching WILL be subject to
 * server/browser policies
 */
$.getCachedCss = function getCachedCss(url, callback)
{
    $('<link>',{rel:'stylesheet', type:'text/css', 'href':url, media:'screen'}).appendTo('head');

    if (typeof callback == 'function')
        callback();
}

/**
 * An AJAX method to asynchronously load a CACHED CSS resource Only ONCE.
 * Note: This removes the jQuery default behaviour of forcing a refresh by means
 * of appending a datestamp to the request URL. Actual caching WILL be subject to
 * server/browser policies
 */
$.getCachedCssOnce = function getCachedCssOnce(url, callback)
{
    if (!$("link[href='" + url + "']").length) {
        $.getCachedCss(url, callback);

        if (typeof callback == 'function')
            callback();
    }
}

Usage example:

$(function() {
    $.getCachedCssOnce("pathToMyCss/main.css");
)}

Usage example with callback:

$(function() {
    $.getCachedCssOnce("pathToMyCss/main.css", function() {
        // Do something once the CSS is loaded
});
sean.boyer
  • 1,187
  • 16
  • 24
1

One way: use document.getElementsByTagName("link") iterate over each and check if its href is equal to the CSS file you check.

Another way: if you know some CSS rule being set only in that file, check if this rule really apply e.g. check if background of something is really red.

Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
1
var links = document.getElementsByTagName('link');
var file  = 'my/file.css';
var found = false;

for ( var i in links )
{
    if ( links[i].type == 'text/css' && file == links[i].href ) {
        found = true; break;
    }
}

if ( !( found ) ) {
    var styles = document.getElementsByTagName('style');
    var regexp = new RegExp('/\@import url\("?' + file + '"?\);/');

    for ( var i in styles )
    {
        if ( styles[i].src == file ) {
            found = true; break;
        } else if ( styles[i].innerHTML.match(regexp) ) {
            found = true; break;
        }
    }
}

if ( !( found ) ) {
    var elm = document.createElement('link');
        elm.href = file;
    document.documentElement.appendChild(elm);
}
joksnet
  • 2,305
  • 15
  • 18
1

You can either check if the filename is within your markup, like:

var lnks    = document.getElementsByTagName('link'),
    loadcss = true;

for(var link in lnks) {
    href = link.getAttribute('href');

    if( href.indexOf('foooobar.css') > -1) ){
            loadcss = false;
            return false;
    }
});

if( loadcss ) {
        var lnk     = document.createElement('link'),
            head    = document.getElementsByTagName('head')[0] || document.documentElement;        

        lnk.rel     = 'stylesheet';
        lnk.type    = 'text/css';
        lnk.href    = '//' + location.host + 'foooobar.css';            

        head.insertBefore(lnk, head.firstChild);
}

or you could check for a specific className which should be available, if the stylesheet was loaded. This probably comes a little closer to a feature detection.

jAndy
  • 231,737
  • 57
  • 305
  • 359
1

use .sheet in jQuery:

HTML:

<link rel="stylesheet" href="custom.css">

jQuery:

if($("link[href='custom.css']")[0].sheet.cssRules.length==0){
//custom.css was not loaded, do your backup loading here
}
xam
  • 452
  • 2
  • 7
  • 15
1

simple way using javascript..,

loadCssIfNotLoaded('https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css');
loadCssIfNotLoaded('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css');

function loadCssIfNotLoaded(url) {
    var element=document.querySelectorAll('link[href="' + url + '"]');
    if (element.length == 0)
    {
        var link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = url;
        document.getElementsByTagName("head")[0].appendChild(link);
    }
}
Adith S
  • 76
  • 4
0

In one line, with jQuery. If the #witness div is visible, we have to load the css file.

In the HTML, we have a:

<div id="witness"></div>

In the CSS file to load, we have:

  #witness{display:none;}

So, if the css file is loaded, the #witness div is not visible. We can check with jQuery and make decision.

!$('#witness').is(':visible') || loadCss() ;

As a snippet:

function loadCss(){
  //...
  console.log('Css file required');
};


!$('#witness').is(':visible') || loadCss();
#witness{display:none;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

<div id="witness"></div>