DOM系列:样式和类
对于任何一位Web开发者而言,处理CSS样式很多时候还是会借助JavaScript。简单的说,我们会碰到一些交互(或UI效果的变化)都会通过JavaScript来处理style
或类。那么今天我们将要学习的是如何通过JavaScript来控制样式和类名,在深入讨论JavaScript处理样式和类的方法之前,我们需要知道在Web页面中元素的样式处理通常有两种方式。
- 在HTML添加类名,然后在CSS样式文件中处理样式
- 通过JavaScript控制HTML元素的
style
属性,改变元素样式
虽然上面提到的方法都可以处理页面元素的样式,但CSS始终是最佳选择 —— 不仅对HTML如此,在JavaScript中也是如此。因为我们一直遵守的是:分离。
当然很多时候,只使用CSS的类方式并不一定能达到我们要的结果(也就是说类再也无法处理时),那么我们就必须使用JavaScript操作HTML元素中的style
属性。 比如,如果我们要动态计算元素的坐标,并想通过JavaScript来设置,那么我们就可以像下面这样做:
elem.style.left = left
elem.style.top = top
这只是其中的一种情景,事实上还有很多其他的情景,比如将文本的颜色变成红色,比如添加一个背景图片。其实,通过JavaScript来控制CSS类名,更灵活,更容易支持。
至于为什么要这么做,或者要怎么做,接下来的内容就是围绕着这些方面来阐述,感兴趣的,欢迎继续往下阅读。
为什么要使用JavaScript设置样式
文章开头我们提到了可以通过JavaScript来控制Web的样式。那么为什么要使用JavaScript设置元素样式?在深入讨论之前,我们有必要知识为什么要使用JavaScript来设置元素样式。在常见的情况下,咱们可以通过样式文件(就是.css
文件或style
标签)或内联样式(元素中调用style
属性)来控制Web元素的外观。这也可能是大家更多时候想要的。
但在很多时候,是希望内容更具交互性,打个比方说,你希望根据用户输入的状态(比如鼠标单击或者悬浮)引入别的样式,这些样式代码在后台运行。虽然像hover
这样的伪选择器提供了一些支持,但是功能还是有很多限制。
言外之意,很多更具交互性(或者说更多的富交互性)的解决方案是需要强度依赖JavaScript。JavaScript不仅可以让你在与之交互的元素上进行样式变化,更重要的是,它允许你在整个页面中使用样式。可以说这种自由性是非常强大的,而且远远超出了CSS对内容样式能力的限制。
两种处理样式的方式
正如文章开头提到的,如果要控制元素的样式变化,有两种方法可能使用JavaScript来修改。一种方法是直接在元素上设置CSS属性(其实就是通过元素内联的style
属性来实现)。另一种方法是从元素中添加或删除类名,这可能导致Web运用或Web程序忽略某些样式规则。接下来我们会围绕着这两种方式进行深入的学习。
直接设置style
在学习如何直接修改元素的style
(或者如标题写的一样,直接通过设置style
)属性来改变元素的样式之前,需要先花点时间来了解CSSStyleDeclaration
接口。
CSSStyleDeclaration
接口可以用来操作元素的样式。常见的方式(接口)有:
- 元素节点的
style
属性,即HTMLElement.style
,也就是前面介绍的方式 CSSStyle
实例的style
属性,即HTMLElement.style.cssText
window.getComputedStyle()
HTMLElement.style
通过JavaScript访问的每个HTML元素都有一个style
对象。该对象允许您指定CSS属性并设置其值。比如你在浏览器控制台中输入$0.style
就可以看到类似下图这样的一个截图:
从上图可以看出来,style
对象下有的CSS属性。言外之意,上图中的CSS属性(还有很多没截到,可以点击这里查阅读所有的属性)可以通过elem.style.property = 'value'
的方式来设置对应属性的值。例如,咱们想对$0
设置背景颜色:
$0.style.backgroundColor = '#d93600'
$0
事实上对应的就是你在浏览器控制台中选中的元素。
简单地说,要直接使用JavaScript来控制元素样式,第一步是要选中要被修改样式的元素。上面示例使用的是$0
这样的快速选择元素方式(在浏览器控制台中可以这么使用),在JavaScript中,可以使用querySelector()
方法选中元素,当然在JavaScript中还可以使用其他的方式,比如前面《getElement*
和 querySelector*
》一文中介绍选中元素的方法。第二步是找到你关心的CSS属性(想要更换的CSS样式属性,比如上例中的backgroundColor
,其实对应的就是CSS中的background-color
),然后给该属性赋值。需要注意的是,CSS中的许多值实际上是字符串。也要记住,很多值需要带有像px
或em
这样的度量单位才能被识别。
通过elem.style.poperty
方式修改元素的样式,实际上修改的是HTML元素的style
属性的值。比如:
总而言之,可以控制HTML元素的style
属性来修改任何你想修改的HTML元素的样式。
也就是说,style
是HTML元素的一个标签属性,也就是说它也是元素节点。既然是DOM元素的节点,我们就可以通过getAttribute
、setAttribute
和removeAttribute
等方法来读写或删除DOM元素的style
属性。就上面的示例,咱们可以使用setAttribute()
实现同样的效果,比如:
$0.setAttribute(
'style',
'background-color: orange; z-index: 9999; border-left-width: 5px;'
)
如果你对
getAttribute
、setAttribute
和removeAttribute
从未接触或不太了解,建议你花点时间阅读前面整理的文章《Attribute
和Property
》一文。
上面的示例演示了HTMLElement.style
属性来给HTML元素添加样式,其效果等同于使用HTMLElement.setAttribute('style','属性值')
。事实上,HTMLElement.style
属性返回的是一个CSSStyleDeclaration
对象,表示元素的内联style
属性,但会忽略样式表应用的属性。
CSSStyleDeclaration
接口中的HTMLElement.style
接口除了给HTML元素写CSS样式之外,也可以读:
$0.style.backgroundColor // => "orange"
$0.style.zIndex // => "9999"
$0.style.borderLeftWidth // => "5px"
上面代码中,style
属性的值是一个CSSStyleDeclaration
实例。这个对象所包含的属性与CSS规则一一对应,但CSS属性名需要改写,比如background-color
要改成驼峰写法,即backgroundColor
。还有一点要特别的注意,如果CSS属性名是JavaScript保留字,则样式名之前需要加上字符串css
,比如float
要写成cssFloat
。
HTMLElement.style.cssText
前面的示例也看到了,除了通过setAttribute('style','styleProperty:styleValue')
方式给HTML元素的style
设置样式之外,还可以通过HTMLElement.style.propery=value
的方式来给style
属性添加样式。但对于要给一个元素添加多个属性值时,需要多次写,对于追求极速开发体验的码农而言,总感觉会有点蛋疼。在CSSStyleDeclaration
的HTMLElement.style.cssText
属性用来读写当前元素style
的值。
先来看读:
$0.style.cssText
输入的值是:
background-color: orange; z-index: 9999; border-left-width: 5px;
同样的,如果我们要给一个元素添加样式,也可以使用.style.cssText
方式,比如:
$0.style.cssText = 'color:#f36; font-size: 2rem;padding: 10px'
这个时候对应的元素的style
值变成:
使用HTMLElement.style.cssText
给元素的style
添加值是要特别注意,如果元素的style
属性一开始就有值,使用上面的方式添加值的话将会覆盖原有的属性值。比如:
值得庆幸的是,我们可以使用下面的方式来改变:
$0.style.cssText += 'color:#f36; font-size: 2rem; padding: 10px'
其中color
、font-size
和padding
属性被追加到元素的style
上,而不是直接覆盖,比如下图所示:
注意,使用cssText
的属性值不用改写CSS属性名。
另外,在使用cssText
时要给一个元素删除style
中的样式,可以给该方法设置值为空字符串,比如:
$0.style.cssText = '';
window.getComputedStyle()
众所周知,HTML元素的行内样式是具有最高的优先级,改变行内样式,通常会立即反映出来。但是,HTML中DOM元素最终的样式是综合各种规则计算出来的。也就是说,要想得到元素实际样式,只读取行内样式是不够的,需要得到浏览器最终计算出来的样式规则。
至于CSS样式是怎么被浏览器计算出来的,真正要理解就要去理解浏览器的工作原理相关的知识了。有关于这部分就不在这里阐述了,如果你感兴趣,可以阅读下面几篇文章:
如果你花时间阅读了上面的文章或者说你对浏览器渲染CSS有一定的了解的话,那么就知道,浏览器解析CSS样式一共有五个来源,其中开发人员只能接触其中的三个来源,HTML元素内联 style
属性,HTML的style
标签样式和link
引用样式;其中另外两个是浏览器客户端的样式:浏览器默认样式和浏览器用户自定义样式。用一张图简单的描述就如下图所示:
其实,如果你平时细心的话,在使用浏览器自带的开发者工具的时候,你就可以查看到浏览器对元素计算出来的属性,比如下图:
而在JavaScript中,咱们就可以使用window.getComputedStyle
方法返回浏览器计算后得到的最终样式规则(即计算出来的样式)。这个方法接受一个节点对象作为参数,返回一个CSSStyleDeclaration
实例,它包含了指定节点的最终样式信息。比如:
上图可以看到元素$0
计算出来的最终样式信息。如果我们把其赋值给一个变量,比如objStyle
,那么我们可以通过类似下面的方式取出元素对应的样式属性的值:
objStyle.color // => rgb(64, 64, 64)
objStyle.backgroundColor // => rgba(0, 0, 0, 0)
getComputedStyle
方法还可以接受第二个参数,表示当前元素的伪元素,比如:before
、:after
、:first-line
和:first-letter
等。
window.getComputedStyle($0, ':before')
需要注意的是,节点元素的style
对象无法读写伪元素的样式,这个时候就需要用到window.getComputedStyle
。使用JavaScript获取伪元素可以用类似下面这样的方法:
window.getComputedStyle($0, ':before').color
除此之外,还可以使用接下来要介绍的CSSStyleDeclaration
实例的getPropertyValue
方法。比如:
window.getComputedStyle($0, ':before').getPropertyValue('color')
在使用CSSStyleDeclaration
实例有几点需要注意:
CSSStyleDeclaration
实例返回的CSS值都是绝对单位。比如,长度都是像素单位,颜色是rgb()
或rgba()
格式- CSS规则的简写形式无效。比如,
margin
的值不能直接读,需要分开来读,比如marginLeft
、marginTop
等 - 如果读取CSS原始的属性名,要用方括号运算符,比如
objStyle['z-index']
,也可以换成驼峰方式objStyle.zIndex
- 该方法返回的
CSSStyleDeclaration
实例的cssText
属性无效,返回undefined
CSSStyleDeclaration
实例的其他方法
CSSStyleDeclaration
实例除了上述的一些方法之外,还有其他的一些方法或属性。接下来咱们要学习的就是有关于他的属性和方法。
前面也提到过了,CSSStyleDeclaration
实例返回的是一个object
,那么我们object
的一些属性和方法也可以用到这里。比如length
、item()
之类的等。比如:
let ele = document.querySelector('#viewport')
let objStyle = window.getComputedStyle(ele)
objStyle.length // => 292
objStyle[0] // => animation-delay
我们也可以通过for
循环把其每个属性打印出来,比如:
for (let i = 0; i < objStyle.length; i++) {
console.log(objStyle[i])
}
除此之外,还有几个实例方法:
CSSStyleDeclaration.getPropertyPriority
方法接受 CSS 样式的属性名作为参数,返回一个字符串,表示有没有设置important
优先级。如果有就返回important
,否则返回空字符串。CSSStyleDeclaration.getPropertyValue
方法接受 CSS 样式属性名作为参数,返回一个字符串,表示该属性的属性值。CSSStyleDeclaration.removeProperty
方法接受一个属性名作为参数,在 CSS 规则里面移除这个属性,返回这个属性原来的值。CSSStyleDeclaration.setProperty
方法用来设置新的 CSS 属性。该方法没有返回值。
有关于getComputedStyle
更详细的介绍可以阅读:
通过className
和classList
控制样式
在我们Web的开发中,通过控制HTML元素的的类名(添加或删除),从而来控制元素的样式,这也是非常常见的方式。在JavaScript中,可以通过className
和classList
来给元素添加或删除样式,从而实现元素样式的修改。
通过className
添加和删除类
通过前面的学习,我们知道className
属性允许你访问HTML元素的class
属性。通过HTMLElement.className
可以读出HTML元素的class
属性的值。同样的,给className
赋值,可以给元素添加类名,但不同的是,新的类名将会覆盖旧的类名。比如下面这样的一个操作:
可以看到,新添加的new
类名,覆盖了已有的jhp
类名。如果想让新添加的类名不覆盖已有的类名,可以通过+=
的方式来添加,比如:
$0.className += ' jhp'
这个时候重新打印className
,其值就是new jhp
,在对应的HTML元素相比较而言就成功的添加了新的类名。
不过这样写,有点蛋疼,咱们可以将其封装成一个函数,比如addClass()
:
function addClass(elements, myClass) {
// 如果没有元素,我们就跳出函数
if (!elements) {
return;
}
// 如果有一个选择器,将获取所选择的元素
if (typeof(elements) === 'string') {
elements = document.querySelectorAll(elements)
}
// 如果我们只有一个DOM元素,那么将它作为一个简化的数组件来操作
else if (elements.tagName) {
elements = [elements]
}
// 向所有选中的元素添加类
for (var i = 0; i < elements.length; i++) {
// 如果类名不存在
if ((' ' + elements[i].className + ' ').indexOf(' ' + myClass + ' ') < 0) {
// 添加类
elements[i].className += ' ' + myClass
}
}
}
有了这个函数之后,咱们就可以这样使用了:
addClass('.classdiv','highlight');
addClass(document.getElementById('iddiv'),'highlight');
使用className
给指定的元素删除一个类名,相对来说比较麻烦一点。比如上例,咱们使用addClass()
函数之后,通过$0.className
可以读取出当前元素$0
的class
的值为new jhp primary
。如果我们要把类名重新恢复到new jhp
时,只能像下面这样:
$0.className = 'new jhp'
为了能更好的操作,咱们也可以类似于addClass()
函数一样,写一个removeClass()
:
function removeClass(elements, myClass) {
// 如果没有这个元素,直接跳出函数
if (!elements) {
return;
}
// 如果有一个选择器,将获取所选择的元素
if (typeof(elements) === 'string') {
elements = document.querySelectorAll(elements)
}
// 如果我们只有一个DOM元素,那么将它作为一个简化的数组件来操作
else if (elements.tagName) {
elements = [elements]
}
// 创建查找类的模式
var reg = new RegExp('(^| )' + myClass + '($| )', 'g')
// 从选中的元素中删除类
for (var i = 0; i < elements.length; i++) {
elements[i].className = elements[i].className.replace(reg, ' ')
}
}
如果要在指定的元素中删除一个类名,就可以像下面这样使用:
removeClass($0, 'new')
最后的结果正如我们所希望的一样:
特别声明,上面
addClass()
和removeClass()
两函数的的代码来源于@Yaphi Berhanu写的博文。
通过classList
来添加和删除类等
上面看的是className
给指定元素的class
中添加和删除类。事实上,咱们还可以使用classList
。在classList
中主要的API有:
elem.classList.add()
: 添加类名elem.classList.remove()
:删除类名elem.classList.toggle()
:切换类名,如果改类名存在将会删除该类名,如果不存在将会添加该类名elem.classList.contains
:判断是否含有该类名,如果包含则返回true
,反之返回的是false
比如我们当前元素$0
,可以在浏览器中输出$0.classList
对应的值。从返回的结果上可以看出来,其返回的是DOMTokenList
。而且从其__proto__
中可以找到classList
对应的API,比如add
、contains
、remove
等。
DOMTokenList
这种类型表示一组空间分隔的标记。通常由HTMLElement.classList
,HTMLLinkElement.relList
,HTMLAnchorElement.relList
或HTMLAreaElement.relList
返回。从0
开始的类JavaScript数组索引。DOMTokenList
始终是区分大小写的。
classList.add()
使用classList.add()
添加类名,咱们可以像下面这样:
$0.classList.add('make', 'me', 'look', 'rad')
classList.contains()
通过classList.contains()
可以判断元素是否包含指定的类名,如果包含返回的是true
,如果没包含返回的是false
:
$0.classList.contains('new') // => false
$0.classList.contains('make') // => true
classList.remove()
使用classList.remove()
可以删除指定的类名。比如:
classList.toggle()
其实该API就类似一个开关。打个比方说,如果当前元素中有指定的类名,那么执行该 API之后会将指定的类名删除,返之将会添加。类似这样的一个操作:
if ($0.classList.contains('rad')) {
$0.classList.remove('rad');
} else {
$0.classList.add('rad');
}
上面的这个功能,就可以使用classList.toggle
来替代:
$0.classList.toggle('cool')
使用classList.toggle
表示:如果添加类,将返回true
,如果删除类,将返回false
:
let a = $0.classList.toggle('cool');
console.log(a); // => true
classList.toggle
可以选择接受第二个参数,该参数是一个布尔值。可以根据第二个参数来控制是否添加还是删除类。
let someCondition;
let b = shadesEl.classList.toggle('cool', !!someCondition);
console.log(b); // => false, `someCondition` 是 undefined,计算结果是false, class被删除
someCondition = 'I wear my sunglasses at night';
let c = shadesEl.classList.toggle('cool', !!someCondition);
console.log(c); // => true, `someCondition` 计算出来的结果是true, class被添加
有了classList
之后,那么前面写的addClass()
和removeClass()
就可以修改一下了。把以前的className
替换成现在的classList
。如此一来,代码会变得简单的多。比如:
function addClass(elements, myClass) {
elements = document.querySelectorAll(elements)
for (var i = 0; i < elements.length; i++) {
elements[i].classList.add(myClass)
}
}
function removeClass(elements, myClass) {
elements = document.querySelectorAll(elements)
for (var i = 0; i < elements.length; i++) {
elements[i].classList.remove(myClass)
}
}
classList
其他API
classList
除了上面一些常用的API之外,还有length
、value
属性以及entries
、forEach
、item
和keys
等。大家可以根据实际需要的去选择所需要的API。比如:
有关于classList
更多的介绍可以阅读:
- Adding, Removing & Toggling Classes With classList in JavaScript
- Quick Tip: Add or Remove a CSS Class with Vanilla JavaScript
- HTML5 DOM元素类名相关操作API classList简介
- CSS操作
总结
通过上面的学习,知道如何通过JavaScript来修改CSS样式。我们平常中会使用到的方法会有:
- 通过DOM Element对象的
getAttribute()
、setAttribute()
和removeAttribute()
等方法修改元素的style
属性 - 通过对元素节点的
style
来读写行内CSS样式 - 通过
style
对象的cssText
属性来修改全部的style
属性 - 通过
style
对象的setProperty()
、getPropertyValue()
、removeProperty()
等方法来读写行内CSS样式 - 通过
window.getComputedStyle()
方法获得浏览器最终计算的样式规则 - 通过
className
或classList
给元素添加或删除类名,配合样式文件来修改元素样式
其实除了上述介绍的方法之外,咱们还可以直接添加样式。常见的方式是通过创建style
标签来添加内置的CSS样式表或者通过link
标签来引用外部样式表。另外还可以使用addRule
和insertRule
来添加样式规则。不过在这篇文章我们没有介绍这些内容,我们将在后续的内容中再来学习这方面的知识。如果你对这方面的知识感兴趣,欢迎持续观注后续的更新。
由于自身是JavaScript的初学者,如果文章中有不对之处,烦请各位拍正。如果您在这方面有更多经验,欢迎在下面的评论中与我们一起分享。如果你觉得这篇文章帮助你了,欢迎给我打赏(^_^)!!!
如需转载,烦请注明出处:https://www.fedev.cn/javascript/style-and-class.htmlNike Air Shox Deliver 809 Men Running shoes Deep Blue Black