项目场景
需要解析外联样式表,收集其中的 CSS 样式插入到 style
标签中。
问题描述
但是当 DOM 中存在跨域外联样式表的时候,执行 resolveStyleSheets
方法会报错,信息如下:
DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules at resolveStyleSheets.
function resolveStyleSheets() {
const styleSheets = document.styleSheets
console.log(styleSheets);
if (styleSheets.length <= 0) return
try {
const styleEl = document.createElement('style');
for (let i = 0; i < styleSheets.length; i++) {
const cssRules = styleSheets.item(i).cssRules;
// styleSheets.item(i).
for (let j = 0; j < cssRules.length; j++) {
const cssRule = cssRules.item(j);
cssText = cssRule.cssText || '';
const textNode = document.createTextNode(cssText);
styleEl.append(textNode);
}
}
document.head.appendChild(styleEl);
document.querySelectorAll('link[rel=stylesheet]').forEach(linkEl => linkEl.parentNode.removeChild(linkEl));
} catch (error) {
console.error('resolveStyleSheets error,', error);
}
}
解决方案
在收集样式之前,先为 link
标签添加 crossorigin
属性,并重新将其挂载到 DOM 上。
添加
crossorigin
属性是为了使浏览器在解析该 StyleSheet 时设置origin-clean flag
。但是由于该属性只会在构建时设置,所以复制一份重新挂载到 DOM。具体可参见 此处 。
function insertCrossOrigin() {
const head = document.head;
document.querySelectorAll('link[rel=stylesheet]').forEach(linkEl => {
// head.removeChild(linkEl);
const newLink = linkEl.cloneNode()
newLink.setAttribute('crossorigin', 'anonymous');
head.appendChild(newLink);
})
}
原因分析
cssRules 属性的读取必须遵循以下规则:
- 如果
origin-clean flag
未设置,会抛出 SecurityError ,即前文提到的报错信息。 - 否则返回一个只读的 CSS Rules 。