【问题标题】:Accessing cross-domain style sheet with .cssRules使用 .cssRules 访问跨域样式表
【发布时间】:2011-03-13 19:07:21
【问题描述】:

当我尝试访问托管在外部域上的某些 CSS 文件时,我在 Firebug 中收到此错误:

Security error" code: "1000
rules = styleSheets[i].cssRules;

我使用的代码是:

$(document).ready(function () {
    $("p").live('mousedown', function getCSSRules(element) {
        element = $(this);
        var styleSheets = document.styleSheets;
        var matchedRules = [],
            rules, rule;
        for (var i = 0; i < styleSheets.length; i++) {
            rules = styleSheets[i].cssRules;
            for (var j = 0; j < rules.length; j++) {
                rule = rules[j];
                if (element.is(rule.selectorText)) {
                    matchedRules.push(rule.selectorText);
                }
            }
        }
        alert(matchedRules);
    });
});

除了在同一个域中移动所有 CSS 文件之外,有没有办法解决这个问题?

【问题讨论】:

标签: jquery cross-domain


【解决方案1】:

唯一真正解决这个问题的方法是首先 CORS 加载您的 CSS。通过使用 CORS XMLHttpRequest 从外部域加载 CSS,然后通过以下方式将 responseText(在这种情况下实际上是 responseCSS)注入页面:

function loadCSSCors(stylesheet_uri) {
  var _xhr = global.XMLHttpRequest;
  var has_cred = false;
  try {has_cred = _xhr && ('withCredentials' in (new _xhr()));} catch(e) {}
  if (!has_cred) {
    console.error('CORS not supported');
    return;
  }
  var xhr = new _xhr();
  xhr.open('GET', stylesheet_uri);
  xhr.onload = function() {
    xhr.onload = xhr.onerror = null;
    if (xhr.status < 200 || xhr.status >= 300) {
      console.error('style failed to load: ' + stylesheet_uri);
    } else {
      var style_tag = document.createElement('style');
      style_tag.appendChild(document.createTextNode(xhr.responseText));
      document.head.appendChild(style_tag);
    }
  };
  xhr.onerror = function() {
      xhr.onload = xhr.onerror = null;
      console.error('XHR CORS CSS fail:' + styleURI);
  };
  xhr.send();
}

这样,浏览器会将 CSS 文件解释为来自与主页响应相同的源域,现在您可以访问样式表的 cssRules 属性。

【讨论】:

  • 不错的答案。但是请注意,在xhr.send() 之前应该有一个} 才能使该功能正常工作。
  • 全局未定义。我需要导入什么才能完成这项工作?
  • @UlyssesAlves 尝试用任一窗口替换 global.XMLHttpRequest。 XMLHttpRequest 或直接引用 XMLHttpRequest。如果您在浏览器上下文中,应该适合您。
  • 大家好,我有一个非常相似的问题。几个月前我创建了一个脚本,用于与 styleSheets[i].cssRules 一起运行。但是,似乎由于某种原因,该方法现在违反了 CORS 规则,我尝试采用上述解决方案。然而,我收到以下错误:Access to XMLHttpRequest at 'file:///C:/website/css/structure.css' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https. 知道吗?
【解决方案2】:

我写了一个小函数来解决跨浏览器的加载问题,包括FF。 GitHub 上的 cmets 有助于解释用法。完整代码https://github.com/srolfe26/getXDomainCSS

免责声明:以下代码依赖于 jQuery。

有时,如果您从无法控制 CORS 设置的地方提取 CSS,直到获得带有 &lt;link&gt; 标记的 CSS,那么要解决的主要问题就是知道何时调用CSS 已加载并可以使用。在旧版 IE 中,您可以在加载 CSS 时运行 on_load 侦听器。

较新的浏览器似乎需要老式的轮询来确定文件何时加载,并且在确定何时满足加载时存在一些跨浏览器问题。请参阅下面的代码以了解其中的一些怪癖。

/**
 * Retrieves CSS files from a cross-domain source via javascript. Provides a jQuery implemented
 * promise object that can be used for callbacks for when the CSS is actually completely loaded.
 * The 'onload' function works for IE, while the 'style/cssRules' version works everywhere else
 * and accounts for differences per-browser.
 *
 * @param   {String}    url     The url/uri for the CSS file to request
 * 
 * @returns {Object}    A jQuery Deferred object that can be used for 
 */
function getXDomainCSS(url) {
    var link,
        style,
        interval,
        timeout = 60000,                        // 1 minute seems like a good timeout
        counter = 0,                            // Used to compare try time against timeout
        step = 30,                              // Amount of wait time on each load check
        docStyles = document.styleSheets        // local reference
        ssCount = docStyles.length,             // Initial stylesheet count
        promise = $.Deferred();

    // IE 8 & 9 it is best to use 'onload'. style[0].sheet.cssRules has problems.
    if (navigator.appVersion.indexOf("MSIE") != -1) {
        link = document.createElement('link');
        link.type = "text/css";
        link.rel = "stylesheet";
        link.href = url;

        link.onload = function () {
            promise.resolve();
        }

        document.getElementsByTagName('head')[0].appendChild(link);
    }

    // Support for FF, Chrome, Safari, and Opera
    else {
        style = $('<style>')
            .text('@import "' + url + '"')
            .attr({
                 // Adding this attribute allows the file to still be identified as an external
                 // resource in developer tools.
                 'data-uri': url
            })
            .appendTo('body');

        // This setInterval will detect when style rules for our stylesheet have loaded.
        interval = setInterval(function() {
            try {
                // This will fail in Firefox (and kick us to the catch statement) if there are no 
                // style rules.
                style[0].sheet.cssRules;

                // The above statement will succeed in Chrome even if the file isn't loaded yet
                // but Chrome won't increment the styleSheet length until the file is loaded.
                if(ssCount === docStyles.length) {
                    throw(url + ' not loaded yet');
                }
                else {
                    var loaded = false,
                        href,
                        n;

                    // If there are multiple files being loaded at once, we need to make sure that 
                    // the new file is this file
                    for (n = docStyles.length - 1; n >= 0; n--) {
                        href = docStyles[n].cssRules[0].href;

                        if (typeof href != 'undefined' && href === url) {
                            // If there is an HTTP error there is no way to consistently
                            // know it and handle it. The file is considered 'loaded', but
                            // the console should will the HTTP error.
                            loaded = true;
                            break;
                        }
                    }

                    if (loaded === false) {
                        throw(url + ' not loaded yet');
                    }
                }

                // If an error wasn't thrown by this point in execution, the stylesheet is loaded, proceed.
                promise.resolve();
                clearInterval(interval);
            } catch (e) {
                counter += step;

                if (counter > timeout) {
                    // Time out so that the interval doesn't run indefinitely.
                    clearInterval(interval);
                    promise.reject();
                }

            }
        }, step);   
    }

    return promise;
}

【讨论】:

  • 仅链接的答案通常是 Stack Overflow 上的frowned upon。随着时间的推移,链接可能会萎缩并变得不可用,这意味着您的答案将来对用户毫无用处。如果您可以在实际帖子中提供答案的一般详细信息,并引用您的链接作为参考,那将是最好的。
  • @vaultah 感谢您提供最佳实践建议。我只花了一年的时间来更新它 :-) 干杯。
【解决方案3】:

从 2013 年开始,您可以在 &lt;link&gt;-Element 上设置“crossorigin”属性,以向浏览器表明此 CSS 是受信任的(MozillaW3)。为此,托管 CSS 的服务器必须设置 Access-Control-Allow-Origin: * 标头。

之后,您可以通过 Javascript 访问其规则。

【讨论】:

【解决方案4】:

如果这触发了您,因为您的某些 CSS 可能来自其他地方,但不是您感兴趣的部分,请使用 try...catch 块,如下所示:

function cssAttributeGet(selectorText,attribute) {
  var styleSheet, rules, i, ii;
  selectorText=selectorText.toLowerCase();
  if (!document.styleSheets) {
    return false;
  }
  for (i=0; i<document.styleSheets.length; i++) {
    try{
      styleSheet=document.styleSheets[i];
      rules = (styleSheet.cssRules ? styleSheet.cssRules : styleSheet.rules);
      for (ii=0; ii<rules.length; ii++) {
        if (
          rules[ii] && rules[ii].selectorText &&
          rules[ii].selectorText.toLowerCase()===selectorText &&
          rules[ii].style[attribute]
        ){
          return (rules[ii].style[attribute]);
        }
      }
    }
    catch(e){
      // Do nothing!
    };
  }
  return false;
}

【讨论】:

  • 这个功能对我帮助很大。谢了。
【解决方案5】:

我在 Firefox 和 chrome 下遇到了类似的问题。我通过向我的域添加一个包含外部域 css 的 css 文件以严厉的方式解决了这个问题,如下所示:

<style type="text/css">
@import url("https://externaldomain.com/includes/styles/cookie-btn.css");
</style>

它很快但很脏。建议将所有 css 文件保留在您的域中。

【讨论】:

  • 它对我不起作用。在这个(现在)具有外部样式表的页面上进行了测试。 var style = $('&lt;style&gt;@import url('+document.styleSheets[0].href+');&lt;/style&gt;'); var importRule = style.sheet.cssRules[0]; var cssRules = importRule.styleSheet.cssRules; // null!
  • 当然可以。但是为什么还要将它与@import 配对呢?
  • 进口生产Access-Control-Allow-Origin
【解决方案6】:

如果您可以控制托管外部样式表的域,添加appropriate Access-Control-Allow-Origin header 可能会有所帮助。

Access-Control-Allow-Origin: http://stylesheet-user.example.com

【讨论】:

  • 好主意,但我发现这至少在 Chrome 上没有任何区别。
【解决方案7】:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-26
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    • 2014-09-28
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多