DOM系列:动态添加CSS样式规则
在上一节中学习了如何通过JavaScript来修改CSS样式。简单地说:查询CSS样式(即计算样式),设置单个样式(设置的是行内样式),设置多个样式(通过类来设置样式)。即:
- 通过DOM Element对象的
getAttribute()
、setAttribute()
和removeAttribute()
等方法修改元素的style
属性 - 通过对元素节点的
style
来读写行内CSS样式 - 通过
style
对象的cssText
属性来修改全部的style
属性 - 通过
style
对象的setProperty()
、getPropertyValue()
、removeProperty()
等方法来读写行内CSS样式 - 通过
window.getComputedStyle()
方法获得浏览器最终计算的样式规则 - 通过
className
或classList
给元素添加或删除类名,配合样式文件来修改元素样式
可以说上面这些都是通过DOM元素来增、删、改、查CSS样式。事实上我们还可以通过脚本化CSS这种技术来控制样式。这种方式,可以让我们的页面更加的快速和高效。那就是直接通过JavaScript动态地添加和删除样式表中的某些样式,用来取代不断地查询DOM元素,并应用各种样式。接下来咱们就来学习脚本化样式表相关的知识。
获取样式表
你可以选择任意的样式表来添加样式规则。众所周知,引用CSS样式常见的方式主要有三种:
- 在元素行内添加样式
<style>
标签内部的样式<link>
标签引入的外部样式
在使用<style>
和<link>
添加样式时,可以在HTML标签中在对应的标签上添加ID
属性,然后直接通过这个DOM元素的sheet
属性就可以取得CSSStyleSheet
对象。样式表也可以通过document.styleSheets
遍历到:
let sheets = document.styleSheets;
CSSStyleSheet
类型表示的是样式表,其也是一个对象,而且是一个类数组对象,它继承自StyleSheet
。
StyleSheet接口
StyleSheet
接口代表网页的一张样式表,包括<link>
元素加载的样式表和<style>
元素内嵌的样式表。
document
对象的styleSheets
属性,可以返回当前页面的所有StyleSheet
实例 —— 所有样式表。它是一个类似数组的对象。
let sheets = document.styleSheets;
for (let i = 0; i < sheets.length; i++) {
console.log(sheets[i])
}
如果是<style>
元素嵌入的样式表,还有另一种获取StyleSheet
实例的方法,就是这个节点元素的sheet
属性。
// HTML 代码为 <style id="myStyle"></style>
var myStyleSheet = document.getElementById('myStyle').sheet;
myStyleSheet instanceof StyleSheet // true
前面也提到过了document
对象的styleSheets
属性,可以返回当前页面的所有StyleSheet
实例。StyleSheet
实例有以下属性。
StyleSheet.disabled
StyleSheet.disabled
返回一个布尔值,表示该样式表是否处于禁用状态。手动设置disabled
属性为true
,等同于在<link>
元素里面,将这张样式表设为alternate stylesheet
,即该样式表将不会生效。
<!DOCTYPE html>
<html lang="en">
<head>
<title>StyleSheet</title>
<style id="style">
#box {
background-color: red !important;
}
</style>
</head>
<body>
<div id="box" style="width: 100px; height: 100px;background-color: green;"></div>
<button id="btn">修改颜色</button>
<script>
let sheets = document.styleSheets
let btn = document.getElementById('btn')
btn.addEventListener('click', function(){
sheets[0].disabled = !sheets[0].disabled
})
</script>
</body>
</html>
当你单击“修改颜色”按钮时,可以看到id
为style
的<style>
中样式并没生效;只有再次点击时才将生效。
注意,
disabled
是一个可读,可写的的属性,该属性只能通过JavaScript设置,不能在HTML语句中设置。
StyleSheet.href
StyleSheet.href
返回样式表的网址。对于内嵌样式表,该属性返回null
。该属性是一个只读属性。
let sheets = document.styleSheets;
for (let i = 0; i < sheets.length; i++) {
console.log(sheets[i].href)
}
StyleSheet.media
StyleSheet.media
属性返回一个类似数组的对象:MediaList
实例,表示适用媒介的字符串。表示当前样式表是用于屏幕screen
,还是打印设备print
或手持设备handheld
,或各种媒介适用all
。该属性只读,默认值是screen
。
当你想在屏幕上显示的时候,你肯定不能把CSS规则加到打印样式表中。你可以仔细的看一下CSSStyleSheet对象的属性信息:
比如下面这样,咱们可以打印出一个页面中所有样式表对应的媒介:
let sheets = document.styleSheets;
for (let i = 0; i < sheets.length; i++) {
console.log(sheets[i].media)
}
MediaList
实例的appendMedium
方法,用于增加媒介;deleteMedium
方法用于删除媒介。
sheets[0].media.appendMedium = 'all'
sheets[1].media.appendMedium = 'screen'
sheets[2].media.appendMedium = 'print'
for (let i = 0; i < sheets.length; i++) {
console.log(sheets[i].media)
}
sheets[2].media.deleteMedium = 'print'
for (let i = 0; i < sheets.length; i++) {
console.log(sheets[i].media)
}
StyleSheet.title
StyleSheet.title
属性返回样式表的title
属性。
StyleSheet.type
StyleSheet.type
属性返回样式表的type
属性,通常是text/css
。
StyleSheet.parentStyleSheet
CSS 的@import
命令允许在样式表中加载其他样式表。StyleSheet.parentStyleSheet
属性返回包含了当前样式表的那张样式表。如果当前样式表是顶层样式表,则该属性返回null
。
StyleSheet.ownerNode
StyleSheet.ownerNode
属性返回StyleSheet
对象所在的 DOM 节点,通常是<link>
或<style>
。对于那些由其他样式表引用的样式表,该属性为null
。
for (let i = 0; i < sheets.length; i++) {
console.log(sheets[i].ownerNode)
}
StyleSheet.cssRules
StyleSheet.cssRules
属性指向一个类似数组的对象(CSSRuleList
实例),里面每一个成员就是当前样式表的一条 CSS 规则。使用该规则的cssText
属性,可以得到 CSS 规则对应的字符串。
for (let i = 0; i < sheets.length; i++) {
console.log(sheets[0].cssRules[i].cssText)
}
每条 CSS 规则还有一个style
属性,指向一个对象,用来读写具体的 CSS 命令。
for (let i = 0; i < sheets.length; i++) {
console.log(sheets[0].cssRules[i].style)
}
StyleSheet.ownerRule
有些样式表是通过@import
规则输入的,它的ownerRule
属性会返回一个CSSRule
实例,代表那行@import
规则。如果当前样式表不是通过@import
引入的,ownerRule
属性返回null
。
CSSStyleSheet实例方法
CSSStyleSheet
实例方法有insertRule()
和deleteRule()
。
CSSStyleSheet.insertRule
方法用于在当前样式表的插入一个新的 CSS 规则。
<style id="style">
#box {
background-color: green;
width: 100px;
height: 100px;
}
</style>
<script>
let sheet = document.getElementById('style').sheet
sheet.insertRule('#box{border: 2px solid orange}', 0)
sheet.insertRule('p {color: black}', 1)
</script>
在浏览器开发者工具中,我们可以看到添加了新的<style>
:
CSSStyleSheet.insertRule
方法可以接受两个参数,第一个参数是表示CSS规则的字符串,这里只能有一条规则,否则会报错;第二个参数是该规则在样式表的插入位置(从0
开始),该参数可选,默认为0
(即默认插入在样式表的头部)。注意,如查插入位置大于现在规则的数目,会报错。该方法的返回值是新插入规则的位置序号。
注意,浏览器对脚本在样式表里面插入规则有很多限制。所以,这个方法最好放在
try ... catch
里使用。
因为并不是所有的浏览器都支持CSSStyleSheet.insertRule
方法,所以最好创建一个函数来处理规则的插入。比如下面这样的一个函数:
function addCSSRule(sheet, selector, rules, index) {
if ('inserRule' in shhet) {
sheet.insertRule(selector + '{' + rules + '}', index)
} else if ('addRule' in sheet) {
sheet.addRule(selector, rules, index)
}
}
// 使用方式
addCSSRule(document.styleSheets[0], 'header', 'float: left')
CSSStyleSheet.deleteRule
方法和CSSStyleSheet.insertRule
方法刚好相反,主要用来在样式表里移除一条规则,它的参数是该条规则在cssRules
对象中的位置。该方法没有返回值。
document.styleSheets[0].deleteRule(1);
CSSRuleList 接口
CSSRuleList
接口是一个类似数组的对象,表示一组CSS规则,成员都是CSSRule
实例。我们一般可以通过StyleSheet.cssRules
属性来获取CSSRuleList
实例。
let sheet = document.getElementById('style').sheet
let crl = sheet.cssRules
crl instanceof CSSRuleList // => true
console.log(crl)
CSSRuleList
实例里面,每一条规则(CSSRule
实例)可以通过rules.item(index)
或者rules[index]
拿到。CSS规则的条数通过rules.length
拿到:
<style id="style">
#box {
background-color: green;
width: 100px;
height: 100px;
}
p {
color: red;
}
</style>
<script>
let sheet = document.getElementById('style').sheet
let crl = sheet.cssRules
console.log(crl.length)
for (let i = 0; i < crl.length; i++) {
console.log(crl[i])
console.log(crl[i] instanceof CSSRule)
}
</script>
注意,添加规则和删除规则不能在
CSSRuleList
实例操作,而要在它的父元素StyleSheet
实例上,通过StyleSheet.insertRule()
和StyleSheet.deleteRule()
操作。
虽然CSSRule
对象表示样式表中的每一条规则,但实际上,CSSRule
是一个供其他多种类型继承的基类型,其中最常见的就是CSSStyleRule
类型,表示样式信息。其他规则还包括@import
、@font-face
、@page
和@charset
。
CSSRule
一条CSS规则包括两个部分:CSS选择器和样式声明:
JavaScript通过CSSRule
接口操作CSS规则。一般通过CSSRuleList
接口(StyleSheet.cssRules
)获取CSSRule
实例。
CSSRule
实例有自己的属性:
CSSRule.cssText
CSSRule.cssText
属性返回当前规则的文本,比如:
<style id="style">
#box {
background-color: green;
width: 100px;
height: 100px;
}
p {
color: red;
}
</style>
<script>
let sheet = document.getElementById('style').sheet
let ruleList = sheet.cssRules
for (let i = 0; i < ruleList.length; i++) {
console.log(ruleList[i].cssText)
}
</script>
如果规则是加载(@import
)其他样式表,cssText
属性返回@import 'url'
。比如基于上例稍做修改:
<style id="style">
@import url('https://www.fedev.cn/css/reset.css')
#box {
background-color: green;
width: 100px;
height: 100px;
}
p {
color: red;
}
</style>
CSSRule.parentStyleSheet
CSSRule.parentStyleSheet
属性返回当前规则所在的样式表对象(StyleSheet
实例),还是基于上面的示例做一点小修改:
let sheet = document.getElementById('style').sheet
let ruleList = sheet.cssRules
let rule = ruleList[0]
console.log(rule.parentStyleSheet === sheet) // => true
CSSRule.parentRule
CSSRule.parentRule
属性返回包含当前规则的父规则,如果不存在父规则(即当前规则是顶层规则),则返回null
。
let sheet = document.getElementById('style').sheet
let ruleList = sheet.cssRules
for (let i = 0; i < ruleList.length; i++) {
console.log(ruleList[i].parentRule)
}
父规则最常见的情况是,当前规则包含在@media
规则代码块之中。比如下面这个示例:
<style id="style">
@supports (display: flex) {
@media screen and (min-width: 900px) {
#box {
display: flex;
}
}
}
</style>
let sheet = document.getElementById('style').sheet
let ruleList = sheet.cssRules
for (let i = 0; i < ruleList.length; i++) {
console.log(ruleList[i].parentRule)
console.log(ruleList[i].cssText)
console.log(ruleList[i].cssRules[i].cssText)
}
CSSRule.type
CSSRule.type
属性返回一个整数值,表示当前规则的类型。
最常见的类型有以下几种。
- 普通样式规则(
CSSStyleRule
实例) @import
规则@media
规则(CSSMediaRule
实例)@font-face
规则
CSSStyleRule
如果一条 CSS 规则是普通的样式规则(不含特殊的 CSS 命令),那么除了 CSSRule
接口,它还部署了 CSSStyleRule
接口。
CSSStyleRule 接口有以下两个属性。
CSSStyleRule.selectorText
CSSStyleRule.selectorText
属性返回当前规则的选择器。
<style id="style">
#box {
width: 100px;
}
</style>
let sheet = document.getElementById('style').sheet
let ruleList = sheet.cssRules
for (let i = 0; i < ruleList.length; i++) {
console.log(ruleList[i].selectorText)
}
注意,这个属性是可写的。
CSSStyleRule.style
CSSStyleRule.style
属性返回一个对象(CSSStyleDeclaration
实例),代表当前规则的样式声明,也就是选择器后面的大括号里面的部分。
CSSStyleDeclaration
实例的cssText
属性,可以返回所有样式声明,格式为字符串。
添加一个新的样式表
在Web页面中添加样式表有两种方式。一种是通过在HTML文档内部添加<style>
元素,即内置样式。
我们可以通过下面两种方式来动态添加新样式表。先来看方法一:
let sheet = document.createElement('style')
sheet.setAttribute('media', 'screen')
sheet.innerHTML = 'body{color:red}'
document.head.appendChild(sheet)
另外一种方式就是将其封装成一个函数:
let sheet = (function () {
let style = document.createElement('style')
document.head.appendChild(style)
return style
})()
另一种是添加外部样式表,即在文档中添加一个<link>
节点,然后将href
属性指向外部样式表的 URL
。
let linkElm = document.createElement('link');
linkElm.setAttribute('rel', 'stylesheet');
linkElm.setAttribute('type', 'text/css');
linkElm.setAttribute('href', 'reset-min.css');
document.head.appendChild(linkElm);
总结
动态添加规则到样式表是高效的手段,可能比你想象的还要简单。请记住这种方案,可能在你的下一个大应用中需要使用,因为它能在代码和元素处理这两方面避免你掉进坑里。借着如何动态创建样式表的机会,文章对有关于动态创建样式表涉及到的相关API做了一些简单的描述。
由于自己是JavaScript的初学者,如果文章中有描述不对之处,还请各路大婶拍正。欢迎各位爷打个赏,鼓励我继续创作一些优质的教程。(^_^)
扩展阅读
如需转载,烦请注明出处:https://www.fedev.cn/javascript/add-rules-stylesheets.htmlThe 13th Version UA Yeezy Boost 350 Turtle Dove, the perfect version