I need to load a bunch of CSS files through ajax and call an animation when the stylesheet has finished loading, otherwise the animation will fail.
What I have done so and used to work pretty well until I came accross doing this cross-domain is this:
$.get(resource.url, {cache:true}, function(css) {
//Now that the stylesheet is in the browser cache, it will load instantly:
$("head").append($("<link>",{
rel: "stylesheet",
type: "text/css",
href: resource.url
}));
}).then(function(){
//Animation here that depends on the loaded css
});
This works fine as long as resource.url
is on the same domain. Once I try to load the css from another domain $.get
will fail like this:
XMLHttpRequest cannot load https://example.com/style.css. Origin https://example.com is not allowed by Access-Control-Allow-Origin.
So I have tried to add CORS into the header through .htaccess
:
<IfModule mod_headers.c>
#cross domain access is okay for resources (#107)
<FilesMatch "\.(css|js)$">
Header add Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
This adds the CORS header to all CSS and JS resources.
For some reason CORS doesn't seem to have an effect on either chrome or firefox (newest versions).
I also came to notice that same domain policy is not enforced when doing $.getScript
for js files, but it is for $.get
:
$.get("https://example.com/script.js", {cache: false}, $.noop, "script");
//works regardless of CORS
but:
$.get("https://example.com/script.js", {cache: false}, $.noop);
//does not work (cross domain policy violation)
So since CORS is not widely supported and doesn't even seem solve the problem for a modern browser, I need something that behaves like $.getScript
, but for CSS stylesheets.
It needs to be asynchronous and have a callback mechanism.
Any ideas? Thanks in advance...
Ajax method The browser performs a JavaScript call to the Ajax engine. In other words, create an XMLHttpRequest object. In the background, an HTTP request is made to the server and the appropriate data is retrieved.
$.get
uses Ajax, which must obey the Same-Origin Policy.
$.getScript
normally uses Ajax, but it also has a fallback option for cross-domain use only. Scripts imported via <script>
tag are not subject to Same-Origin rules, so jQuery just adds a <script>
tag to the page and sets its src
attribute to the requested script URL.
The same exemption is made for CSS resources loaded in through <link>
tags. In a perfect world, you should be able to 1) make a new <link>
element, 2) set its href
attribute to the right URL and 3) listen for a load
event on that element. I've added a callback to this solution from the jQuery forums:
// note: non-compatible example code, see below for better code
jQuery.getCSS = function( url, media, callback ) {
jQuery( document.createElement('link') ).attr({
href: url,
media: media || 'screen',
type: 'text/css',
rel: 'stylesheet'
}).appendTo('head')
.on("load", callback);
};
One small problem here: it doesn't work cross-browser. <link>
tags do not fire load
events in all browsers -- see the jQuery bug report for the $.getCSS
function that could have been.
So, the general-case workaround is a bit insane: add the CSS URL as the source of a new <img>
tag and listen for its onerror
handler:
// correct code!
jQuery.getCSS = function( url, media, callback ) {
jQuery( document.createElement('link') ).attr({
href: url,
media: media || 'screen',
type: 'text/css',
rel: 'stylesheet'
}).appendTo('head');
jQuery( document.createElement('img') ).attr('src', url)
.on("error", callback);
};
You could also add a bit of optimization and listen for load
event on the <link>
(for browsers that support it) and an error
event on the <img>
(for those that don't), and put some logic in place to ensure the callback is only ever called once, in case both events occur.
As for making $.getCSS
return a deferred object, that's a bit out of my area of expertise, but there's no theoretical reason why it couldn't be done.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With