3

I can get the content of a single css file this way

getStyleCSS = $.get("MyStyle.css");
$.when(getStyleCSS).done(function(response) {
    var strCSS = response; // response here is the text content of the css file
}

But when I try to get several css file contents like this I get Deferred objects instead of the css contents.

// Get a handle on an aspx created element which contains css script elements
var elementDiv = document.getElementById('somediv');

// Get the stylesheets from this element
var links= elementDiv.getElementsByTagName('link');
arrLinks = [];
arrDeferreds = [];

// jQuery get each of the css links
for (var i = 0; i < arrLinks.length; i++) {
    var link = arrLinks[i];
    arrDeferreds.push($.get(link));
}

// Fetch the css documents
$.when(arrDeferreds).done(function (response) {
    var result = response; // response here is an array of Deferred objects
}

Is there a way to get the text content of multiple css files using javascript/jQuery?

Context: I need to send a subset of a page's HTML to an external (cross site) service which will generate a PDF of the specified HTML. In order for the pdf to be generated properly I need to embed the text of all css content into the html. The page contents originate with aspx/ascx controls and some of those controls contain script elements pointing to various css files. I'm trying to fetch the css text content of for each of these elements.

nickvans
  • 898
  • 13
  • 24
  • 2
    I think you might find an answer here? https://stackoverflow.com/questions/1679507/getting-all-css-used-in-html-file – AussieJoe Jul 11 '18 at 16:41
  • 1
    I think you're right, and that removes the need to do a second get of the data from the server so it's a double win. Thanks! – nickvans Jul 11 '18 at 16:52
  • if you find a solution, post it and mark it as the answer! cheers – AussieJoe Jul 11 '18 at 17:04
  • It won't let me mark it as a solution for 2 days (not really sure why) but I'll do that when I can. Thanks again for the help. – nickvans Jul 11 '18 at 17:08

2 Answers2

3

It's not really a solution (I still haven't figured out how to get the results from the Deferred objects) but this seems to be a legit work around. Thanks @AussieJoe for the pointer to another stackoverflow answer.

I can build up the css string for all css on the page like this

var strCSS = '';
for (var i = 0; i < document.styleSheets.length; i++) {
    var stylesheet = document.styleSheets[i];
    for (var j = 0; j < stylesheet.cssRules.length; j++) {
        var rule = stylesheet.cssRules[j];
        strCSS += rule.cssText;
    }
}
nickvans
  • 898
  • 13
  • 24
2

A problem with your code is that $.when() does not accept an array of deferreds/promises as an argument. From the jQuery doc:

If a single argument is passed to jQuery.when() and it is not a Deferred or a Promise, it will be treated as a resolved Deferred and any doneCallbacks attached will be executed immediately.

So, passing an array of deferreds, just gives you the array right back.

Instead, it needs them passed as separate arguments and it provides the results as separate arguments to .done() as in:

$.when(d1, d2, d3).then(function(r1, r2, r3) {
    console.log(r1, r2, r3);
});

If you have the deferreds in an array as you do in your code, you can use .apply() to pass them. And, then the results are again not given to you in an array, but in a series of separate arguments to .done() (again, really inconvenient).

So, you can change this code:

// jQuery get each of the css links
for (var i = 0; i < arrLinks.length; i++) {
    var link = arrLinks[i];
    arrDeferreds.push($.get(link));
}

// Fetch the css documents
$.when(arrDeferreds).done(function (response) {
    var result = response; // response here is an array of Deferred objects
}

to this:

// jQuery get each of the css links
for (var i = 0; i < arrLinks.length; i++) {
    var link = arrLinks[i];
    arrDeferreds.push($.get(link));
}

// Fetch the css documents
$.when.apply($, arrDeferreds).done(function() {
    // copy arguments object into an actual array
    let results = Array.prototype.slice.call(arguments);
    console.log(results);
}

Or, in ES6, you can just use Promise.all() which uses arrays:

// Fetch the css documents
Promise.all(arrDeferreds).then(function(results) {
    console.log(results);
});

To make a long story short, in any sort of modern Javascript environment, use Promise.all() instead of $.when().


FYI, if you can't use Promise.all() or a polyfill for it, then I developed this $.all() implementation awhile ago that lets you use arrays:

// jQuery replacement for $.when() that works like Promise.all()
// Takes an array of promises and always returns an array of results, even if only one result
$.all = function(promises) {
    if (!Array.isArray(promises)) {
        throw new Error("$.all() must be passed an array of promises");
    }
    return $.when.apply($, promises).done(function () {
        // if single argument was expanded into multiple arguments, then put it back into an array
        // for consistency
        var args = Array.prototype.slice.call(arguments, 0);
        if (promises.length === 1 && arguments.length > 1) {
            // put arguments into an array for consistency
            return [args];
        } else {
            return args;
        }
    });
};
jfriend00
  • 683,504
  • 96
  • 985
  • 979