在上一章中,我们学会了如何获取样式表对象。但获取样式表只是第一步——就像你拿到了一个装满文件的柜子,真正的挑战在于 如何操作柜子里的每一份文件

CSSOM 的核心操作对象,就是样式表中的每一条具体规则。这些规则在 JavaScript 中以 CSSRule 对象的形式存在。通过操作这些对象,我们可以:

  • 读取某条规则的选择器和样式声明。

  • 动态添加一条全新的样式规则。

  • 删除不再需要的规则。

  • 修改现有规则的选择器或样式值。

这一系列操作,构成了“用 JavaScript 动态操控 CSS”的真正能力。本章将逐一拆解这些操作,让你从“会改内联样式”进阶到“会操控样式表本身”。

一、cssRules —— 获取样式表中的所有规则

每个 CSSStyleSheet 对象都有一个 cssRules 属性,它返回一个 CSSRuleList 对象。这个列表包含了样式表中按顺序排列的所有 CSS 规则。

const sheet = document.styleSheets[0];
const rules = sheet.cssRules;
console.log(rules.length); // 输出规则的数量
CSSRuleList 的特点

CSSRuleList 是一个 类数组对象,支持通过索引访问和 length 属性。但它 不是 真正的数组,不能直接使用 forEach 等方法。

// 正确:通过索引访问
const firstRule = sheet.cssRules[0];

// 正确:使用 for...of 遍历
for (const rule of sheet.cssRules) {
    console.log(rule.cssText);
}

// 错误:CSSRuleList 没有 forEach 方法
// sheet.cssRules.forEach(rule => console.log(rule)); // 报错!

// 变通方案:转为数组
const rulesArray = [...sheet.cssRules];
rulesArray.forEach(rule => console.log(rule.selectorText));
一个重要的特性:实时更新

cssRules 返回的 CSSRuleList 是 实时(live) 的。这意味着,当你通过 insertRule() 或 deleteRule() 修改样式表后,cssRules 会自动反映最新的状态,不需要重新获取。

const sheet = document.styleSheets[0];
console.log(sheet.cssRules.length); // 假设输出 3

// 插入一条新规则
sheet.insertRule('.new-class { color: red; }', 0);
console.log(sheet.cssRules.length); // 输出 4 —— 自动更新了!
关于 rules 属性的补充

你可能在某些旧代码中见过 sheet.rules 而不是 sheet.cssRules。rules 是早期 IE 的实现方式,虽然功能上与 cssRules 相同,但 推荐统一使用标准的 cssRules

二、insertRule() —— 向样式表中添加新规则

要向样式表中添加一条新规则,使用 insertRule() 方法。

sheet.insertRule(rule, index);
  • rule(必需):一个字符串,包含要插入的完整 CSS 规则。对于普通样式规则,需要包含选择器和样式声明;对于 @规则(如 @media),需要包含完整的 at 标识符和内容。

  • index(可选):新规则插入的位置,默认为 0(插入到最前面)。该值不能大于 cssRules.length。

  • 返回值:新插入规则在 cssRules 中的索引。

基本用法示例
const sheet = document.styleSheets[0];

// 在末尾添加一条规则(index 等于 length 表示追加到末尾)
sheet.insertRule('.btn-primary { background: blue; color: white; }', sheet.cssRules.length);

// 在最前面插入一条规则(index 为 0)
sheet.insertRule('/* 全局重置 */ * { margin: 0; padding: 0; }', 0);

// 在索引为 2 的位置插入
sheet.insertRule('.warning { border: 2px solid orange; }', 2);
插入 @规则

insertRule() 同样支持插入 @media、@keyframes 等 at 规则:

// 插入媒体查询规则
sheet.insertRule('@media (max-width: 600px) { .sidebar { display: none; } }', 0);

// 插入关键帧动画
sheet.insertRule('@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }', 0);
异常处理

insertRule() 在以下情况会抛出异常:

  • IndexSizeError:当 index > cssRules.length 时抛出。

  • HierarchyRequestError:当规则无法在指定位置插入时抛出(例如,在样式规则之后插入 @import 规则是不允许的)。

  • SyntaxError:当规则字符串格式不正确时抛出。

因此,建议用 try...catch 包裹:

try {
    sheet.insertRule('.invalid { color: red; }', 0);
} catch (e) {
    console.error('插入规则失败:', e.message);
}

三、deleteRule() —— 删除样式表中的规则

要删除样式表中的某条规则,使用 deleteRule() 方法。

sheet.deleteRule(index);
  • index(必需):要删除的规则在 cssRules 中的索引。

  • 返回值:无(undefined)。

const sheet = document.styleSheets[0];

// 删除第一条规则
sheet.deleteRule(0);

// 删除最后一条规则
const lastIndex = sheet.cssRules.length - 1;
sheet.deleteRule(lastIndex);
removeRule() —— 一个过时的方法

你可能在旧代码中见过 removeRule() 方法。它是一个遗留方法,功能与 deleteRule() 完全相同,但已被标准方法 deleteRule() 取代

// 不推荐:过时的 removeRule()
sheet.removeRule(0);

// 推荐:使用标准的 deleteRule()
sheet.deleteRule(0);

在现代浏览器中,统一使用 deleteRule() 即可

四、CSSStyleRule —— 深入规则内部

在 cssRules 列表中,最常见的规则类型是 CSSStyleRule,它代表一条普通的样式规则(选择器 + 样式声明)。

CSSStyleRule 有两个核心属性:

selectorText —— 获取和设置选择器

selectorText 属性返回规则的选择器文本,并且是可读写的

const sheet = document.styleSheets[0];
const rule = sheet.cssRules[0];

// 读取选择器
console.log(rule.selectorText); // 输出例如 "h1" 或 ".container"

// 修改选择器 —— 这会同时更新样式表中的规则!
rule.selectorText = '.main-title';

修改 selectorText 会 直接影响样式表 中对应规则的选择器,所有匹配该选择器的元素样式都会随之变化。

style —— 操作样式声明

style 属性返回一个 CSSStyleDeclaration 对象,代表该规则的所有样式声明。这个对象 虽然标记为只读,但它的属性是可读写的

const sheet = document.styleSheets[0];
const rule = sheet.cssRules[0];

// 读取样式
console.log(rule.style.color);     // 输出例如 "red"
console.log(rule.style.fontSize);  // 输出例如 "16px"

// 修改样式
rule.style.color = 'blue';
rule.style.backgroundColor = '#f0f0f0';

// 添加新样式属性
rule.style.setProperty('border-radius', '8px');

// 删除样式属性
rule.style.removeProperty('margin-top');

使用 setProperty() 和 removeProperty() 方法可以更精细地控制样式声明。

一个完整的操作示例

来看一个综合示例,演示如何获取一条规则并完整地修改它:

<style id="demo">
    .box { 
        width: 200px; 
        height: 200px; 
        background: red; 
    }
</style>

<script>
    const sheet = document.getElementById('demo').sheet;
    const rule = sheet.cssRules[0]; // 获取 .box 规则
    
    console.log('修改前:', rule.selectorText, rule.style.cssText);
    // 输出: .box width: 200px; height: 200px; background: red;
    
    // 修改选择器
    rule.selectorText = '.square';
    
    // 修改样式
    rule.style.background = 'blue';
    rule.style.borderRadius = '50%';
    rule.style.setProperty('transition', 'all 0.3s');
    
    console.log('修改后:', rule.selectorText, rule.style.cssText);
    // 输出: .square width: 200px; height: 200px; background: blue; 
    //       border-radius: 50%; transition: all 0.3s;
</script>

这个操作直接修改了样式表本身,页面上所有匹配 .square 类的元素都会立刻应用新的样式。

五、通过 cssText 批量操作 —— 一个重要的注意事项

CSSRule 接口有一个 cssText 属性,它返回该规则完整的 CSS 文本。

const sheet = document.styleSheets[0];
const rule = sheet.cssRules[0];
console.log(rule.cssText); 
// 输出例如: ".box { width: 200px; height: 200px; background: red; }"
重要:cssText 现在是只读的

过去,cssText 属性是可读写的,开发者可以通过直接赋值来批量修改整条规则。但 现在它已经被规范改为只读属性

// 这样做没有任何效果(也不会报错)
rule.cssText = '.new-box { width: 300px; }';
console.log(rule.cssText); // 仍然是原来的内容!

尝试设置 cssText 不会产生任何效果,也不会发出警告或错误。

正确的批量修改方式

既然 cssText 不能再直接赋值,那么如何“批量”修改规则呢?有两种方式:

方式一:逐条修改 selectorText 和 style 属性

这是最直接的方式,通过分别修改选择器和样式声明来达到“批量更新”的效果:

const rule = sheet.cssRules[0];
rule.selectorText = '.new-class';
rule.style.cssText = 'width: 300px; height: 300px; background: green;';

注意:rule.style.cssText 和 rule.cssText 是 不同的东西!前者是 CSSStyleDeclaration 的属性,仍然是可读写的;后者是 CSSRule 的属性,现在是只读的

方式二:删除旧规则,插入新规则

如果需要彻底替换一条规则,最可靠的方式是先删除再插入:

const sheet = document.styleSheets[0];
const index = 0;

// 删除旧规则
sheet.deleteRule(index);

// 插入新规则(完整的 CSS 文本)
sheet.insertRule('.new-class { width: 300px; height: 300px; background: green; }', index);
新旧方法对比

操作方式

适用场景

注意事项

rule.selectorText = ...

只改选择器

直接生效

rule.style.xxx = ...

只改单个样式属性

直接生效

rule.style.cssText = ...

批量修改样式声明

仍然可用

rule.cssText = ...

批量修改整条规则

已失效(只读)

deleteRule() + insertRule()

完全替换整条规则

最可靠的方式

六、实战场景:动态管理主题样式

将这些知识整合到一个实际场景中——实现一个简单的“主题切换”功能,动态修改页面中所有按钮的样式:

<style id="theme-styles">
    .btn {
        padding: 12px 24px;
        border: none;
        border-radius: 4px;
        font-size: 16px;
        cursor: pointer;
        background: #007bff;
        color: white;
    }
    .btn:hover {
        background: #0056b3;
    }
</style>

<button class="btn">点击我</button>
<button onclick="switchTheme()">切换主题</button>

<script>
    function switchTheme() {
        const sheet = document.getElementById('theme-styles').sheet;
        
        // 找到 .btn 规则(假设它是第一条规则)
        const btnRule = sheet.cssRules[0];
        
        // 读取当前背景色,决定切换方向
        const currentBg = btnRule.style.background;
        
        if (currentBg === 'rgb(0, 123, 255)' || currentBg === '#007bff') {
            // 切换到暗色主题
            btnRule.style.background = '#6c757d';
            btnRule.style.color = '#fff';
        } else {
            // 切换回亮色主题
            btnRule.style.background = '#007bff';
            btnRule.style.color = 'white';
        }
        
        // 修改 hover 状态(假设是第二条规则)
        const hoverRule = sheet.cssRules[1];
        if (hoverRule && hoverRule.selectorText === '.btn:hover') {
            const isDark = btnRule.style.background === 'rgb(108, 117, 125)';
            hoverRule.style.background = isDark ? '#5a6268' : '#0056b3';
        }
    }
</script>

这个例子展示了如何通过 CSSOM 直接操作样式表中的规则,实现全局样式的动态切换——而不需要为每个按钮单独修改内联样式。

七、小结

本章我们深入了 CSSOM 的核心操作层面,掌握了操作样式规则的完整工具箱:

  1. cssRules:获取样式表中的所有规则,返回一个实时的 CSSRuleList。通过索引访问单条规则,用 for...of 遍历所有规则。

 2. insertRule():向样式表中添加新规则。需要传入完整的规则字符串,可以指定插入位置。支持普通样式规则和 @规则。

 3. deleteRule():从样式表中删除指定索引的规则。removeRule() 是过时的替代方法,应使用 deleteRule()。

 4. CSSStyleRule 接口:代表一条具体的样式规则。selectorText 可读写,用于获取或修改选择器;style 属性返回 CSSStyleDeclaration 对象,用于精细操作每条样式声明。

 5. cssText 的注意事项:CSSRule.cssText 现在 只读,不能直接赋值。批量修改应使用 rule.style.cssText(注意区分)或“删除 + 插入”的方式。

掌握了这些操作,你就真正拥有了“用 JavaScript 编写 CSS”的能力。在下一章中,我们将把视角从“样式表本身”转向“单个元素”——学习如何通过 element.style 操作内联样式,以及它和操作样式表规则之间的区别与联系。