【问题标题】:Generate css dynamically via templates and placeholders通过模板和占位符动态生成 css
【发布时间】:2017-05-20 10:25:59
【问题描述】:

我想在运行时动态生成css

最初我使用sass 并定义了一些变量并正在使用这些变量。但是必须首先从scss 生成css。 Sass 让我可以灵活地使用变量和函数,但我仍然无法在运行时通过 javascript 更改它们。

一种方法是通过javascript 更改内联样式,但这种方法并不完全灵活。

 document.getElementById("myDiv").style.color = "red"; 

我不想在上面做,也不想通过javascript附加任何<style>属性。

我想使用 javascript,但不想更改每个样式属性。我想使用 css 和 javascript 实现类似 scss 的效果,但在运行时,即动态。

例如假设我从 ajax 调用 中获得了 color 信息,现在我想根据立即收到的 color 更改网站的整个主题,而无需重新启动或重新部署我的应用程序。

例如

就像在 scss 中所做的那样

.myClass {
           background:$color;
           // Update color value dynamically at run-time
}

有没有可能,或者我想错了方向!

【问题讨论】:

  • 你可能想看看使用 cookie、会话或本地变量
  • 查看 CSS 变量,也称为自定义属性。
  • 你会从 ajax 调用中得到一组(相当)固定的变量吗?
  • 取决于您要更改的元素数量,以及间距和各种颜色的编程相关程度,CSS 变量和 CSS 过滤器css-tricks.com/almanac/properties/f/filter 可能会带您走很长一段路。使用 JS 更新 ::root 上的变量,rest 应该级联。

标签: javascript html css sass


【解决方案1】:

结束了使用 this 和 CSS 变量。我要添加第二个答案,因为它与我的第一个答案非常不同,并且它更符合您的原始问题(使用 JS 更新 CSS 变量)。

但是...不要这样做。 :) IE <style> 元素要慢,尽管我还没有测试过。这个jsperf 测试了各种样式更新方法。它没有在单个 style 元素上包含 innerHTML(可能是最快的),但您可以看到以下 CSS DOM 方法比其他方法慢。

// get the stylesheet
// array position depends on how many style sheets you're loading.     
// adjust as needed.
var sheet = document.styleSheets[0];


// simplest method: insertRule()
// setTimeout only for demo so you can see the change
window.setTimeout(function(){
  // @media all {} is a trick to insert more than one 
  // selector and/or properties at once. Otherwise it's:
  // sheet.insertRule(":root", "--header-color: green"); ...repeat...
  sheet.insertRule("@media all { :root { --header-color: green; --main-color: orange;  } }", 1);
}, 1200);



// SAFER method via addCSSRule. 
// button and getAjaxStyles are just placeholders, obviously
var btn = document.querySelector('button');
btn.addEventListener("click", getAjaxStyles);

function getAjaxStyles() {
  // success callback... break apart the json and update the CSS variables
  addCSSRule(sheet, ":root", "--header-color: orange");
  addCSSRule(sheet, ":root", "--main-color: blue");
  addCSSRule(sheet, ":root", "--alt-color: red");
  addCSSRule(sheet, ":root", "--borderColorA: lavender");
  
  // or go with a single big string. definitely faster:
  // addCSSRule(sheet, ":root", "--alt-color: red; --borderColorA: #0ff; ")
}

// Credit for addCSSRule() goes to Diego Flórez in a comment on 
// https://davidwalsh.name/add-rules-stylesheets
var addCSSRule = function(sheet, selector, rules) {
  //Backward searching of the selector matching cssRules
  var index = sheet.cssRules.length - 1;
  for (var i = index; i > 0; i--) {
    var current_style = sheet.cssRules[i];
    if (current_style.selectorText === selector) {
      //Append the new rules to the current content of the cssRule;
      rules = current_style.style.cssText + rules;
      sheet.deleteRule(i);
      index = i;
    }
  }
  if (sheet.insertRule) {
    sheet.insertRule(selector + "{" + rules + "}", index);
  } else {
    sheet.addRule(selector, rules, index);
  }
  return sheet.cssRules[index].cssText;
}
/* Set initial CSS variables */
:root {
  --header-color: #333;
  --main-color: #888;
  --alt-color: #bbb;
  --borderColorA: #ccc;
}
h1 {
  color: var(--header-color);
}
p {
  border-bottom: 1px solid var(--borderColorA);
  color: var(--main-color);
}
p+p {
  color: var(--alt-color);
}
<h1>header</h1>
<p>paragraph 1</p>
<p>paragraph 2</p>
<button>Update CSS Variables</button>

【讨论】:

  • 在更新变量方面似乎付出了很多努力。非常感谢您的努力和研究。
【解决方案2】:

要扩展链接的“可能重复”问题中提供的信息,您可以轻松地在页面 CSS 文件中设置“默认”样式集,然后创建包含任何基于覆盖的内联 &lt;style&gt; ekement根据您的 AJAX 调用的响应。只要两个位置(即 CSS 文件和内联样式部分)中的元素/类/ID 定义相同,特异性就会导致内联定义覆盖 CSS 定义。

因此,使用您的示例,您的静态 CSS 文件将包含:

.myClass {
    background: #FFFFFF;
}

。 . .以便在 AJAX 调用失败时有一个默认值,然后您动态创建的 &lt;style&gt; 部分将包含:

.myClass {
    background: THE_AJAX_RESPONSE_VALUE;
}

。 . .这将覆盖默认值。

更新 #1:

根据您的示例 JSON,这将非常容易。 . .您将遍历 JSON 的每个顶级属性并创建:

KEY_NAME {
    . . .
}

然后,在该块中,遍历该属性中的每个属性并添加键和值以创建样式定义:

KEY_NAME {
    key1: value1,
    key2: value2,
    . . .
    keyN: valueN
}

更新 #2:

您还可以使用StyleSheetCSSStyleSheet 接口来访问现有样式表中的规则,但是,鉴于它使用类似数组的结构,这意味着遍历所有 CSS 定义以查找一个你想要的并改变它。可以在另一个 SO 问题的答案中找到如何执行此操作的示例:Is it possible to alter a CSS stylesheet using JavaScript? (NOT the style of an object, but the stylesheet itself)

不过,在这两种方法之间,创建覆盖 &lt;style&gt; 部分似乎更简单。

【讨论】:

  • 我不想通过 js 创建新的&lt;Style&gt; 标签我想使用现有的 css 并用新值更新占位符或变量或合并它们
  • @WitVault - 简答。 . .不。这就是创建 SASS 之类的东西的原因,以增加类似的灵活性(尽管实际上并不是您正在寻找的特定灵活性)。我添加了另一个替代方法的链接,它允许您自己实际操作样式表规则,但我仍然认为覆盖 &lt;style&gt; 部分是更好的选择。
【解决方案3】:

由于 JSON 具有元素名称和相关样式,刷新页面样式表(与内联元素样式相比)可能是最快的,因为它使用 innerHTML 并且只需要一次 DOM 查找。

您需要遍历 JSON 以创建与 CSS 兼容的字符串,然后将其转储到 onpage 样式元素中。您可以通过将现有的 innerHTML 与新的 CSS 字符串连接来附加 CSS。为简单起见,我在样式表中添加了一个 ID,但您也可以在需要时生成样式元素。

var StringifiedAjaxStyleObject = "h1 {background-color: #ecc; color: #633}";
var styleSheet = document.getElementById("style-update");

// some additional fake test style returns...
var testStyle1 = "h1 {background-color: #ccc; color: #333}";
var testStyle2 = "h1 {background-color: #667; color: #bbc}";
var testStyle3 = "h1 {background-color: #fee; color: #b00}";

// some fake ajax returns...
window.setTimeout(function() {
  styleSheet.innerHTML = StringifiedAjaxStyleObject;
}, 1000);
window.setTimeout(function() {
  styleSheet.innerHTML = testStyle1;
}, 2000);
window.setTimeout(function() {
  styleSheet.innerHTML = testStyle2;
}, 3000);
window.setTimeout(function() {
  styleSheet.innerHTML = testStyle3;
}, 4000);
/* base styles ... */
h1 {
  padding: 5px;
  background-color: #eef;
  color: #007
}
<!-- empty stylesheet -->
<style id="style-update" type="text/css" rel="stylesheet"></style>

<h1>Hi, mom</h1>
<button>Update Styles<button>

编辑:

这是基于您评论中的 JSON 对象的稍微更真实的版本。通过按钮触发。

var styleSheet = document.getElementById("style-update");
var btn = document.querySelector('button');

btn.addEventListener("click", updateStyles);

function updateStyles() {
  var StringifiedAjaxStyleObject
    , newCSS
    , ajaxReturn
  ;

  // ...your ajax method to get the new styles...
  // on success...
  ajaxReturn = {
    ".base": {
      "background-color": "#b83605",
      "border-color": "#543927",
      "color": "gray",
      "text-shadow": "0 -1px 0 rgba(0, 0, 0, 0.15)"
    },
    ".overlay": {
      "background": "rgba(76, 65, 80, 0.2)",
      "color" : "#ddd"
    }
  };
  
  // Convert the object to a string
  newCSS = cssStringFromJson(ajaxReturn);

  // Update the stylesheet
  styleSheet.innerHTML = newCSS;
}


function cssStringFromJson(cssJSON) {
  var styleStr = "",
    i, j;

  for (i in cssJSON) {
    styleStr += i + " {\n"
    for (j in cssJSON[i]) {
      styleStr += "\t" + j + ": " + cssJSON[i][j] + ";\n"
    }
    styleStr += "}\n"
  }

  return styleStr;
}
/* base styles ... */
.base {
  border: 1px solid #ccf;
  background-color: #eef;
  color: #000;
  padding: 15px;
}

.overlay {
  padding: 5px 15px;
  background: rgba(96, 95, 180, 0.2);
}

body {
  font-family: sans-serif;
}

button {
  margin-top: 1em;
  font-size: 1em;
}
<!-- empty stylesheet -->
<style id="style-update" type="text/css" rel="stylesheet"></style>

<div class="base">
  <p>.base</p>
  <div class="overlay">
    <p>.overlay</p>
  </div>
</div>

<button>Update Styles</button>

【讨论】:

    【解决方案4】:

    您可以尝试 Angular 模板。 它会打破你以前的 sass,但它会在稍后解决。

    【讨论】:

    • 我没有使用角度
    猜你喜欢
    • 2014-06-20
    • 2020-02-29
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 2019-12-26
    • 1970-01-01
    • 2018-10-29
    • 2015-05-10
    相关资源
    最近更新 更多