JavaScript动态操作CSS的完整指南:从内联样式到运行时样式表

作者:像素漫游

时间:2026-10-23

浏览:447

本文深入解析JavaScript动态操作CSS的多种技术路径。从基础的.style属性与.cssText修改内联样式,到利用.classList管理CSS类名,再到进阶使用document.styleSheets、insertRule等API构建运行时样式表。文章最后提供了一套完整的CSS-in-JS实现方案,展示如何通过JS动态生成并注入CSS规则,适用于需要高度动态样式的Web应用开发场景。

文章目录

JavaScript操作CSS的API概览

JavaScript作为Web交互的核心语言,结合丰富的Web API极大地扩展了其功能边界,使得界面交互更加灵活多样。常见的API包括WebGL、Canvas和DOM API,此外还有一组相对冷门但极具潜力的CSS API。

随着JSX及众多JS框架的普及,通过JS API直接操作DOM已成为主流做法。然而,在CSS领域采用类似技术的情况较少。尽管存在CSS-in-JS等方案,但主流方式仍依赖编译时转译以生成静态CSS,从而避免运行时重绘带来的性能损耗。但在某些场景下,开发者可能需要通过JS动态创建完整的样式表,而不仅仅是修改元素样式或类名。

内联样式与.style属性操作

内联样式

在探讨复杂技术之前,先回顾基础操作。我们可以通过修改HTMLElement的.style属性来直接编辑内联样式。


const el = document.createElement('div')

el.style.backgroundColor = 'red'
// 或者 
el.style.cssText = 'background-color: red'
// 或者
el.setAttribute('style', 'background-color: red')

直接在.style对象上设置属性时,需使用驼峰命名法(如backgroundColor)作为键名,而非CSS中的短横线命名。若需批量设置多个内联样式,可通过.style.cssText属性一次性赋值,这种方式更为高效。

需要注意的是,直接赋值给cssText会清空该元素原有的所有内联样式,因此必须一次性提供完整的样式定义。

若觉得逐个设置内联样式繁琐,还可以结合Object.assign()方法,通过.style属性一次性应用多个样式属性。


// ...
Object.assign(el.style, {
 backgroundColor: "red",
 margin: "25px"
})

这些基础操作背后隐藏着丰富的细节。.style对象实现了CSSStyleDeclaration接口,除了刚才提到的.cssText外,还包含.length(属性数量)、.item()、.getPropertyValue()和.setPropertyValue()等方法。


// ...
const propertiesCount = el.style.length
for(let i = 0; i < propertiesCount; i++) {
 const name = el.style.item(i) // 'background-color'
 const value = el.style.getPropertyValue(name) // 're'
 const priority = el.style.getPropertyPriority(name) // 'important'
 
 if(priority === 'important') {
  el.style.removeProperty()
 }
}

这里有一个实用技巧:在遍历.style对象时,.item()方法支持通过索引访问的语法形式。


// ...
el.style.item(0) === el.style[0]; // true

CSS类名管理与.classList接口

CSS类

接下来探讨更高级的结构——CSS类。在获取和设置类名时,通常使用字符串形式的.className属性。


// ...
el.className = "class-one class-two";
el.setAttribute("class", "class-one class-two");

另一种设置类名的方式是直接赋值给class属性(读取方式相同)。但与.style.cssText类似,直接设置.className会覆盖元素上所有现有的类名,因此需要手动包含未修改的类名。

虽然可以通过字符串操作实现上述功能,但更推荐使用较新的.classList属性。需要注意的是,IE9不支持该属性,IE10和IE11仅部分支持。

.classList属性实现了DOMTokenList接口,提供了多种实用方法。例如,.add()、.remove()、.toggle()和.replace()可用于增删改当前的CSS类集合;而.item()、.entries()和.forEach()则简化了对类名集合的遍历操作。


// ...
const classNames = ["class-one", "class-two", "class-three"];
classNames.forEach(className => {
 if(!el.classList.contains(className)) {
  el.classList.add(className);
 }
});

动态样式表与CSSStyleSheet API

Stylesheets

Web API中一直存在StyleSheetList接口,由document.styleSheets属性实现。该只读属性返回一个包含所有链接或嵌入样式表的StyleSheetList对象,每个对象代表文档中的一个样式表。


for(styleSheet of document.styleSheets){
 console.log(styleSheet);
}

通过遍历打印结果可知,每次循环输出的是CSSStyleSheet对象,每个对象包含以下属性:

属性 描述
media 获取当前样式作用的媒体。
disabled 打开或禁用一张样式表。
href 返回 CSSStyleSheet 对象连接的样式表地址。
title 返回 CSSStyleSheet 对象的title值。
type 返回 CSSStyleSheet 对象的type值,通常是text/css。
parentStyleSheet 返回包含了当前样式表的那张样式表。
ownerNode 返回CSSStyleSheet对象所在的DOM节点,通常是<link>或<style>。
cssRules 返回样式表中所有的规则。
ownerRule 如果是通过@import导入的,属性就是指向表示导入的规则的指针,否则值为null。IE不支持这个属性。

CSSStyleSheet对象方法包括:

方法 描述
insertRule() 在当前样式表的 cssRules 对象插入CSS规则。
deleteRule() 在当前样式表删除 cssRules 对象的CSS规则。

了解StyleSheetList后,我们进一步查看CSSStyleSheet本身。CSSStyleSheet扩展了StyleSheet接口,仅提供只读属性,如.ownerNode、.href、.title和.type,这些属性大多直接从定义样式表的地方获取。回顾加载外部CSS文件的HTML代码,即可理解其含义:


<head>
<link rel="stylesheet" type="text/css" href="style.css" rel="external nofollow" title="Styles">
</head>

现在我们知道,HTML文档可包含多个样式表,每个样式表可包含不同的规则,甚至通过@import引入更多样式表。CSSStyleSheet提供了.insertRule()和.deleteRule()两个方法,用于增加和删除CSS规则。


// ...
const ruleIndex = styleSheet.insertRule("div {background-color: red}");
styleSheet.deleteRule(ruleIndex);

.insertRule(rule, index):此函数可在cssRules规则集合中插入指定规则。参数rule为表示规则的字符串,index为插入位置的索引值。

.deleteRule(index):此函数可删除指定索引的规则,参数index规定要删除规则的索引。

CSSStyleSheet还包含.ownerRule和.cssRules两个属性。虽然.ownerRule与@import相关,但更值得关注的是.cssRules。简单来说,它是CSSRuleList中的CSSRule集合,可通过前述的.insertRule()和.deleteRule()方法进行修改。需注意,某些浏览器可能阻止跨域访问外部CSSStyleSheet的.cssRules属性。

那么,什么是CSSRuleList?

CSSRuleList是一个数组对象,包含有序的CSSRule对象集合。每个CSSRule可通过rules.item(index)或rules[index]访问。这里的rules是实现了CSSRuleList接口的对象,index是从0开始的整数,顺序与CSS样式表中的顺序一致。样式对象的数量由rules.length表示。

对于CSSStyleRule对象:

每个CSSStyleSheet对象可包含若干CSSStyleRule对象,即CSS样式规则,如下所示:


<style type="text/css">
 h1{color:red}
 div{color:green}
</style>

在上述代码中,style标签是一个CSSStyleSheet样式表对象,该对象包含两个CSSStyleRule对象,即两条CSS样式规则。

CSSStyleRule对象具有以下属性:

1.type:返回0-6的数字,表示规则类型,具体列表如下:

0:CSSRule.UNKNOWN_RULE。

1:CSSRule.STYLE_RULE(定义一个CSSStyleRule对象)。

2:CSSRule.CHARSET_RULE(定义一个CSSCharsetRule对象,用于设定当前样式表的字符集,默认与当前网页相同)。

3:CSSRule.IMPORT_RULE(定义一个CSSImportRule对象,用于通过@import引入其他样式表)。

4:CSSRule.MEDIA_RULE(定义一个CSSMediaRule对象,用于设定此样式适用于显示器、打印机或投影机等媒体类型)。

5:CSSRule.FONT_FACE_RULE(定义一个CSSFontFaceRule对象,对应CSS3的@font-face)。

6:CSSRule.PAGE_RULE(定义一个CSSPageRule对象)。

2.cssText:返回一个字符串,表示当前规则的内容,例如:


div{color:green}

3.parentStyleSheet:返回所在的CSSStyleRule对象。

4.parentRule:如果规则位于另一规则中,该属性引用另一个CSSRule对象。

5.selectorText:返回此规则的选择器,如上面的div即为选择器。

6.style:返回一个CSSStyleDeclaration对象。


// ...
const ruleIndex = styleSheet.insertRule("div {background-color: red}");
const rule = styleSheet.cssRules.item(ruleIndex);

rule.selectorText; // "div"
rule.style.backgroundColor; // "red"

构建运行时CSS-in-JS实现

实现

现在,我们对CSS相关的JS API有了足够了解,可以创建自己的小型运行时CSS-in-JS实现。思路是创建一个函数,接收简单的样式配置对象,生成一个新的CSS类哈希名称供后续使用。

实现流程简单:需要一个能访问某种样式表的函数,只需使用.insertRule()方法和样式配置即可运行。先从样式表部分开始:


function createClassName(style) {
 // ...
 let styleSheet;
 for (let i = 0; i < document.styleSheets.length; i++) {
 if (document.styleSheets[i].CSSInJS) {
  styleSheet = document.styleSheets[i];
  break;
 }
 }
 if (!styleSheet) {
 const style = document.createElement("style");
 document.head.appendChild(style);
 styleSheet = style.sheet;
 styleSheet.CSSInJS = true;
 }
 // ...
}

如果使用ESM或其他JS模块系统,可在函数外部安全创建样式表实例,无需担心被其他代码访问。但为演示示例,我们将stylesheet上的.CSSInJS属性设为标志位,通过该标志判断是否使用它。

如果还需要创建新样式表怎么办?最佳选择是创建一个新的<style/>标记,并将其附加到HTML文档的<head/>上。这会自动将新样式表添加到document.styleSheets列表,并允许通过<style/>标记的.sheet属性访问它,非常巧妙。


function createRandomName() {
 const code = Math.random().toString(36).substring(7);
 return `css-$[code]`;
}

function phraseStyle(style) {
 const keys = Object.keys(style);
 const keyValue = keys.map(key => {
 const kebabCaseKey = 
  key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
 const value = 
  `${style[key]}${typeof style[key] === "number" ? "px" : ""}`;
 
 return `${kebabCaseKey}:${value};`;
 });
 
 return `{${keyValue.join("")}}`;
}

除了上述技巧,我们首先需要一种为CSS类生成新随机名称的方法。然后,将样式对象正确表达为可行的CSS字符串形式。这包括驼峰命名与短横线全名之间的转换,以及可选的像素单位(px)转换处理。


function createClassName(style) {
 const className = createRandomName();
 let styleSheet;
 // ...
 styleSheet.insertRule(`.${className}${phraseStyle(style)}`);
 return className;
}

完整代码如下:

HTML


<div id="el"></div>

JS


function createRandomName() {
 const code = Math.random().toString(36).substring(7);
 return `css-$[code]`;
}

function phraseStyle(style) {
 const keys = Object.keys(style);
 const keyValue = keys.map(key => {
 const kebabCaseKey = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
 const value = `${style[key]}${typeof style[key] === "number" ? "px" : ""}`;
 return `${kebabCaseKey}:${value};`;
 });
 return `{${keyValue.join("")}}`;
}

function createClassName(style) {
 const className = createRandomName();
 let styleSheet;
 for (let i = 0; i < document.styleSheets.length; i++) {
 if (document.styleSheets[i].CSSInJS) {
  styleSheet = document.styleSheets[i];
  break;
 }
 }
 if (!styleSheet) {
 const style = document.createElement("style");
 document.head.appendChild(style);
 styleSheet = style.sheet;
 styleSheet.CSSInJS = true;
 }
 styleSheet.insertRule(`.${className}${phraseStyle(style)}`);
 return className;
}

const el = document.getElementById("el");

const redRect = createClassName({
 width: 100,
 height: 100,
 backgroundColor: "red"
});

el.classList.add(redRect);

运行效果:

JavaScript操作CSS的API概览

总结

正如本文所述,使用JS操作CSS非常有趣,我们可以挖掘许多实用的API。上述示例仅是冰山一角,在CSS API(或更准确地说,相关API)中还有更多方法等待被探索。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章