3

I want to dynamically insert some HTML content and some CSS urls through JS.

I have 3+ CSS files. I want them to be downloaded before my content is inserted on the page.

Is there a way to find out whether the above-mentioned files have been downloaded?

This is how it should work:

  • Download css files;
  • Show HTML after all the css files have been downloaded;
  • Start loading JS files after inserting the HTML;
  • Trigger callback after all the JS files have been loaded;
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Basit
  • 16,316
  • 31
  • 93
  • 154

10 Answers10

3

Source: https://stackoverflow.com/a/3794242/1292652

Retreiving CSS text using AJAX

Instead of using messy workarounds to determine of the CSS has loaded, you can use AJAX function to create a dynamic CSS URL and fetch the it as plain text.

Inserting the CSS

After fetching the raw text, you can use this function to insert the CSS into a style tag and add a callback:

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);
}

Using it

And now you can use this as:

loadCss(ajaxResponseText, function(){
    console.log("CSS loaded, you can show the dialog now :)");
})

Allowing cross-domain AJAX

In your comment, you mentioned you had to load jQuery and jQueryUI which I'm guessing wil de on a different domain.

To get around the AJAX cross-domain restriction, check out this link or this one or this library

Community
  • 1
  • 1
Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
3

You could use YepNope.js, YepNope allows you to build asynchronous conditional tests to see whether resources have loaded. Once your tests have passed you can tell it to inject new CSS or JS files.

Example below has been taken from the YepNope.js site.

yepnope.injectCss( stylesheetSource [, callback ] [, elemAttributes ] [, timeout ]);

// Example
yepnope.injectCss("print.css", function () {
  console.log("css injected!");
}, {
  media: "print"
}, 5000);

You can even make YepNope load the initial CSS files first and then once they have completed loading YepNope can trigger a callback to do additional tasks, such as loading more JS or CSS files.

Aran
  • 3,298
  • 6
  • 32
  • 33
1
<html>
<head>
   <!-- 
       Put your styles here.  Make sure the *last* CSS file has the following:
       body { display: block !important }

       This will allow the stylesheets to load while the body is hidden.  
       When the last stylesheet is loaded, the body will be visible.
   -->
</head>

<body style="display: none">
    <!-- Lots of HTML here -->

    <!-- 
        Put your script tags here.
        In the first script, I recommend binding to the window.load event...  
        which will be fired once all your scripts are done loading.
    -->
</body>
</html>
Ryan Wheale
  • 26,022
  • 8
  • 76
  • 96
0

I think this is somewhat along the lines of what you wanted:

<html>
  <head>
    <!-- CSS Stuff goes here -->
    <!-- JS Stuff come right after -->
  </head>
  <body>
    <!-- HTML Stuff comes withing Body -->
  </body>
</html>

This will cause:-

  1. All the CSS files to download in parallel;
  2. Then the HTML stuff gets parsed and displayed;
  3. The browser gets all the JS;
  4. The JS is tun and it does whatever inserting it wants;

Your question is a bit confusing, can you clarify it? I didn't understand why you'd require to have such an order, can you give the big picture? JS is meant to be a fire-and-forget language, it will be awkard to see if everything's downloaded. Did this make sense to you and/or help you?

Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
  • If you downvote, please comment on why you thought you should do so. I'm trying to answer as per what I understood. – Yatharth Agarwal Sep 21 '12 at 14:28
  • i mean dynamically loading css + html through javascript after page is loaded. – Basit Sep 21 '12 at 15:06
  • @Basit I still don't understand. What's the problem? Just dump the JS code anywhere and it'll dynamically load whatever whenever it's loaded. Why does it have to be _after_ the page has finished downloading? – Yatharth Agarwal Sep 21 '12 at 15:09
  • its loading through ajax (bigPipe) for new pages. i need to know if all css are downloaded, so i can show html content on page and also i need to know if all javascript files are downloaded, so i can do a callback function after its completed. – Basit Sep 21 '12 at 15:11
  • @Basit Sorry for my ignorance, but can you explain why you have to insert the HTML only after the CSS is done? It's the browser's problem to retro-actively apply the CSS to the HTML, isn't it? – Yatharth Agarwal Sep 21 '12 at 15:14
  • Can you give an example of the callback function you want to call? – Yatharth Agarwal Sep 21 '12 at 15:15
  • for example there are 3 javascript files that need to be download first. jquery.js, jquery.ui.js, yahoo.js. after they are downloaded, then i can trigger a function to open up dialog box, which depend on jquery.js and jquery.ui.js files. do you see what i mean? – Basit Sep 21 '12 at 15:20
  • @Basit Can you approve my edit so I can remove my initial downvote? – Yatharth Agarwal Sep 21 '12 at 15:31
  • I like [this link](http://recurial.com/programming/understanding-callback-functions-in-javascript/) for explaining callbacks. – Yatharth Agarwal Sep 21 '12 at 15:34
  • i understand callbacks. i just need to know if required files have been download or not yet, which can be 1 or more... – Basit Sep 21 '12 at 19:39
  • Just open the page with you browser.. you can inspect whatever is loaded (html, css, js) (F12 in chrome for example) – Sato Sep 24 '12 at 12:50
0

I know it is not a pretty solution, but you can fetch the CSS files using AJAX and add their content to a <style>, instead of inserting a <link> to the DOM. Using AJAX, you can know for sure when the CSS is completely downloaded and add the whole thing into the page under your terms.

PhistucK
  • 2,466
  • 1
  • 26
  • 28
0

Source: https://stackoverflow.com/a/8122005/1292652

I don't recommend this for the same reason why you shouldn't use a while loop to implement sleep(), but if you must, you can try it.

In the linked answer, a reference style was added. For example: #ensure-cssload-8473649 { display: none }. But since you're downloading files not in your control, you'll have to select a few styles from the sheet for detection.

The cssLoad() function is:

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);
    }
}

You can use it as:

onCssLoad({
    "id": <insert element CSS applies to>,
     css: <insert sample CSS styles>
}, function () {
    console.log("CSS loaded, you can show the dialog now :)");
});
Community
  • 1
  • 1
Yatharth Agarwal
  • 4,385
  • 2
  • 24
  • 53
0

Why don't you add an element on page and check some style that it should have with some js. If it has that style then that stylesheet is probably load.

Like this:

if (document.getElementById("csspage-3-box").style.color === "#somethingUnique") {
    //it is loaded
}

You could append a few hidden divs with IDs unique to each style sheet. If they have any styles at all then you know that the sheet you want is loaded. This operation is cheap. It does add some non-semantic div elements though. That being said, I feel like everyone has at least one non-semantic element in their DOM.

Just a suggestion, but 3 css files isn't that many. Other easy solutions include the following:

A) You could just cat and minify them and deliver them all at once.
B) Throw them onto a CDN (like aws s3 + cloudfront), which is super cheap and they will get loaded in parallel.
C) Just load them in as normal. You could also put them on a sub domain.

Now that aside if I were you I'd divide my CSS down into a bunch of files. 3 is too few files. I'd separate out grid and every element type. Then re-assemble them on a per page basis (or page group). This allows you to include each individual piece as you need.

After all that it is fairly easily to append a link tag (as long as you can easily get a path to it). I recommend using the link instead of the style tag because if you do go the CDN route or subdomain route you will have issues with cross-domain issues if trying to grab the content of the css file with some xhr.

Parris
  • 17,833
  • 17
  • 90
  • 133
0

In building the apps that our team develops there can be many CSS & Script files and the head of your HTML can end up looking rather cumbersome. I built a little script that will load all of your files by passing them to the method as an Array. However, I have the loadCSS() in its own script file and I just invoke and pass in the Array in via a script tag in the HTML. Code is in monolithic format to make it easier to view.

window.onload = function() {//Use $(document).ready() if using jQuery

    var cssArray = ["Style01.css", "Style02.css", "Style03.css"];
    loadCSS(cssArray, function () {

        alert("Style sheets have been loaded.");    
    });
    //Note: You can do a similar process for the JS files.
    //Load HTML when CSS is finished - Shown below
    loadHTML(); 
}

function loadCSS(array, callback) {

var cssArray = array;

    for (var i = 0; i < cssArray.length; i++)
    {
        document.write("<link rel='stylesheet' type='text/css' href='styles/" + cssArray[i] + "' />");
        //console.log(cssArray[i] + " style sheet has been loaded.");

        //Detects when for loop is finished evaluating the Array
        if (i == cssArray.length - 1) {

            return callback();
        } 
    }

    //loadHTML() can also be invoked here to ensure CSS files have been loaded.
}

If you are attempting to build out some HTML dynamically then you could try something like this after the CSS files are loaded.

function loadHTML() {

//Create Main Table
var mainTable = document.createElement("table");
mainTable.id = "mainTable";
mainTable.cellPadding = 0;
mainTable.cellSpacing = 0;
mainTable.border = 0;       

//Create Body
var mainBody = document.createElement("tbody");   

//Create Table Row
var mainTR = document.createElement("tr");
mainTR.style.height = "50px";
mainTR.className = ""; //Insert class from one of the style sheets

//Create Main Cell
var mainTD = document.createElement("td");
mainTD.id = "mainTD";
//mainTD.style.width = "";
mainTD.style.textAlign = "left";
mainTD.style.padding = "5px 10px 5px 10px";
mainTD.className = ""; //Insert class from one of the style sheets
mainTD.innerHTML = "Test Text";

mainTR.appendChild(mainTD); 
mainBody.appendChild(mainTR);
mainTable.appendChild(mainBody);     

document.body.appendChild(mainTable);               
}

If you have any questions please feel free to ask. Although there are already some good examples posted, hopefully this can help.

  • im doing similar to your method.. i just need to know if css file has been downloaded yet or not. – Basit Sep 29 '12 at 13:08
  • Yes, the CSS will be loaded before any of the HTML is rendered out. The `loadCSS()` method is called and the for loop will run in its entirety before the `loadHTML()` method is called. You should be able to open up your dev tools and see the CSS files embedded in the ``. – Dzeimsas Zvirblis Sep 29 '12 at 14:58
  • Also, if you want to be certain that the `loadHTML()` method is called after all the CSS are loaded you can invoke `loadHTML()` after the `for` loop. I updated the example code above. – Dzeimsas Zvirblis Sep 29 '12 at 15:02
  • im already doing that. what i mean is checking if file has been downloaded yet, before i can call loadHTML();. for loop will start loading the css file, but its not loaded yet. how can i check when the file has been loaded. like we check it with javascript.onload – Basit Sep 29 '12 at 15:05
  • There isnt an event that I am aware of that will detect when CSS files are loaded similar to the `onload` event for elements. However, you can create a callback function in the `loadCSS` function and then fire the callback when the for loop is finished evaluating the `cssArray`. I updated the example code with the callback functionality. I hope this helps a little bit more. – Dzeimsas Zvirblis Sep 29 '12 at 17:15
0

what about :

$(document).ready(function() {
    $("body").hide();
});

then, in the downloaded css files :

body { diaplay:block; }
Frederik.L
  • 5,522
  • 2
  • 29
  • 41
0

Use loadCSS to load your CSS. and inside onload method put your code you want to execute after all css load.

var counter = 0;
function onload(){
   counter--;
    setTimeout(function(){ // insuring if loaded immediately 
        if(counter == 0){ 
       // your code  here //******** CODEREPLACE ************/
          alert('all css files loaded!');
         }
    });
}
function loadCSS(href){
    counter++;
    l  = document.createElement('link');
    l.href = href;
    l.onreadystatechange = function () { // For IE
          if (this.readyState == 'loaded'){
              onload.call( this );
           }
     };
    l.rel = 'stylesheet';
    l.type='text/css';
    l.onload = onload;
    document.getElementsByTagName('head')[0].appendChild(l);
}

loadCSS('http://www.google.co.in/search?q=load+css+files+dynamically&aq=f&oq=load+css+files+dynamically&sugexp=chrome,mod=1&sourceid=chrome&ie=UTF-8');
Anoop
  • 23,044
  • 10
  • 62
  • 76