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);
}
document 的styleSheets 返回一个类型为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 ,故StyleSheetList 的length 值为1 。
也可使用for ... of 语句进行遍历:
function doOnIframeLoad(doc) {
let styleSheetList = doc.styleSheets;
for (let cssStyleSheet of styleSheetList) {
pc.log(cssStyleSheet);
}
}
CSSStyleSheet
CSSStyleSheet ,相对于上面代码中的:
这一部分。
CSSStyleSheet 继承于StyleSheet ,有3 个关于样式规则的属性:ownerRule , cssRules 及rules 。
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 。
cssRules 与rules 是同一对象的不同引用,均返回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
上面我们获取到了2 个CSSStyleRule ,分别对应于:
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 。因此,可使用以下代码读取style 的backgroundColor 属性值:
function doOnIterCSSStyleRule(cssStyleRule) {
pc.group(cssStyleRule.selectorText);
pc.log('background-color: "%s"', cssStyleRule.style['backgroundColor']);
pc.groupEnd();
}
正如其名,CSSStyleDeclaration 用于记录用户所声明的CSS 样式。我们对body 声明了background-color 属性,故能读取其值;对id 为demo 的div 未声明使用background-color 属性,则该属性值为空字符串。
两种属性
CSSStyleDeclaration 的属性名共有2 种。第一种是使用整数 作为属性名来存储用户声明的CSS 属性名。从0 开始,每声明一个属性,则该值加1 。style 的length 属性记录了用户在特定选择器下所声明属性的数量。
第二种是以文本 作为属性名来存储用户声明的CSS 属性值。
例如,当我们为body 声明了color 及background-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 的自身属性,而以文本 为键名的属性是CSSStyleDeclaration 的prototype 链中的属性,因此,Object .entries 方法应只返回以数字 为键名的属性。但Chrome 混淆了此点,将所有的属性都列入了自身属性,因此上面不得不使用额外的代码来判断属性名是否数字 。Safari 中不需要此判断语句。
CSS 的margin 复合属性名称,被最终拆分为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 ,还是使用驼峰标记法的名称backgroundColor ,CSSStyleDeclaration 都能取出相应的属性值。
修改用户声明的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 只是一个纯粹的数据包装,我们得根据其具体的数据结构,自行编写读取相应属性值的代码。而CSSStyle 的styleMap 属性值,返回一个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 来引用的外联样式,也可通过document 的styleSheets 属性来获取。
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 代码。