第一部分:接口介绍
首先说在HTML中定义样式的方式有 3 种:
- 通过
link元素包含外部样式表文件 - 使用
style元素定义嵌入式样式 - 使用
style特性定义针对特定元素的样式
DOM2级模块围绕这三种应用样式机制提供了一套API。理解了这套API就理解了如何用JS操作CSS了。这套接口种类很多。如下图:

接口都是有规律的,死记硬背不好记住,根据HTML中定义样式的3种方式来看接口的定义,自然就更好理解。
1.1 CSSStyleSheet样式表对象
名字中带有StyleSheet的接口就样式表的意思。CSSStyleSheet接口类型表示的是样式表。包括通过link元素包含的外部样式表和在<style type="text/css"></style>元素中定义的样式表。CSSStyleSheet接口继承StyleSheet。StyleSheet可以作为一个基础接口来定义非 CSS 样式表。
1.2 CSSRule样式表规则对象
名字中带有Rule的接口就代表规则的意思。CSSRule对象表示样式表中的每一条规则。CSSRule其实 是一个供其他多种类型继承的基类型。其中最常用的就是 CSSStyleRule 类型,表示样式信息。
CSSRule的所有子类型如下:
CSSCharsetRuleCSSConditionRuleCSSCounterStyleRuleCSSFontFeatureValuesRuleCSSGroupingRuleCSSImportRuleCSSKeyframeRuleCSSKeyframesRuleCSSMarginRuleCSSMediaRuleCSSNamespaceRuleCSSPageRuleCSSSupportsRuleCSSViewportRuleCSSFontFaceRuleCSSStyleRule
这些规则很少有必要通过脚本来访问,而最常用的就是
CSSStyleRule类型。
通过下图可以加深对StyleSheet,CSSRule关系的理解。

1.3 CSSStyleDeclaration 应用在元素style属性的对象
任何支持style特性的HTML元素在JavaScript中都有一个对应的style属性。这个style对象是 CSSStyleDeclaration的实例。包含着通过HTML的style特性指定的所有样式信息,但不包含 与外部样式表或嵌入样式表经层叠而来的样式。
我们经常使用的通过HTMLElement.style属性就是返回来CSSStyleDeclaration对象。CSSStyleDeclaration对象表示一个元素的style属性。
第二部分:CSSStyleSheet类型接口介绍
2.1 StyleSheet 类型支持的属性和方法:
CSSStyleSheet 继承自StyleSheet,StyleSheet作为一个基础接口来定义非 CSS 样式表。从 ,StyleSheet接口继承而来的属性如下:
disabled:表示样式表是否被禁用的布尔值。这个属性是可读/写的,将这个值设置为 true 可 以禁用样式表。href:如果样式表是通过包含的,则是样式表的 URL;否则,是 null。media:ownerNode:指向拥有当前样式表的节点的指针,样式表可能是在 HTML 中通过link(HTMLLinkElement)或style(HTMLStyleElement)引入的。如果当前样式表是其他样式表通过@import 导入的,则这个属性值为 null。IE 不支持这个属性。parentStyleSheet:在当前样式表是通过@import导入的情况下,这个属性是一个指向导入它的样式表的指针,否则为null。title: ownerNode 中 title 属性的值,否则为null。type:表示样式表类型的字符串。对 CSS 样式表而言,这个字符串是"type/css"。
CSSStyleSheet对象则是一 套只读的接口。除了disabled属性之外。
2.2 CSSStyleSheet 接口类型支持的属性和方法:
CSSStyleSheet除了继承StyleSheet接口的属性之外,而且还支持下列属性和方法:
cssRules(rules):样式表中包含的样式规则的集合。IE 不支持这个属性,但有一个类似的rules属性。ownerRule:如果样式表是通过@import导入的,这个属性就是一个指针,指向表示导入的规 则;否则,值为 null。IE 不支持这个属性。deleteRule(index):删除cssRules集合中指定位置的规则。IE 不支持这个方法,但支持 一个类似的removeRule()方法。insertRule(rule,index):向cssRules集合中指定的位置插入rule字符串。IE 不支持这 个方法,但支持一个类似的addRule()方法。replace():构建一个样式表,不允许外部引用。replaceSync():构建一个样式表,允许外部引用。
2.3 使用JavaScript访问样式表
访问样式表方式一
应用于文档的所有样式表是通过document.styleSheets集合来表示的。通过这个集合的 length属性可以获知文档中样式表的数量,而通过方括号语法或 item()方法可以访问每一个样式表。
let sheet = null;
for(let i = 0, len = document.styleSheets.length;i < len; i++) {
sheet = document.styleSheets[i];
console.log(sheet.href)
}
以上代码可以输出文档中使用的每一个样式表的 href 属性(style元素包含的样式表没有 href 属性)。
访问样式表方式二
还可以通过link或style元素取得CSSStyleSheet对象。DOM规定了一个包含CSSStyleSheet 对象的属性,名叫sheet。除了 IE,其他浏览器都支持这个属性。IE 支持的是styleSheet属性。
在不同浏览器中都能取得样式表对象:
function getStyleSheet(element){
return element.sheet || element.styleSheet;
}
//取得第一个<link/>元素引入的样式表,如果没有则返回空的HTMLCollection集合
const link = document.getElementsByTagName("link")[0];
if(typeof link === 'object' && link.rel === 'stylesheet') {
const sheet = getStyleSheet(link);
}
如果
link标签不是引入的css样式,则sheet返回null。
第三部分:CSSRule规则类型接口介绍
CSSRule对象表示样式表中的每一条规则。实际上,CSSRule是一个供其他多种类型继承的基类 型,其中最常见的就是CSSStyleRule类型,表示样式信息(其他规则还有@import、@font-face、 @page和@charset,但这些规则很少有必要通过脚本来访问)。CSSStyleRule 对象包含下列属性。
cssText:返回整条规则对应的文本。由于浏览器对样式表的内部处理方式不同,返回的文本 可能会与样式表中实际的文本不一样;Safari始终都会将文本转换成全部小写。IE 不支持这个 属性。parentRule:如果当前规则是导入的规则,这个属性引用的就是导入规则;否则,这个值为 null。IE 不支持这个属性。parentStyleSheet:当前规则所属的样式表。IE 不支持这个属性。selectorText:返回当前规则的选择符文本。style:一个CSSStyleDeclaration对象,可以通过它设置和取得规则中特定的样式值。type:表示规则类型的常量值。对于样式规则,这个值是 1。IE 不支持这个属性。styleMap:一个StylePropertyMap对象。StylePropertyMap对象提供了CSS声明块的表示,该声明块可以替代CSSStyleDeclaration。
CSSStyleRule对象的cssText属性与style.cssText属性类似,但并不相同。前者包含选择符文本和围绕样式信息的花括号,后者只包含样式信息(类似于 元素的 style.cssText)。此外,cssText 是只读的,而style.cssText也可以被重写。
下面是获取各个属性显示的结果:
<style type="text/css">
.demo {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
<script>
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules;
var rule = rules[0];
console.log(rule.selectorText); //.demo
console.log(rule.style.backgroundColor); //blue
console.log(rule.style.width); //100px
console.log(rule.style.height); //200px
//.demo { background-color: blue; width: 100px; height: 200px; }
console.log(rule.cssText);
//background-color: blue; width: 100px; height: 200px;
console.log(rule.style.cssText);
</script>
使用rule.style这种方式,可以像确定元素的行内样式信息一样,确定与规则相关的样式信息。与使用元素的方式一样,在这种方式下也可以修改样式信息,如下面的例子:
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules;
var rule = rules[0];
rule.style.backgroundColor = "red";
以上面这种方式修改规则会影响页面中适用于该规则的所有元素。换句话说,如果有两个带有demo类的div元素,那么这两个元素都会应用修改后的样式。
3.1 创建规则和删除规则(CSSStyleSheet接口的方法)
创建规则
DOM 规定,要向现有样式表中添加新规则,需要使用insertRule()方法。这个方法接受两个参 数:规则文本和表示在哪里插入规则的索引。
var sheet = document.styleSheets[0];
sheet.insertRule("body { background-color: silver }", 0); //IE不支持
IE8 及更早版本支持一个类似的方法,名叫 addRule()
sheet.addRule("body", "background-color: silver", 0); //仅对 IE 有效
以跨浏览器的方式向样式表中插入规则:
//insertRule(document.styleSheets[0], "body", "background-color: silver", 0);
function insertRule(sheet, selectorText, cssText, position){
if (sheet.insertRule){
sheet.insertRule(selectorText + "{" + cssText + "}", position);
} else if (sheet.addRule){
sheet.addRule(selectorText, cssText, position);
}
}
上面这个例子插入的规则会改变元素的背景颜色。插入的规则将成为样式表中的第一条规则(插入到了 位置 0)——规则的次序在确定层叠之后应用到文档的规则时至关重要。
删除规则
从样式表中删除规则的方法是 deleteRule(),这个方法接受一个参数:要删除的规则的位置。例 如,要删除样式表中的第一条规则。
sheet.deleteRule(0);
IE 支持的类似方法叫 removeRule():
sheet.removeRule(0); //仅对 IE 有效
以跨浏览器的方式向样式表中删除规则:
//deleteRule(document.styleSheets[0], 0);
function deleteRule(sheet, index){
if (sheet.deleteRule){
sheet.deleteRule(index);
} else if (sheet.removeRule){
sheet.removeRule(index);
}
}
添加规则和删除规则在实际
Web开发中并不常用,慎重使用。
第四部分:CSSStyleDeclaration 直接访问元素的样式
任何支持 style 特性的 HTML 元素在 JavaScript 中都有一个对应的 style 属性。就是我们平常在HTML元素里写的样式:
<div style="font-size:20px;color:red;">容器</div>
上面这个 style 对象 是 CSSStyleDeclaration 的实例。包含着通过 HTML 的 style 特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。
对于使用短划线(分隔不同的词汇,例如 background-image)的 CSS 属性 名,必须将其转换成驼峰大小写形式。下面是几个例子:
| CSS属性 | JavaScript属性 |
|---|---|
background-image |
style.backgroundImage |
color |
style.color |
font-family |
style.fontFamily |
var myDiv = document.getElementById("myDiv");
myDiv.style.backgroundColor = "red";
myDiv.style.width = "100px";
myDiv.style.border = "1px solid black";
其中一个不能直接转换的 CSS属性 就是 float。由于 float 是 JavaScript 中的保留字,因此不能用作属性名。DOM2 级样式”规范规定 样式对象上相应的属性名应该是 cssFloat。而 IE 支持的则是styleFloat。可以通过下面的方式来判断当前浏览器所支持的float:
const support = (function(){
const div = document.createElement("div");
div.style.cssText = "float:left;";
let support = {
cssFloat: !!div.style.cssFloat
}
return support;
})()
const floatReal = support.cssFloat ? 'cssFloat' : 'styleFloat';
还可以直接通过document.documentMode来判断:
const floatReal = Number(document.documentMode) < 9 ? 'styleFloat' : 'cssFloat'
只要取得一个有效的 DOM 元素的引用,就可以随时使用 JavaScript 为其设置样式:
var myDiv = document.getElementById("myDiv");
myDiv.style.width = "100px";
myDiv.style.border = "1px solid black";
所有度量值都必须指定一个度量单位。下面是一个设置元素style属性的例子:
function setStyle(element, styles) {
function is_numeric(n) {
return (n !== '' && !isNaN(parseFloat(n)) && isFinite(n));
}
Object.keys(styles).forEach(function(prop) {
var unit = '';
if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && is_numeric(styles[prop])) {
unit = 'px';
}
element.style[prop] = styles[prop] + unit;
});
}
setStyle(document.getElementById("myDiv"),{ position: 'absolute', top: 0 })
Element.style返回的只是行内样式,并不是该元素的全部样式。通过样式表设置的样式,或者从父元素继承的样式,无法通过这个属性得到。元素的全部样式要通过window.getComputedStyle()得到。
4.1 CSSStyleDeclaration 对象的属性和方法
cssText:通过它能够访问到 style 特性中的 CSS 代码,可读写。length:应用给元素的CSS属性的数量。parentRule:表示 CSS 信息的 CSSRule 对象。getPropertyPriority(propertyName)::如果给定的属性使用了!important 设置,则返回 "important";否则,返回空字符串。getPropertyValue(propertyName)::返回给定属性的字符串值。item(index):返回给定位置的 CSS 属性的名称。removeProperty(propertyName):从样式中删除给定属性。setProperty(propertyName,value,priority):将给定属性设置为相应的值,并加上优先 权标志("important"或者一个空字符串)。
cssText详解
通过cssText属性可以访问 style 特性中的 CSS 代码。在读取模式下,cssText 返回浏览器对 style特性中 CSS 代码的内部表示。在写入模式下,赋给 cssText 的值会重写整个 style 特性的值;也就是说,以前通过 style 特性指定的样式信息都将丢失。例如,如果通过 style 特性为元素设置了边框,然后再以不包含边框的规则重写 cssText,那么就会抹去元素上的边框。
<div id="demo" style="border:5px solid red;">容器</div>
var myDiv = document.getElementById("myDiv");
myDiv.style.cssText = "background-color: green";
console.log(myDiv.style.cssText) //background-color: green;
设置
cssText是为元素应用多项变化最快捷的方式,因为可以一次性地应用所有变化。
length属性,item()方法, getPropertyValue()方法,removeProperty()方法
设计 length 属性的目的,就是将其与 item()方法配套使用,以便迭代在元素中定义的 CSS 属性。 在使用 length 和 item()时,style 对象实际上就相当于一个集合:
<div id="demo" style="width:100px;font-size:22px;">容器</div>
const myDiv = document.getElementById('demo');
for(let i = 0; i < myDiv.style.length; i++) {
//或者使用myDiv.style.item(i)
console.log(myDiv.style[i]) //width font-size
}
使用方括号语法或item()方法,都可以取得 CSS 属性名("background-color", 不是"backgroundColor")。然后,就可以在 getPropertyValue()中使用取得的属性名进一步取得 属性的值。
<div id="demo" style="width:100px;font-size:22px;">容器</div>
const myDiv = document.getElementById('demo');
var prop, value, i, len;
for (i=0, len= myDiv.style.length; i < len; i++){
prop = myDiv.style[i];
value = myDiv.style.getPropertyValue(prop);
console.log(prop + ':' + value); //width:100px font-size:22px
}
要从元素的样式中移除某个 CSS 属性,需要使用 removeProperty()方法。使用这个方法移除一 个属性,意味着将会为该属性应用默认的样式(从其他样式表经层叠而来)。例如,要移除通过 style 特性设置的 font-size 属性:
<div id="demo" style="width:100px;font-size:22px;">容器</div>
const myDiv = document.getElementById('demo');
myDiv.style.removeProperty("font-size");
只要移除相应的属性,就可以为元素应用默认值。
4.2 getComputedStyle() 获得计算的样式
虽然 style 对象能够提供支持 style 特性的任何元素的样式信息,但它不包含那些从其他样式表 层叠而来并影响到当前元素的样式信息。
“DOM2 级样式”增强了 document.defaultView,提供了 getComputedStyle()方法。这个方法接受两个参数:要取得计算样式的元素和一个伪元素字符串(例 如":after")。如果不需要伪元素信息,第二个参数可以是 null。getComputedStyle()方法返回一个 实时的CSSStyleDeclaration对象,当元素的样式更改时,它会自动更新本身。其中包含当前元素的所有计算的样式。
其实
window.getComputedStyle()也可以获得元素的CSSStyleDeclaration对象。document.defaultView.getComputedStyle()和window.getComputedStyle()区别,请参考: MDN这篇文章。
下面代码通过 window.getComputedStyle()获取元素样式:
<style type="text/css">
.demo {
background-color: blue;
width: 100px;
height: 200px;
}
</style>
<div id="demo" style="background-color: red; border: 5px solid black;">容器</div>
<script>
const myDemo = document.getElementById('demo');
const computedStyle = window.getComputedStyle(myDemo,null);
console.log(computedStyle.backgroundColor); //rgb(255, 0, 0)
console.log(computedStyle.width); //100px
console.log(computedStyle.height); //200px
console.log(computedStyle.border); //5px solid rgb(0, 0, 0)
</script>
上面打印出的背景颜色不是"blue",因为这个样式在自身的 style 特性中已经被覆盖了。
边框属性可能 会也可能不会返回样式表中实际的 border 规则。因为设置这种属性(综合属性)实际上会涉及 很多其他属性。在设置 border 时,实际上是设置了四个边的边框宽度、颜色、样式属性
( border-left-width 、 border-top-color 、 border-bottom-style等等)。 因 此,即使 computedStyle.border不会在所有浏览器中都返回值,但具体到computedStyle.borderLeftWidth会 返回值。
IE 不支持getComputedStyle()方法,但它有一种类似的概念。在 IE 中,每个具有style属性 的元素还有一个currentStyle属性。这个属性是CSSStyleDeclaration的实例。下面是一个兼容模式的例子:
function getStyles(elem) {
window.getComputedStyle ? window.getComputedStyle(elem,null) : elem.currentStyle;
}
var myDemo = document.getElementById('demo');
var computedStyle = getStyles(myDemo)
所有计算的样式都是只读的。不能修改计算后样式对象中的 CSS 属性。
第五部分:实例
5.1 设置style样式
const ieVersion = Number(document.documentMode);
/**
* 将:-_等变成驼峰式,如foo-bar变成fooBar
* @param name 要处理的字符串
* @returns {*} 处理后的字符串
*/
const camelCase = function(name) {
return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
// 开头的不大写,其余的大写
return offset ? letter.toUpperCase() : letter;
}).replace(/^moz([A-Z])/, 'Moz$1'); // 对moz进行特殊处理
};
/**
* 设置元素的样式
* @param element 要设置的元素
* @param styleName 要设置的样式
* @param value 要设置的值
*/
function setStyle(element, styleName, value) {
if (!element || !styleName) return;
// 如果是对象则拆分后依次设置
if (typeof styleName === 'object') {
for (var prop in styleName) {
if (styleName.hasOwnProperty(prop)) {
setStyle(element, prop, styleName[prop]);
}
}
} else {
styleName = camelCase(styleName);
// opacity特殊处理
if (styleName === 'opacity' && ieVersion < 9) {
element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')';
} else {
element.style[styleName] = value;
}
}
};
5.2 获得style样式
var ieVersion = Number(document.documentMode);
/**
* 将:-_等变成驼峰式,如foo-bar变成fooBar
* @param name 要处理的字符串
* @returns {*} 处理后的字符串
*/
const camelCase = function(name) {
return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
// 开头的不大写,其余的大写
return offset ? letter.toUpperCase() : letter;
}).replace(/^moz([A-Z])/, 'Moz$1'); // 对moz进行特殊处理
};
/**
* 获取样式,分IE9以下和其他两种方式处理
* @type {Function}
* @param element 要获取样式的元素
* @param styleName 要获取的样式名
*/
var getStyle = ieVersion < 9 ? function(element, styleName) {
if (!element || !styleName) return null;
// 将样式名转成驼峰式
styleName = camelCase(styleName);
// float特殊处理
if (styleName === 'float') {
styleName = 'styleFloat';
}
try {
// opacity特殊处理
switch (styleName) {
case 'opacity':
try {
return element.filters.item('alpha').opacity / 100;
} catch (e) {
return 1.0;
}
default:
return (element.style[styleName] || element.currentStyle ? element.currentStyle[styleName] : null);
}
} catch (e) {
return element.style[styleName];
}
} : function(element, styleName) {
if (!element || !styleName) return null;
styleName = camelCase(styleName);
if (styleName === 'float') {
styleName = 'cssFloat';
}
try {
var computed = document.defaultView.getComputedStyle(element, '');
return element.style[styleName] || computed ? computed[styleName] : null;
} catch (e) {
return element.style[styleName];
}
};
5.3 增加和删除class样式
/**
* 判断是否包含某类
* @param el 要检测的元素
* @param cls 要检测的类名
* @returns {boolean}
*/
function hasClass(el, cls) {
if (!el || !cls) return false;
if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
if (el.classList) {
return el.classList.contains(cls);
} else {
return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
}
};
/**
* 给元素添加某些类
* @param el 要处理的元素
* @param cls 要添加的类
*/
function addClass(el, cls) {
if (!el) return;
var curClass = el.className;
var classes = (cls || '').split(' ');
for (var i = 0, j = classes.length; i < j; i++) {
var clsName = classes[i];
if (!clsName) continue;
if (el.classList) {
el.classList.add(clsName);
} else if (!hasClass(el, clsName)) {
curClass += ' ' + clsName;
}
}
if (!el.classList) {
el.className = curClass;
}
};
/**
* 给元素移除某些类
* @param el 要处理的元素
* @param cls 要移除的类
*/
function removeClass(el, cls) {
if (!el || !cls) return;
var classes = cls.split(' ');
var curClass = ' ' + el.className + ' ';
for (var i = 0, j = classes.length; i < j; i++) {
var clsName = classes[i];
if (!clsName) continue;
if (el.classList) {
el.classList.remove(clsName);
} else if (hasClass(el, clsName)) {
curClass = curClass.replace(' ' + clsName + ' ', ' ');
}
}
if (!el.classList) {
el.className = trim(curClass);
}
};