【问题标题】:Inject CSS stylesheet as string using Javascript使用 Javascript 将 CSS 样式表作为字符串注入
【发布时间】:2013-03-08 11:10:55
【问题描述】:

我正在开发一个 Chrome 扩展程序,我希望用户能够添加自己的 CSS 样式来更改扩展程序页面(而不是网页)的外观。我已经研究过使用document.stylesheets,但它似乎希望将规则拆分,并且不会让您注入完整的样式表。有没有一种解决方案可以让我使用字符串在页面上创建新样式表?

我目前没有使用 jQuery 或类似的,所以最好使用纯 Javascript 解决方案。

【问题讨论】:

标签: javascript css dom google-chrome-extension


【解决方案1】:

有几种方法可以做到这一点,但最简单的方法是创建一个<style> 元素,设置其textContent 属性,然后附加到页面的<head>

/**
 * Utility function to add CSS in multiple passes.
 * @param {string} styleString
 */
function addStyle(styleString) {
  const style = document.createElement('style');
  style.textContent = styleString;
  document.head.append(style);
}

addStyle(`
  body {
    color: red;
  }
`);

addStyle(`
  body {
    background: silver;
  }
`);

如果您愿意,您可以稍微更改一下,以便在调用 addStyle() 时替换 CSS,而不是附加它。

/**
 * Utility function to add replaceable CSS.
 * @param {string} styleString
 */
const addStyle = (() => {
  const style = document.createElement('style');
  document.head.append(style);
  return (styleString) => style.textContent = styleString;
})();

addStyle(`
  body {
    color: red;
  }
`);

addStyle(`
  body {
    background: silver;
  }
`);

IE编辑:注意IE9及以下版本只允许up to 32 stylesheets,所以在使用第一个sn-p时要小心。 IE10 增加到 4095 个。

2020 年编辑: 这个问题已经很老了,但我仍然偶尔会收到有关它的通知,因此我将代码更新为更现代并替换了 .innerHTML.textContent。这个特定实例是安全的,但尽可能避免使用innerHTML 是一种很好的做法,因为它可能是 XSS 攻击向量。

【讨论】:

  • 我最终会想出innerHTML,但是你提供的第二个sn-p真的很酷!
  • 两个 sn-ps 都给我错误:TypeError: document.body is null document.body.appendChild(node);
  • 你不能追加到节点的innerHTML 以连续添加吗?例如node.innerHTML = node.innerHTML + " " + str;?
【解决方案2】:

感谢this guy,我找到了正确答案。以下是它的完成方式:

function addCss(rule) {
  let css = document.createElement('style');
  css.type = 'text/css';
  if (css.styleSheet) css.styleSheet.cssText = rule; // Support for IE
  else css.appendChild(document.createTextNode(rule)); // Support for the rest
  document.getElementsByTagName("head")[0].appendChild(css);
}

// CSS rules
let rule  = '.red {background-color: red}';
    rule += '.blue {background-color: blue}';

// Load the rules and execute after the DOM loads
window.onload = function() {addCss(rule)};

fiddle

【讨论】:

    【解决方案3】:

    你听说过 Promise 吗?它们适用于所有现代浏览器,并且使用起来相对简单。看看这个将css注入html头部的简单方法:

    function loadStyle(src) {
        return new Promise(function (resolve, reject) {
            let link = document.createElement('link');
            link.href = src;
            link.rel = 'stylesheet';
    
            link.onload = () => resolve(link);
            link.onerror = () => reject(new Error(`Style load error for ${src}`));
    
            document.head.append(link);
        });
    }
    

    你可以如下实现:

    window.onload = function () {
        loadStyle("https://fonts.googleapis.com/css2?family=Raleway&display=swap")
            .then(() => loadStyle("css/style.css"))
            .then(() => loadStyle("css/icomoon.css"))
            .then(() => {
                alert('All styles are loaded!');
            }).catch(err => alert(err));
    }
    

    真的很酷,对吧?这是一种使用 Promises 决定样式优先级的方法。

    或者,如果你想同时导入所有样式,你可以这样做:

    function loadStyles(srcs) {
        let promises = [];
        srcs.forEach(src => promises.push(loadStyle(src)));
        return Promise.all(promises);
    }
    

    像这样使用它:

    loadStyles([
        'css/style.css',
        'css/icomoon.css'
    ]);
    

    您可以实现自己的方法,例如按优先级导入脚本、同时导入脚本或同时导入样式和脚本。如果我获得更多选票,我将发布我的实现。

    如果您想了解有关 Promises 的更多信息,请阅读更多here

    【讨论】:

    • 确实很有趣。在这里,您等待一个 css 的结束来加载下一个,但一次加载它不是更有效吗?
    • 一次加载它们肯定更有效。但是,在某些情况下,可能需要分配某个优先级。我已经发布了一个同步样式加载实现。
    • 我想这是更好的解决方案,因为与公认的解决方案相比,promise 将异步运行并且不会呈现阻塞
    【解决方案4】:

    我最近也有同样的需求,并编写了一个函数来做与 Liam 相同的功能,除了还允许多行 CSS。

    injectCSS(function(){/*
        .ui-button {
            border: 3px solid #0f0;
            font-weight: bold;
            color: #f00;
        }
        .ui-panel {
            border: 1px solid #0f0;
            background-color: #eee;
            margin: 1em;
        }
    */});
    
    // or the following for one line
    
    injectCSS('.case2 { border: 3px solid #00f; } ');
    

    source of this function。您可以从Github repodownload。或者查看更多example usage here

    我更喜欢use it with RequireJS,但它也可以在没有 AMD 加载程序的情况下作为全局函数工作。

    【讨论】:

    • 您使用多行注释的方法很有趣。我不确定这个想法有多“好”,但它确实很有趣。
    • 我会说这是一种危险的误用。 CSS 也允许多行 cmets,所以从现有样式表复制粘贴样式时要小心。
    猜你喜欢
    • 2011-12-19
    • 2017-12-26
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 2011-01-15
    • 2012-02-06
    • 1970-01-01
    • 2013-02-21
    相关资源
    最近更新 更多