在上一章中,我们认识了 CSSOM 是什么,以及它在浏览器渲染流程中的位置。现在,是时候真正动手操作了。

如果说 CSSOM 是一张“样式地图”,那么第一步就是要学会 如何拿到这张地图——也就是如何用 JavaScript 获取到文档中的样式表对象。

你可能会有疑问:我平时用 element.style.color = 'red' 不就能改样式了吗?为什么要费劲去获取样式表?

这是个好问题。element.style 操作的是 单个元素的内联样式,影响范围有限。而通过样式表对象,我们可以 直接修改样式表本身——比如一次性改变页面中所有 .btn 按钮的颜色,或者动态添加一条全新的样式规则。这种“从源头操控”的能力,是 element.style 无法替代的。

本章将带你掌握四种获取样式表的核心方法,并深入理解样式表对象的结构。准备好了吗?我们开始。

一、document.styleSheets —— 通往所有样式表的入口

获取文档中所有样式表最直接的方式,就是使用 document.styleSheets 属性。

const sheets = document.styleSheets;
console.log(sheets); // 输出一个 StyleSheetList 对象

document.styleSheets 返回一个 StyleSheetList 对象。你可以把它理解为一个“样式表列表”,里面按文档顺序存放着当前页面所有的样式表——无论是通过 <link> 引入的外部样式表,还是通过 <style> 标签定义的内部样式表,都会被收录其中。

如何访问具体的样式表?

StyleSheetList 是一个 类数组对象,这意味着你可以通过索引来访问其中的每一项:

// 获取文档中的第一个样式表
const firstSheet = document.styleSheets[0];

// 获取样式表的数量
const count = document.styleSheets.length;
console.log(`页面共有 ${count} 个样式表`);

重要提示: StyleSheetList 虽然长得像数组,但它 不是 真正的数组,不能直接使用 map()、forEach() 等数组方法。不过,你可以用 for...of 循环来遍历它:

// 正确:使用 for...of 遍历
for (const sheet of document.styleSheets) {
    console.log(sheet.href || '内联样式表');
}

// 错误:StyleSheetList 没有 forEach 方法
// document.styleSheets.forEach(sheet => console.log(sheet)); // 报错!

// 变通方案:转换为数组后再使用数组方法
const sheetArray = [...document.styleSheets];
sheetArray.forEach(sheet => console.log(sheet.href));
关于“构造样式表”的补充

值得一提的是,除了传统的 <link> 和 <style> 方式,现在还可以通过 new CSSStyleSheet() 构造函数以编程方式创建样式表。这类“构造样式表”需要通过 document.adoptedStyleSheets 来挂载,不会出现在 document.styleSheets 中。这是较新的特性,有兴趣可以后续深入了解,但本章我们聚焦于传统的样式表获取方式。

二、通过 <style> 元素的 sheet 属性获取关联样式表

除了从 document.styleSheets 统一获取,还有一种更精确的方式: 直接通过 <style> 元素的 sheet 属性获取它关联的样式表对象

<style id="my-style">
    .btn { background: blue; color: white; }
    .btn:hover { background: darkblue; }
</style>

<script>
    const styleEl = document.getElementById('my-style');
    const sheet = styleEl.sheet;  // 获取与该 <style> 元素关联的样式表对象
    console.log(sheet.cssRules);  // 输出该样式表中的所有规则
</script>

sheet 属性是 只读 的,返回一个 StyleSheet 对象(更准确地说是 CSSStyleSheet 对象)。如果该 <style> 元素的 type 属性不是 text/css,则 sheet 会返回 null。

这种方法的好处是 精准定位——当页面有多个样式表时,你不需要通过索引去猜测哪个是哪个,直接通过 ID 或选择器获取对应的 <style> 元素,就能拿到它的样式表。

对于 <link> 元素呢?

你可能会问:<link> 元素有没有类似的属性?答案是有的。<link> 元素对应的接口是 HTMLLinkElement,它同样有一个 sheet 属性,用法完全一致:

<link id="main-css" rel="stylesheet" href="styles/main.css">

<script>
    const linkEl = document.getElementById('main-css');
    const sheet = linkEl.sheet;
    console.log(sheet.href); // 输出 "styles/main.css"
</script>

所以,无论是 <style> 还是 <link>,你都可以通过元素的 sheet 属性直接获取其关联的样式表对象。

三、样式表对象的基本属性与结构

当你成功获取到一个样式表对象后,它到底长什么样?我们来看看 CSSStyleSheet 接口的核心属性和结构。

CSSStyleSheet 的继承关系

CSSStyleSheet 接口代表一个 CSS 样式表,它 继承自父接口 StyleSheet。也就是说,它既有来自 StyleSheet 的基础属性,也有自己特有的属性和方法。

来自 StyleSheet 的基础属性

属性

类型

描述

href

字符串

外部样式表的 URL;如果是内联样式表则返回 null

title

字符串

样式表的标题(通过 <link title="..."> 或 <style title="..."> 设置)

type

字符串

样式表的 MIME 类型,通常是 "text/css"

parentStyleSheet

对象

如果该样式表是通过 @import 导入的,则指向父样式表

ownerNode

节点

拥有该样式表的 DOM 节点(<style> 或 <link> 元素)

disabled

布尔值

表示该样式表是否被禁用(可读写)

CSSStyleSheet 特有的属性

属性

类型

描述

cssRules

CSSRuleList

包含样式表中所有规则(CSSRule 对象)的实时列表

ownerRule

CSSImportRule

如果该样式表是通过 @import 导入的,返回对应的导入规则;否则返回 null

其中 cssRules 是最常用的属性。它返回一个 CSSRuleList,里面按顺序存放着样式表中的每一条规则。你可以通过索引访问单条规则:

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

// 获取第一条规则
const firstRule = rules[0];
console.log(firstRule.cssText); // 输出规则的完整 CSS 文本

// 遍历所有规则
for (const rule of rules) {
    console.log(rule.selectorText, rule.style);
}

小贴士: cssRules 返回的是一个 实时的列表。也就是说,如果你通过 insertRule() 或 deleteRule() 修改了样式表,cssRules 会自动反映最新的状态,不需要重新获取。

一个完整示例

来看一个综合示例,展示如何获取样式表并读取其中的信息:

<style id="theme">
    body { background: #f0f0f0; font-family: Arial, sans-serif; }
    .container { max-width: 1200px; margin: 0 auto; }
    .btn { padding: 10px 20px; border: none; border-radius: 4px; }
</style>

<script>
    const sheet = document.getElementById('theme').sheet;
    
    // 基础属性
    console.log('样式表类型:', sheet.type);        // "text/css"
    console.log('是否禁用:', sheet.disabled);      // false
    console.log('所属节点:', sheet.ownerNode);     // <style id="theme">
    console.log('外部 URL:', sheet.href);          // null(因为是内联样式表)
    
    // 读取规则
    console.log('规则数量:', sheet.cssRules.length); // 3
    for (let i = 0; i < sheet.cssRules.length; i++) {
        const rule = sheet.cssRules[i];
        console.log(`规则 ${i}: ${rule.selectorText} => ${rule.style.cssText}`);
    }
    // 输出:
    // 规则 0: body => background: rgb(240, 240, 240); font-family: Arial, sans-serif;
    // 规则 1: .container => max-width: 1200px; margin: 0px auto;
    // 规则 2: .btn => padding: 10px 20px; border: none; border-radius: 4px;
</script>

四、如何判断样式表是内联还是外部引入?

在实际开发中,我们经常需要区分样式表是通过 <link> 外部引入的,还是通过 <style> 内联定义的。这可以通过 href 属性和 ownerNode 属性来判断。

方法一:通过 href 属性

外部样式表(通过 <link> 引入)的 href 属性会返回完整的 URL 字符串;而 内联样式表(通过 <style> 定义)的 href 属性则为 null。

const sheets = document.styleSheets;

for (const sheet of sheets) {
    if (sheet.href) {
        console.log('外部样式表:', sheet.href);
    } else {
        console.log('内联样式表(<style> 或动态创建的)');
    }
}
方法二:通过 ownerNode 属性

ownerNode 属性返回拥有该样式表的 DOM 节点。通过检查 ownerNode.tagName,可以精确判断是 <link> 还是 <style>:

for (const sheet of document.styleSheets) {
    const node = sheet.ownerNode;
    if (node.tagName === 'LINK') {
        console.log('通过 <link> 引入的外部样式表:', sheet.href);
    } else if (node.tagName === 'STYLE') {
        console.log('通过 <style> 定义的内部样式表');
    } else {
        console.log('其他方式创建的样式表');
    }
}
实际应用场景

为什么要区分这两者?一个常见的场景是:动态修改样式时,避免误操作外部 CDN 样式表

假设你的页面引入了第三方 UI 库的样式(来自 CDN),同时也定义了自己的覆盖样式。如果你遍历所有样式表并批量修改规则,可能会意外破坏第三方库的样式。这时候,通过判断 href 是否包含特定域名,或者通过 ownerNode 的 ID 或 class 来筛选,就能精准锁定目标样式表。

// 只修改我们自己定义的样式表(假设我们的 <style> 标签有 id="app-styles")
const targetSheet = document.getElementById('app-styles').sheet;
// 然后安全地操作 targetSheet.cssRules...

五、小结

本章我们学习了获取和访问样式表的四种核心方法,以及样式表对象的基本结构:

  1. document.styleSheets:获取文档中所有样式表的统一入口,返回一个 StyleSheetList 类数组对象。

  2. 元素的 sheet 属性:通过 <style> 或 <link> 元素的 sheet 属性,精准获取其关联的样式表对象。

  3. 样式表对象的结构:CSSStyleSheet 继承自 StyleSheet,核心属性包括 href、ownerNode、disabled、cssRules 和 ownerRule。其中 cssRules 包含了样式表中的所有规则,是我们后续操作的重点。

 4. 判断样式表类型:通过 href 是否为 null,以及 ownerNode.tagName 是 LINK 还是 STYLE,可以准确区分外部样式表和内联样式表。

掌握了这些获取样式表的方法,你就拿到了操作 CSSOM 的“入场券”。在下一章中,我们将深入 CSSRule 的世界,学习如何读取、添加、修改和删除样式表中的具体规则——那才是真正“动态操控样式”的核心战场。