Доступ к междоменной таблице стилей с помощью .cssRules - PullRequest
23 голосов
/ 09 июля 2010

Я получаю эту ошибку в Firebug, когда пытаюсь получить доступ к некоторым файлам CSS, размещенным на внешних доменах:

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 в одном домене?

Ответы [ 7 ]

12 голосов
/ 27 августа 2013

Единственное реальное решение этой проблемы - это загрузить CORS в первую очередь.Используя 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 ваших таблиц стилей.

10 голосов
/ 17 сентября 2013

Начиная с 2013 года, вы можете установить атрибут «crossorigin» в элементе <link>, чтобы сигнализировать браузеру, что этот CSS является доверенным ( Mozilla , W3 ). Чтобы это работало, Сервер, на котором размещен CSS, должен установить заголовок Access-Control-Allow-Origin: *.

После этого вы можете получить доступ к его правилам через Javascript.

2 голосов
/ 29 апреля 2016

Я написал небольшую функцию, которая решит кросс-браузерную проблему загрузки, включая FF. Комментарии к GitHub помогают объяснить использование. Полный код на https://github.com/srolfe26/getXDomainCSS.

Отказ от ответственности: Код ниже зависит от jQuery.

Иногда, если вы вытаскиваете CSS из места, где вы не можете управлять настройками CORS, которые можете, пока не получите CSS с тегом <link>, основной проблемой, которая должна быть решена, становится знание того, когда вы вызываете CSS был загружен и готов к использованию. В более старых версиях IE вы могли запускать слушатель on_load при загрузке CSS.

Более новые браузеры, кажется, требуют старомодного опроса, чтобы определить, когда файл загружается, и имеют некоторые межбраузерные проблемы при определении, когда нагрузка удовлетворяется. Посмотрите код ниже, чтобы поймать некоторые из этих причуд.

/**
 * 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;
}
2 голосов
/ 15 октября 2010

Если у вас есть контроль над доменом, в котором размещена внешняя таблица стилей, может помочь добавить соответствующий заголовок Access-Control-Allow-Origin .

Access-Control-Allow-Origin: http://stylesheet-user.example.com
1 голос
/ 11 июня 2012

У меня была похожая проблема в Firefox и Chrome.Я решил это жестко, добавив к своему домену файл css, который включал внешний домен css, например:

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

Это быстро, но грязно.Рекомендуется хранить все CSS-файлы в вашем домене.

1 голос
/ 26 июня 2011

Если это срабатывает для вас, потому что часть вашего 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;
}
0 голосов
/ 19 июля 2010

Возможно, вам придется написать прокси для этого CSS-файла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...