Web编程技术营地
研究、演示、创新

Manipulation of CSS With JavaScript

撰写时间:2025-11-14

修订时间:2025-11-22

内联样式与style属性的区别

let src = `
A demo div
`; let iframe = document.createElement('iframe'); iframe.srcdoc = src; iframe.onload = (evt) => { let domElement = iframe.contentDocument.querySelector('#demo'); pc.log(domElement.style.margin); pc.log(domElement.style.color); }; pc.appendChild(iframe);

可以看出,通过domElement.style的方式,只能取出其HTML标签的style属性中的相应值,而不能取出内联样式style中设置的值。

StyleSheetList

对于内联样式,可使用以下代码来获取:

function doOnIframeLoad(doc) { pc.log(doc.styleSheets); }

documentstyleSheets返回一个类型为StyleSheetList的对象。通过调用其length属性及item方法,可以遍历各个子元素。子元素的类型为CSSStyleSheet

let styleSrc = ` `; function doOnIframeLoad(doc) { let styleSheetList = doc.styleSheets; for (let i = 0; i < styleSheetList.length; i++) { pc.log(styleSheetList.item(i)); } }

每个CSSStyleSheet对应于所声明的每个style标签。因上面只声明了一个style,故StyleSheetListlength值为1

也可使用for ... of语句进行遍历:

function doOnIframeLoad(doc) { let styleSheetList = doc.styleSheets; for (let cssStyleSheet of styleSheetList) { pc.log(cssStyleSheet); } }

CSSStyleSheet

CSSStyleSheet,相对于上面代码中的:

这一部分。

CSSStyleSheet继承于StyleSheet,有3个关于样式规则的属性:ownerRule, cssRulesrules

let styleSrc = ` `; function doOnIframeLoad(doc) { for (let cssStyleSheet of doc.styleSheets) { pc.log(cssStyleSheet); pc.log(cssStyleSheet.ownerRule); pc.log(cssStyleSheet.cssRules); pc.log(cssStyleSheet.rules); pc.log(cssStyleSheet.cssRules === cssStyleSheet.rules); } }

如果通过@import来导入样式,则ownerRule指向相应的CSSImportRule。否则,该属性值为null

cssRulesrules是同一对象的不同引用,均返回CSSRuleList对象。

CSSRuleList

let styleSrc = ` `; function doOnIframeLoad(doc) { for (let cssStyleSheet of doc.styleSheets) { let cssRuleList = cssStyleSheet.cssRules; pc.log(cssRuleList); } }

StyleSheetList一样,CSSRuleList也有length属性及item方法。因此,我们可使用同样的方法进行遍历。

let styleSrc = ` `; function doOnIframeLoad(doc) { for (let cssStyleSheet of doc.styleSheets) { let cssRuleList = cssStyleSheet.cssRules; for (let cssStyleRule of cssRuleList) { pc.log(cssStyleRule); } } }

通过遍历,我们得到了每个CSSStyleRule

CSSStyleRule

上面我们获取到了2CSSStyleRule,分别对应于:

body { color: #ccc; background-color: #222; }

#demo { margin: 1rem; }

selectorText及cssText属性

下面先看其基本属性。

let styleSrc = ` `; function doOnIterCSSStyleRule(cssStyleRule) { pc.log(cssStyleRule.selectorText); pc.log(cssStyleRule.cssText); }

selectorText即位于{...}之前的选择器的文本内容, 而cssText则是该选择器的所有完整的文本内容。

有趣的是,cssText将所有颜色值都转换为使用rgb()的格式来表示。

style属性

style属性返回一个类型为CSSStyleDeclaration的对象。

初窥CSSStyleDeclaration

let styleSrc = ` `; function doOnIterCSSStyleRule(cssStyleRule) { if (!navigator.userAgent.includes('Chrome')) { pc.log(cssStyleRule.style); } pc.log(cssStyleRule.style['backgroundColor']); }

Chrome会将一些只属于prototype的属性移到对象自身,导致打印对象时将列出过长的信息。故将Chrome过滤掉。下面代码当调用Object.entries()方法时也会出现同样的问题。

CSSStyleDeclaration是一个CSS属性的属性名-属性值对的封装器。它包含了所有的CSS的属性名及其对应的属性值(即便我们未声明使用特定的属性),及其一些辅助的方法。因此,如果展开界面中的CSSStyleDeclaration,则会看到很长很长的信息。

属性名使用驼峰标记法,故在CSS中所使用的background-color,其对应的属性名为backgroundColor。因此,可使用以下代码读取stylebackgroundColor属性值:

function doOnIterCSSStyleRule(cssStyleRule) { pc.group(cssStyleRule.selectorText); pc.log('background-color: "%s"', cssStyleRule.style['backgroundColor']); pc.groupEnd(); }

正如其名,CSSStyleDeclaration用于记录用户所声明的CSS样式。我们对body声明了background-color属性,故能读取其值;对iddemodiv未声明使用background-color属性,则该属性值为空字符串。

两种属性

CSSStyleDeclaration的属性名共有2种。第一种是使用整数作为属性名来存储用户声明的CSS属性名。从0开始,每声明一个属性,则该值加1stylelength属性记录了用户在特定选择器下所声明属性的数量。

第二种是以文本作为属性名来存储用户声明的CSS属性值。

例如,当我们为body声明了colorbackground-color值时,则CSSStyleDeclaration有以下属性:

0: "color" 1: "background-color" ... color: "rgb(204, 204, 204)" backgroundCoolor: "rgb(34, 34, 34)"

下面直接打印这些属性值:

function doOnIterCSSStyleRule(cssStyleRule) { let cssStyleDeclaration = cssStyleRule.style; if (cssStyleRule.selectorText === 'body') { pc.log(cssStyleDeclaration[0]); pc.log(cssStyleDeclaration[1]); pc.log(cssStyleDeclaration['color']); pc.log(cssStyleDeclaration['backgroundColor']); } }

找出用户声明的所有CSS属性

上面显式地读取background-color的值,那是因为我们事先已经知道body已声明了background-color属性值。如果没有事先知道,但又需列出用户所声明的所有属性名值呢?

第一步,找到所有以数字为键名的属性值,该属性值即用户所声明的CSS属性名;第二步,读取键名为上述值的属性值。

下面代码完成第一步的工作。

function doOnIterCSSStyleRule(cssStyleRule) { pc.group(cssStyleRule.selectorText); let cssStyleDeclaration = cssStyleRule.style; pc.log('Properties User Declared: %d', cssStyleDeclaration.length); for (let [key, propName] of Object.entries(cssStyleDeclaration)) { if (key.match(/^\d/)) { pc.log(`[%s]: %s`, key, propName); } } pc.log(Array.isArray(cssStyleDeclaration)); pc.groupEnd(); }

数字为键名的属性是CSSStyleDeclaration的自身属性,而以文本为键名的属性是CSSStyleDeclarationprototype链中的属性,因此,Object.entries方法应只返回以数字为键名的属性。但Chrome混淆了此点,将所有的属性都列入了自身属性,因此上面不得不使用额外的代码来判断属性名是否数字Safari中不需要此判断语句。

CSSmargin复合属性名称,被最终拆分为margin-top, margin-right, margin-bottom, margin-left等更具体的属性名称。

第二步,找到了用户声明的属性名称propName,则可进一步使用cssStyleDeclaration[propName]读取出其相应的属性值。下面分类列出用户所声明的所有CSS的属性名及属性值。

function doOnIterCSSStyleRule(cssStyleRule) { pc.group(cssStyleRule.selectorText); let cssStyleDeclaration = cssStyleRule.style; for (let [key, propName] of Object.entries(cssStyleDeclaration)) { if (key.match(/^\d/)) { pc.log(`%s: %s`, propName, cssStyleDeclaration[propName]); } } pc.groupEnd(); }

一个有趣的发现是,无论是使用CSS的属性名称background-color,还是使用驼峰标记法的名称backgroundColorCSSStyleDeclaration都能取出相应的属性值。

修改用户声明的CSS属性

可以直接修改CSSStyleDeclaration的各个属性值。

function doOnIterCSSStyleRule(cssStyleRule) { let cssStyleDeclaration = cssStyleRule.style; if (cssStyleRule.selectorText === 'body') { cssStyleDeclaration.backgroundColor = '#555'; cssStyleDeclaration.border = '10px solid gray'; for (let [key, propName] of Object.entries(cssStyleDeclaration)) { if (key.match(/^\d/)) { pc.log(`%s: %s`, propName, cssStyleDeclaration[propName]); } } } }

styleMap属性

上面的CSSStyleDeclaration只是一个纯粹的数据包装,我们得根据其具体的数据结构,自行编写读取相应属性值的代码。而CSSStylestyleMap属性值,返回一个StylePropertyMap的对象,提供了查询、添加、删除、清除属性的各个方法。

StylePropertyMap是一些主流浏览器自行实现的类,不是CSSOM规范中所定义的类。Firefox不支持此类。

let styleSrc = ` `; function doOnIterCSSStyleRule(cssStyleRule) { let stylePropertyMap = cssStyleRule.styleMap; logOnce(stylePropertyMap); pc.group(cssStyleRule.selectorText); pc.log(stylePropertyMap.getAll('color')); pc.log(stylePropertyMap.get('color')?.toString()); pc.log(stylePropertyMap.get('background-color')?.toString()); pc.log(stylePropertyMap.get('margin')?.toString()); pc.log(stylePropertyMap.get('padding')?.toString()); pc.groupEnd(); pc.log(navigator.userAgent.includes('Chrome')); }

HTML标签的style属性

HTML标签的style属性名,也返回一个CSSStyleDeclaration对象。但它与我们在style标签中所声明的CSSStyleDeclaration对象不是同一对象。

let srcdoc = `
A demo div
`; initIframe(srcdoc); let demoDiv; function doOnIframeLoad(doc) { demoDiv = doc.querySelector('#demo'); if (!navigator.userAgent.includes('Chrome')) { pc.log(demoDiv.style); } } function doOnIterCSSStyleRule(cssStyleRule) { if (cssStyleRule.selectorText === '#demo') { pc.log(`color value in inline style: "%s"`, demoDiv.style.color); pc.log(`color value in declared style: "%s"`, cssStyleRule.style.color); pc.log(demoDiv.style === cssStyleRule.style); } }

如果在HTML标签的style属性及在样式单style中设置了不同的值,则style属性值有更高的权重。

let srcdoc = `
A demo div
`; initIframe(srcdoc);

外联样式

使用link来引用的外联样式,也可通过documentstyleSheets属性来获取。

let srcdoc = `
A demo div

abc

`; initIframe(srcdoc); function doOnIframeLoad(doc) { let aa = doc.styleSheets; pc.log(aa); }

获取用户声明的所有CSS属性

现在拟使用2个外联样式单,1个外联样式单,有2个元素通过style属性来声明样式,有1个元素通过JavaScript来设置样式。下面将所有这些用户声明的CSS属性名与属性值分类打印出来。

let srcdoc = `
A demo div

Paragraph 1.

Paragraph 2.

`; initIframe(srcdoc); function doOnIframeLoad(doc) { let p1 = doc.querySelector('p:nth-of-type(1)'); p1.style.textIndent = '2em'; for (let cssStyleSheet of doc.styleSheets) { if (cssStyleSheet.href) { pc.group(cssStyleSheet.href.split('/').pop()); } else { pc.group('Inline stylesheet'); } for (let cssStyleRule of cssStyleSheet.cssRules) { pc.group(cssStyleRule.selectorText); printUserDeclaredValues(cssStyleRule.style); pc.groupEnd(); } pc.groupEnd(); } pc.group('Inline styles'); let tagsWithInlineStyle = doc.querySelectorAll('[style]'); for (let tag of tagsWithInlineStyle) { pc.group(tag.tagName.toLowerCase()); printUserDeclaredValues(tag.style); pc.groupEnd(); } pc.groupEnd(); } function printUserDeclaredValues(cssStyleDeclaration) { for (let [key, propName] of Object.entries(cssStyleDeclaration)) { if (key.match(/^\d/)) { pc.log(`%s: %s`, propName, cssStyleDeclaration[propName]); } } }

上面设置CSS样式的方式较多,共有4种。尽管如此,上面代码一个不漏地将它们全部提取并分类打印出来。

如果将上述代码包装为一个函数,则我们可以提取出任意网页中作者自己设置的所有CSS代码。

参考资源

  1. W3C: CSS Object Model (CSSOM)
  2. MDN: CSS Object Model (CSSOM)
  3. Manipulation of CSS Using JavaScript