7

I have a function doing the following using javascript:

  1. Create link element and set href=cssFile.
  2. Insert the link element in head tag.
  3. Create a div element.
  4. Set the class name using setAttribute
  5. appendChild the div on body.
  6. Now getting CSS rule value using document.defaultView.getComputedStyle(divElement, null)[cssRule].

Now getComputedStyle is returning the default values, and if I wait on breakpoint using Firebug before getComputedStyle call, then it returns the CSS rule from the CSS injected.

Regards,
Munim

Abdul Munim
  • 18,869
  • 8
  • 52
  • 61

5 Answers5

9

You can create the dynamic css url and fetch the css as plain text using a normal ajax call.

Then use this to load the css:

function loadCss(cssText, callback){
    var style = document.createElement('style');
    style.type='text/css';
    if(callBack != undefined){
        style.onload = function(){
            callBack();
        };
    }
    style.innerHTML = cssText;
    head.appendChild(style);
}

And use it like this:

loadCss(ajaxResponseText, function(){
    console.log("yaay css loaded, now i can access css defs");
})
letronje
  • 9,002
  • 9
  • 45
  • 53
  • 1
    +1 for this solution. I prefer it to the one I proposed, assuming all browsers can interpret a dynamically added style tag (haven't tried it). – Tim M. Sep 25 '10 at 14:49
  • 1
    Thanks for the solution! But one thing that I forgot to mention in the question, and that is my CSS is on other domain... Meaning, a cross-browser issue with no ajax response – Abdul Munim Sep 25 '10 at 14:57
  • @Munim: if the css is on another domain, try adding the 'link' tag (to head) with the appropriate type, rel and href and onload function. Let me know if it works. – letronje Sep 25 '10 at 15:22
  • 1
    Should be `style.onload = callback;` (also, note the case). – Aleksei Zabrodskii Dec 22 '12 at 01:23
  • You switch between `callback` and `callBack`. Should be `callback` I think. – Lasse Bunk Dec 23 '13 at 17:40
8

This is actually what I did.

To ensure a specific CSS file is loaded, I added a style in the end of the CSS file. For example:

#ensure-cssload-8473649 {
  display: none
}

Now I have a JavaScript function which will fire the callback specified when the above style is loaded on the page:

var onCssLoad = function (options, callback) {
    var body = $("body");
    var div = document.createElement(constants.TAG_DIV);
    for (var key in options) {
        if (options.hasOwnProperty(key)) {
            if (key.toLowerCase() === "css") {
                continue;
            }
            div[key] = options[key];
        }
    }

    var css = options.css;
    if (css) {
        body.appendChild(div);
        var handle = -1;
        handle = window.setInterval(function () {
            var match = true;
            for (var key in css) {
                if (css.hasOwnProperty(key)) {
                    match = match && utils.getStyle(div, key) === css[key];
                }
            }

            if (match === true) {
                window.clearTimeout(handle);
                body.removeChild(div);
                callback();
            }
        }, 100);
    }
}

And this is how I used the function above:

onCssLoad({
    "id": "ensure-cssload-8473649",
    css: {
        display: "none"
    }
}, function () {
    // code when you want to execute 
    // after your CSS file is loaded
});

Here the 1st parameter takes the options where id is to check against the test style and css property to verify against what loaded from the CSS.

Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
Abdul Munim
  • 18,869
  • 8
  • 52
  • 61
  • This solution is still the best if we don't want to load the css ourselves. (for ex. if we use a loader) – Offirmo Mar 20 '13 at 15:02
4

I assume you are doing this because you need to dynamically create the URL of the stylesheet.

Couple options come to mind:

1) Create the URL server-side and avoid this problem altogether.

2) Use a setTimeout to check whether or not the style has been loaded and check every 20ms or so until getComputedStyle returns the value you want.

I don't like #2 at all...but it's an option. If you use #2 make sure to clear the timeout even if there is an exception.

Tim M.
  • 53,671
  • 14
  • 120
  • 163
2

Here is a solution that seems to work across all browsers.

function loadCss(fileUrl) {
  // check for css file type
  if (fileUrl.indexOf(".css")==fileUrl.length-4) {
    // Create link element
    var fileref=document.createElement("link");
    fileref.setAttribute("rel", "stylesheet");
    fileref.setAttribute("type", "text/css");
    fileref.setAttribute("href", fileUrl);
    if (typeof fileref!="undefined") {
      // remove the . if this is a relative link
      if(fileUrl.indexOf('.')==0) {
        fileUrl = fileUrl.substr(1);
  }
  // generate the full URL to use as the fileId
      var pathname = window.location.pathname;
  var pathUrl = pathname.substr(0,pathname.lastIndexOf("/"));
  var fileId = window.location.protocol + "//" + window.location.host + pathUrl + fileUrl;
      // append the newly created link tag to the head of the document
      document.getElementsByTagName("head")[0].appendChild(fileref);

  // begin checking for completion (100ms intervals up to 2000ms)
  this.checkCSSLoaded(fileId,100,0,2000);

    } else throw 'INVALID_CSS_ERROR';
  } else throw 'INVALID_CSS_ERROR';
}

function checkCSSLoaded(cssHref,milliPerCheck,milliPerCount,milliTimeout) {
  // Look through all sheets and try to find the cssHref
  var atSheet = -1;
  var sheetLength = document.styleSheets.length;
  while(++atSheet < sheetLength ) {
if(cssHref == document.styleSheets[atSheet].href) {
      // If found dispatch and event or call a procedure
      /* Do whatever is next in your code here */
  return;
}
  }
  // check for timeout
  if(milliCount > milliTimeout) { 
    alert('INVALID_CSS_ERROR::'+" ("+cssHref+"+ not found!");
    /* Do whatever happens if timeout is reached here */
return;
  }
  // else keep trying
  setTimeout(checkCSSLoaded ,milliPerCheck, cssHref, milliPerCheck, milliCount+millPerCheck, milliTimeout);
}

Essentially we

  1. Create a link tag.
  2. Set its attributes so it knows its a stylesheet link tag
  3. Create a file id in such a way that it will always be the full file URL
  4. Append the link tag to the head of the document head
  5. Perform consecutive tests to see if (stylesheet.href == fileID) comes into existence
    • If found do something else if timeout do something else keep checking
Drone Brain
  • 419
  • 3
  • 8
0

Using document.styleSheets to check if a css is loaded is wrong, since as soon as a css link is being added to the DOM, it will be available from document.styleSheets, even if it is not loaded yet.

Adding a marker to CSS is hacky too.

The correct solution is to listen to the onload event :

   var loadedCss = {};
   cssHref = "http://www.foo.com/bar.css";
   css = document.createElement("link");
   css.setAttribute("rel", "stylesheet");
   css.setAttribute("type", "text/css");
   css.setAttribute("href", cssHref);
   css.onload = function(){
      loadedCss[cssHref] = true;
   }
   document.getElementsByTagName("head")[0].appendChild(css);


   function isCssLoaded(url) {
       return loadCss[url];
   }
cl yu
  • 109
  • 1
  • 3