6

我需要通过ajax加载一堆CSS文件,并在样式表加载完成后调用动画,否则动画会失败。

我这样做并且过去工作得很好,直到我遇到这个跨域是这样的:

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

只要resource.url在同一个域上,它就可以正常工作。一旦我尝试从另一个域加载 css$.get将失败,如下所示:

XMLHttpRequest cannot load https://example.com/style.css. Origin https://example.com is not allowed by Access-Control-Allow-Origin.

所以我试图通过以下方式将CORS添加到标题中.htaccess

<IfModule mod_headers.c>
    #cross domain access is okay for resources (#107)
    <FilesMatch "\.(css|js)$"> 
        Header add Access-Control-Allow-Origin "*"
    </FilesMatch>
</IfModule>

这会将 CORS 标头添加到所有 CSS 和 JS 资源。

由于某种原因, CORS似乎对 chrome 或 firefox(最新版本)都没有影响。

我还注意到,在$.getScript处理 js 文件时没有强制执行相同的域策略,但它适用于$.get

$.get("https://example.com/script.js", {cache: false}, $.noop, "script"); 

//works regardless of CORS

但:

$.get("https://example.com/script.js", {cache: false}, $.noop); 

//does not work (cross domain policy violation)

因此,由于 CORS 没有得到广泛支持,甚至似乎无法解决现代浏览器的问题,所以我需要一些类似于$.getScript, 但用于 CSS 样式表的东西。

它需要是异步的并具有回调机制。

有任何想法吗?提前致谢...

4

2 回答 2

8

$.get使用 Ajax,它必须遵守同源策略。

$.getScript 通常使用 Ajax,它也有一个仅供跨域使用的后备选项。通过标签导入<script>的脚本不受同源规则的约束,因此 jQuery 只需将<script>标签添加到页面并将其src属性设置为请求的脚本 URL。

通过<link>标签加载的 CSS 资源也有同样的例外。在一个完美的世界中,您应该能够 1) 创建一个新<link>元素,2) 将其href属性设置为正确的 URL,以及 3) 侦听load该元素上的事件。我已经从 jQuery 论坛向这个解决方案添加了回调:

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

这里有一个小问题:它不能跨浏览器工作。 <link>标记不会load在所有浏览器中触发事件 - 请参阅jQuery 错误报告了解$.getCSS可能的功能。

所以,一般情况下的解决方法有点疯狂:添加 CSS URL 作为新<img>标签的源并监听它的onerror处理程序:

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

您还可以添加一些优化并侦听(对于支持它的浏览器)load事件和(对于那些不支持的浏览器)上的事件,并放置一些逻辑以确保回调只被调用一次,以防两个事件都发生。<link>error<img>

至于让$.getCSSreturn 成为一个延迟对象,这有点超出我的专业领域,但没有理论上的理由不能做到这一点。

于 2012-07-12T03:57:35.623 回答
0

使用延迟而不是创建 jQuery 插件将我自己的 apsillers 答案的实现放在一起。

fetchStyleSheet(url, media = 'screen') {
    let $dfd = $.Deferred(),
        finish = () => $dfd.resolve(),
        $link = $(document.createElement('link'))
            .attr({
                media,
                type: 'text/css',
                rel: 'stylesheet'
            })
            .on('load', 'error', finish)
            .appendTo('head'),
        $img = $(document.createElement('img'))
            .on('error', finish); // Support browsers that don't fire events on link elements

    $link[0].href = $img[0].src = url;
    return $dfd.promise();
}

fetchStyleSheet('/url/to/my/stylesheet.css').done(() => {
    // Stuff after the sheet has loaded
});

这个话题已经有好几年了,我不知道 img 上的备份事件是否仍然需要,但实现起来似乎相当简单。

对于我的特定用例,我实际上并不关心工作表是否无法加载,因此这两种情况都得到了解决。如果您想单独处理这些案例,您可以将参数传递给finish处理程序以适当地解决/拒绝。

于 2017-10-27T19:02:55.877 回答