动态修改样式
有时候我们会通过JavaScript来动态修改样式,这个时候我们有两个选择:
- 在CSS中编写好对应的样式,动态的添加class
- 动态的修改style属性
// 修改和设置内联style
const boxEl = document.getElementById('box')
const fontSize = '25px'
// 元素.style 获取到的是一个对象
// 可以通过该对象来修改和设置元素对应的内联样式
// 1. 使用style设置的样式需要一个个进行添加,所以如果需要设置的样式比较多的情况下,推荐动态修改class
// 2. 使用style设置的样式的属性值可以是变量或者某个可能会改变的值,这个时候就推荐操作style,因为class只能绑定样式,无法动态修改样式
boxEl.onclick = () => {
boxEl.style.color = 'red'
boxEl.style.backgroundColor = 'skyblue'
// 这里font-size的值是一个变量,所以这里只能通过style的方式来进行设置
boxEl.style.fontSize = fontSize
}
const boxEl = document.getElementById('box')
// class attribute对应的 property属性的名称为className
// 这是因为JavaScript早期是不允许使用class这种关键字来作为对象的属性,所以DOM规范使用了className
// 虽然现在已经没有这类限制,但是毕竟DOM规范使用了className,所以推荐使用className,而不是class
// boxEl.className = 'active' 这种方式设置的class会将之前设置的class给覆盖掉
boxEl.onclick = () => boxEl.className = 'active'
classList
根据之前的示例我们可以看到,对className进行赋值,它会替换整个类
如果我们需要添加或者移除单个的class,可以使用classList属性
classList是一个存储了元素样式的可迭代对象,可以使用for-of遍历
注意: 默认情况下,classList添加的都是类选择器,所以可以省略类选择器前边的点
| 方法 | 说明 |
|---|---|
| elem.classList.add (class) | 添加一个类 |
| elem.classList.remove(class) | 移除类 |
| elem.classList.toggle(class) | 如果类不存在就添加类,存在就移除它 |
| elem.classList.contains(class) | 检查给定类是否存在,返回 true/false |
// add方法在添加类的时候,会自动在类之前加上对应的空格
// 例如原本的类有title 即class=“title”
// add('active')后,会变为class=“title active”
boxEl.classList.add('active')
// 判断一个元素上是否存在某个类
// 参数为box 不是 .box
boxEl.classList.contains('box')
const boxEl = document.getElementById('box')
for (const styleName of boxEl.classList) {
console.log(styleName)
/*
=>
box
active
*/
}
style
设置
如果需要单独修改某一个CSS属性,那么可以通过style来操作
对于多词(multi-word)属性,推荐使用驼峰式 camelCase替换多词写法
const boxEl = document.getElementById('box')
boxEl.style.color = 'red'
// 多值属性在设置的时候,可以使用多值写法,也可以使用小驼峰写法
// 推荐使用小驼峰写法
boxEl.style.fontSize = '30px'
boxEl.style['background-color'] = 'skyblue'
// 如果我们将值设置为空字符串,就相当于在内联样式中,移除了对应的样式设置
// 此时其就会使用对应class中设置的样式,如果都没有找到,那么就会使用浏览器默认样式
boxEl.style.display = ''
// 使用style来设置样式的时候,只能一个个进行设置
// 直接组合成一个对象后再进行赋值 是不起作用的,也就是无效的
boxEl.style = {
color: red,
fontSize: '30px',
backgroundColor: 'skyblue'
}
const boxEl = document.getElementById('box')
// boxEl.style.color = 'red'
// boxEl.style.fontSize = '30px'
// boxEl.style['background-color'] = 'skyblue'
// 我们可以使用cssText属性来为元素同时设置多个style属性
// 但是它和classname一样,会覆盖原本设置的所有style样式,不推荐
boxEl.style.cssText = `
font-size: 20px;
color: red;
background-color: skyblue
`
获取
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.active {
color: red;
font-size: 24px;
}
</style>
</head>
<body>
<div class="box active" id="box" style="background-color: skyblue;">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsum, est!
</div>
<script src="./index.js"></script>
</body>
</html>
const boxEl = document.getElementById('box')
// 对于内联样式,是可以通过style.*的方式读取到的
// --- 如果读取的元素不存在于style中,默认的返回值是空字符串
// 对于通过style或css文件设置到元素上的样式,是读取不到的
console.log(boxEl.style.backgroundColor) // => skyblue
console.log(boxEl.style.color) // => ''
console.log(boxEl.style.fontSize) // => ''
// 如果我们需要获取style或css文件中设置到元素上的样式,可以使用全局函数getComputedStyle
// getComputedStyle方法有两个参数,参数1为元素对象,参数2为对应元素的某个伪元素选择器
console.log(getComputedStyle(boxEl)) // => getComputedStyle方法的返回值是一个对象
// 获取到的是计算后的样式值,而不是设置值
// 如font-size是16px,设置的值为2em --- 得到的结果为 32px 而不是2em
console.log(getComputedStyle(boxEl).fontSize) // => 24px
元素操作
创建元素
我们可以使用document.write 方法写入一个元素
- 这种方式写起来非常便捷,但是对于复杂的内容、元素关系拼接并不方便
- 它是在早期没有DOM的时候使用的方案,目前依然被保留了下来,但是已经不常使用
目前我们最为常见的创建元素流程为:
-
使用
document.createElement(tag)创建一个元素或者使用
document.createDocumentFragment来创建对应的代码片段 -
将新建的元素或代码片段插入到DOM的某一个位置
const ulEl = document.getElementById('ul')
// documentFragment相对于dom中的一个临时子树,可以视为轻量级的document来进行使用
// 我们可以将多个元素先挂载到documentFragment下,然后在统一一起挂载到我们所需要挂载的那个元素下(在这里就是ulEl)
// 这样我们实际只在浏览器中操作了一次DOM,而其余元素的挂载是在内存中完成的,从而减少了页面的回流,提高了性能
const fragmentEl = document.createDocumentFragment()
for (let i = 1; i <= 3; i++) {
const liEl = document.createElement('li')
liEl.textContent = i
fragmentEl.append(liEl)
}
ulEl.append(fragmentEl)
元素的CRUD
对元素的操作有新方法和旧方法,
对于旧方法,操作元素必须要找到其对应的父节点,并且一次只能操作一个元素
对于新方法,其参数是可变参数,也就是其可以插入任意个元素或字符串
这些字符串和元素在进行操作的时候,会被视为兄弟节点
| 方法 | 说明 | 对应的旧方法 | 旧方法说明 |
|---|---|---|---|
| node.append(...nodes or strings) | 在 node 末尾 插入节点或字符串 | parentElem.appendChild(node) | 在parentElem的父元素最后位置添加一个子元素 |
| node.prepend(...nodes or strings) | 在 node 开头 插入节点或字符串 | parentElem.insertBefore(node, nextSibling) | 在parentElem的nextSibling前面插入一个子元素 |
| node.before(...nodes or strings) | 在 node 前面 插入节点或字符串 | parentElem.insertBefore(node, nextSibling) | |
| node.after(...nodes or strings) | 在 node 后面 插入节点或字符串 | parentElem.appendChild(node) 或 parentElem.insertBefore(node, nextSibling) | |
| node.replaceWith(...nodes or strings) | 使用给定的节点或字符串替换 node | parentElem.replaceChild(node, oldChild) | 在parentElem中,新元素替换之前的oldChild元素 |
| node.remove() | 移除元素自身及对应子元素 | parentElem.removeChild(node) | 在parentElem中,移除某一个元素 |
插入元素和替换元素
const boxEl = document.querySelector('.box')
// 查找div.box 下的第一个span元素
const boxSpanEl = boxEl.querySelector('span')
const spanEl = document.createElement('p')
spanEl.textContent = 'hello world'
boxSpanEl.replaceWith(spanEl, 'lorem')
移除元素
移除元素我们可以调用元素本身的remove方法
const boxEl = document.querySelector('.box')
// 移除自身元素 以及 自身的所有子元素
boxEl.remove()
克隆元素
如果我们想要复制一个现有的元素,可以通过cloneNode方法
- 可以传入一个Boolean类型的值,来决定是否是深度克隆
- 深度克隆会克隆对应元素的子元素,否则不会
const boxEl = document.querySelector('.box')
const cloneEl = boxEl.cloneNode()
const deepCloneEl = boxEl.cloneNode(true)
console.log(cloneEl) // => <div class="box"></div>
console.log(deepCloneEl)
/*
=>
<div class="box">
<h2 class="title">hello world</h2>
<span>Lorem, ipsum.</span>
</div>
*/
元素操作综合练习 --- 倒计时
const countDownEl = document.getElementById('countDown')
let hour = 1
let minute = 0
let second = 0
function calcTimerStr(str, length = 2, symbol = '0') {
if (typeof str !== 'string') {
str = str + ''
}
return str.padStart(length, symbol)
}
countDownEl.textContent = `${calcTimerStr(hour)} : ${calcTimerStr(minute)} : ${calcTimerStr(second)}`
const timer = setInterval(() => {
second -= 1
if (second < 0 && minute !== 0) {
minute -= 1
second = 59
}
if (second < 0 && minute < 0 && hour !== 0) {
hour -= 1
minute = 59
second = 59
}
if (second === 0 && minute === 0 && hour === 0) {
clearInterval(timer)
}
countDownEl.textContent = `${calcTimerStr(hour)} : ${calcTimerStr(minute)} : ${calcTimerStr(second)}`
}, 1000)
大小,位置和滚动
我们要获取元素的样式可以使用getComputedStyle方法,但是这个方法只能获取我们所设置的样式的计算值或默认的样式值
对于元素的某些属性,如整个页面的高度(包括溢出的部分),页面滑动了多少,使用getComputedStyle方法是无法获取的
为此JavaScript为我们提供了client系列,offset系列,scroll系列属性来帮助我们获取对应的值
元素的大小,位置,滚动
client系列
clientWidth和clientHeight是不计算滚动条的 (在chrome中,垂直滚动条的宽度为15px)
| 属性 | 说明 |
|---|---|
| clientWidth | contentWith + padding * 2 |
| clientHeight | contentHeight + padding * 2 |
| clientTop | border-top的宽度 |
| clientLeft | border-left的宽度 |
offset系列
| 属性 | 说明 |
|---|---|
| offsetWidth | 元素完整的宽度 |
| offsetHeight | 元素完整的高度 |
| offsetLeft | 当前元素相对于包含块的offsetX |
| offsetHeight | 当前元素相对于包含块的offsetY |
scroll系列
| 属性 | 说明 |
|---|---|
| scrollWidth | 整个可滚动的区域宽度 |
| scrollHeight | 整个可滚动的区域高度 |
| scrollLeft | 向左卷曲出去的距离 + borderLeft |
| scrollTop | 向上卷曲出去的距离 + borderTop |
无论是
client系列,offset系列还是scroll系列,其获取的结果都是Number类型的值, 也就是说获取的结果的默认单位都是px
window的大小,位置
window即是浏览器中的全局对象(GO), 其在语义上其实代表着浏览器窗口
如果网页有滚动条,该滚动条属于window而不是html元素
| 属性 | 说明 |
|---|---|
| innerWidth、innerHeight | 获取window窗口的宽度和高度(包含滚动条) |
| outerWidth、outerHeight | 获取window窗口的整个宽度和高度(包括调试工具、工具栏) |
| documentElement.clientHeight、documentElement.clientWidth | 获取html的内容区域的高度或宽度 只包含content区域,不包含border和padding |
| documentElement.offsetHeight、documentElement.offsetWidth | 获取整个文档的高度或宽度 1. 整个文档的宽度或高度包括content,border和padding 2. 滚动条部分的宽度或高度不统计在内 |
| screenX | 返回浏览器左边界到操作系统桌面左边界的水平距离 |
| screenY | 返回浏览器顶部距离系统桌面顶部的垂直距离 |
| scrollX | X轴滚动的位置 (别名pageXOffset) |
| scrollY | Y轴滚动的位置 (别名pageYOffset) |
scrollTo/scrollBy
scrollTo/scrollBy即可以运行在Element上,也可以运行在Window上
| 方法 | 说明 |
|---|---|
| scrollBy(x,y) | 将页面或元素中的内容滚动至 相对于当前位置的 (x, y) 位置 |
| scrollTo(pageX,pageY) | 将页面或元素中的内容滚动至 (pageX, pageY) scroll方法是scrollTo方法的别名 |
// scrollBy和scrollTo传递两个参数的时候,分别表示的是x轴的距离和y轴的距离
// 如果x和y超出了元素可以滚动的最大距离,那么元素只会滚动其能滚动的最大距离
window.scrollBy(0, 100)
divEl.scrollBy(0, 100)
// 其除了可以传入两个参数外,其还可以传递一个参数,即为配置对象
scrollBy({
// x轴需要滚动的距离
left: 0,
// y轴需要滚动的距离
top: 1000,
// 设置滚动行为,默认情况下,会向锚点一样直接跳过去
// 设置了behavior为smooth后,浏览器会有一个比较平滑的过渡动画效果
behavior: 'smooth'
})
Element.getBoundingClientRect()
Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置,也就是返回"元素边界矩形"对应的值
使用该方法计算的时候,浏览器需要对页面进行重绘,以便于获取最新的结果,所以该方法是比较损耗性能的
getBoundingClientReact方法的返回结果对象存在如下属性
| 属性 | 值 |
|---|---|
| left | 元素左上角距离视口左边的距离 |
| top | 元素左上角距离视口顶部的距离 |
| right | 元素右下角距离视口左边的距离 |
| bottom | 元素右下角距离视口顶部的距离 |
| width | 元素的宽度 对于contentBox -> width + padding + border 对于borderBox -> width |
| height | 元素的高度 对于contentBox -> height + padding + border 对于borderBox -> height |
| x | 元素左上角距离视口左边的距离 不是标准中的属性,浏览器可能不支持,不推荐使用 |
| y | 元素左上角距离视口顶部的距离 不是标准中的属性,浏览器可能不支持,不推荐使用 |