在上一章中,我们学会了如何获取样式表对象。但获取样式表只是第一步——就像你拿到了一个装满文件的柜子,真正的挑战在于 如何操作柜子里的每一份文件。
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 的核心操作层面,掌握了操作样式规则的完整工具箱:
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 操作内联样式,以及它和操作样式表规则之间的区别与联系。