Fuxi
Fuxi

Reputation: 7589

jQuery: loading css on demand + callback if done

I want to load CSS files on demand (by eg. running an XML HTTP request which returns the CSS files to be loaded) for example style1.css, style2.css ..

So is there a way in jQuery (or a plugin) to this?

the idea is: loading html via xmlhttp, loading +adding required css-files, then - after anything is finished, display that html.

any idea?

Thanx!

Upvotes: 31

Views: 19326

Answers (8)

Popnoodles
Popnoodles

Reputation: 28409

How to load multiple CSS files with callback as requested
Note: ithout xdomain permissions, $.get will only load local files

WORKING DEMO
Note that the text "all css loaded" appears after loading but before the CSS is applied. Perhaps another workaround is required to overcome that.

$.extend({
    getManyCss: function(urls, callback, nocache){
        if (typeof nocache=='undefined') nocache=false; // default don't refresh
        $.when.apply($,
            $.map(urls, function(url){
                if (nocache) url += '?_ts=' + new Date().getTime(); // refresh? 
                return $.get(url, function(){                    
                    $('<link>', {rel:'stylesheet', type:'text/css', 'href':url}).appendTo('head');                    
                });
            })
        ).then(function(){
            if (typeof callback=='function') callback();
        });
    },
});

Usage

var cssfiles=['https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css', 'https://stackpath.bootstrapcdn.com/bootswatch/4.3.1/cerulean/bootstrap.min.css'];

$.getManyCss(cssfiles, function(){
    // do something, e.g.
    console.log('all css loaded');
});

to force refresh the css files add true

$.getManyCss(cssfiles, function(){
    // do something, e.g.
    console.log('all css loaded');
}, true);

Upvotes: 24

Erutan409
Erutan409

Reputation: 752

THIS DOES NOT REQUIRE THE CSS FILE(S) TO BE LOADED MORE THAN ONCE

Working off from the other established answers, I managed to come up with this, which seems to work similar to jQuery.getScript():

(function($){

    $.extend({
        getCss: function(url, success) {

            if ($("head").children("style[data-url='"+url+"']").length) {
                console.warn("CSS file already loaded: "+url);
            }

            var deferred = $.Deferred(function(defer){
                $.ajax({
                    url: url
                    ,context: {defer:defer,url:url}
                    ,dataType: 'text'
                    ,cache: (function(){
                        var cache = $.ajaxSetup().cache;
                        if (cache === undefined) {cache = false;}
                        return cache;
                    })()
                })
                .then(
                    function(css, textStatus, jqXHR){
                        css = "\n\n/* CSS dynamically loaded by jQuery */\n\n"+css;
                        $('<style type="text/css" data-url="'+this.url+'">'+css+'</style>').appendTo("head");
                        this.defer.resolve(css, textStatus, jqXHR);
                    }
                    ,function(jqXHR, textStatus, errorThrown){
                        this.defer.reject(jqXHR, textStatus, errorThrown);
                    }
                );
            });
            if ($.isFunction(success)) {deferred.done(success);}
            return deferred;
        }
    });

})(jQuery);

It doesn't load the requested file more than once, which DOES require that the CSS content is statically stored in the head. But, it makes sense considering that loading/evaluating JavaScript is different than how styling is accessible to the browser for rendering.

$.getCss('path/to/css/file.css')
.done(function(){
    console.log("Worked");
 })
.fail(function(){
    console.log("Failed");
});

So far I've tested it with Chrome, IE, and Firefox. All seem to work fine.

Upvotes: 1

Robert
Robert

Reputation: 21388

If you want it to by dynamic (read: on demand) you can modify Whit's response to loadCssFile() and call that with the file you want to load.

function loadCssFile(pathToFile) {
    var css = jQuery("<link>");
    css.attr({
      rel:  "stylesheet",
      type: "text/css",
      href: pathToFile
    });
    $("head").append(css);
}

Upvotes: 0

Alberto
Alberto

Reputation: 113

The answer given by @Popnoodles is not correct because the callback is not executed after all items have been loaded, but rather when the $.each loop is finished. The reason is, that $.each operation does not return a Deferred object (which is expected by $.when).

Here is a corrected example:

$.extend({
    getCss: function(urls, callback, nocache){
        if (typeof nocache=='undefined') nocache=false; // default don't refresh
        $.when.apply($,
            $.map(urls, function(url){
                if (nocache) url += '?_ts=' + new Date().getTime(); // refresh? 
                return $.get(url, function(){                    
                    $('<link>', {rel:'stylesheet', type:'text/css', 'href':url}).appendTo('head');                    
                });
            })
        ).then(function(){
            if (typeof callback=='function') callback();
        });
    }
});

Upvotes: 9

Berk Can
Berk Can

Reputation: 21

If you want to load multiple CSS files, but want them to be loaded one by one, you can use solution below:

var getCss = function (url, callback) {
        $.get(url, function () {
            $('<link>', {rel: 'stylesheet', type: 'text/css', 'href': url}).appendTo('head');
            if (typeof callback == 'function') callback();
        });
    };

Define function above, then define array containing you wanted css files

var cssfiles = ['css/pic1.css', 'css/pic2.css',
    'css/pic3.css', 'css/pic4.css', 'css/pic5.css',
    'css/pic6.css', 'css/pic7.css', 'css/pic8.css',
    'css/pic9.css'];

Then define callback function that will call each css files in the array

var callback = function (index) {
    getCss(cssfiles[index], function () {
        if (index + 1 < cssfiles.length) {
            callback(index + 1);
        }
    });
};

then start function with first css file by giving its index

callback(0);

Upvotes: 0

kikus
kikus

Reputation: 51

The solution can be improved a bit I think... First of all, when you use this lines of code:

...
$.get(url, function(){                    
    $('<link>', {rel:'stylesheet', type:'text/css', 'href':url}).appendTo('head');
});
...

You're really making 2 calls to retrieve the css: first in the $.get itself and a second time when you append the <link> into the head. Removing the $.get will also retrieve the css, but just once:

...
$('<link>', {rel:'stylesheet', type:'text/css', 'href':url}).appendTo('head');
...

But if you need to do more things (eg. loading a script file) while retrieving these css' before calling the callback function I think a better approach for this would be using promises instead of the when...then solution. You could do something like this:

var requests = []; //array which will contain every request we want to do before calling the callback function

$.map(urls, function(url) { //urls is the array of css files we want to load
    var defer = $.Deferred();
    defer.promise();

    //we add the deferred object to the requests array
    requests.push(defer);

    var cssEl = $('<link>', { rel: 'stylesheet', type: 'text/css', 'href': url });
    cssEl.appendTo('head').on("load", function() {
        defer.resolve();
    });
});

var anotherRequest = $.ajax({...}); //eg. a script file
requests.push(anotherRequest);

$.when.apply($, requests).done(function() {
    // callback when all requests are done
});

This way, if some of the css takes some time to load, the callback function won't be executed until all of them are retrieved.

Upvotes: 2

goliatone
goliatone

Reputation: 2174

You are trying to achieve lazy loading of your resources. There are different plug ins to handle this kind of behavior.

I've can name this two:

Two snippets from the plugins page to show it's use:

Jquery Plugins

$.plugins({ path: '/scripts/', plugins: [        
    { id:'box', js:'box.js', css:'box/styles.css', sel:'a[rel*=box]', ext:'box', fn:'box' },        
]});

jQuery(document).ready(function($){
  $('a').box();
});

Lazy with dependencies:

$.lazy('ui.draggable.js','draggable',{
'css':['modal.css','drag.css'],
'js':['ui.core.js']
});

// And then you use you plugins as you always do
$("#draggable").draggable();

Upvotes: 1

Whit
Whit

Reputation: 1159

Here is how I would load it:

$(document).ready( function() {
    var css = jQuery("<link>");
    css.attr({
      rel:  "stylesheet",
      type: "text/css",
      href: "path/to/file/style.css"
    });
    $("head").append(css);
});

Upvotes: 8

Related Questions