前面介绍的是操作 DOM 元素本身,这里介绍如何使用 JS 操作 DOM 元素的样式。
其实我们已经知道了,我们其实可以直接操作 DOM 元素的属性,而 DOM 元素有一个叫 class 的属性,我们只需要修改元素的 class 属性,其实就可以让他应用不同的样式,如下所示:
html
<style>
.container {
height: 100px;
width: 100px;
border: 1px solid;
}
.red {
background-color: red;
}
</style>
<div class="container">
</div>
<button class="btn">change</button>
<script>
var div = document.querySelector(".container");
var btn = document.querySelector(".btn");
btn.onclick = function () {
div.className = "container red"
}
</script>
在点击的时候修改元素的 class 属性,从而间接修改样式。
但是这个 className 属性虽然兼容性非常好,但其实并不好用,为什么呢,就比如我元素的 class 属性为 container green left list,此时我通过 className 属性获取到的值就是 "container green left list" 这样一个字符串,如果我要删除掉 green,我当然可以使用 split() 函数分割为数组,然后用 indexOf() 函数找到 green 所在的位置,然后再用 splice() 函数删除掉 green,然后再用 join() 函数重新组装成字符串这样修改,但是是不是有一点太麻烦了。从这个小例子就可以看出,由于 class 属性可以同时有多个,我们想通过 className 修改元素的 class 属性是一件非常麻烦的事情,对于拥有多个 class 属性的元素,我们要修改其 class 属性,更愿意将他转换为数组来处理,实际上,浏览器提供的 classList 属性已经做好了这件事。
Element.classList 属性可以返回 Element 的所有 class 属性,并且以一个类数组的形式返回,除此之外,这个类数组有很多可供使用的方法:
classList.remove():删除类名classList.add():添加类名classList.toggle():切换类名,如果参数里面的类名存在,就删除它,如果不存在,就加上他classList.replace():将第一个参数提供的类名替换为第二个参数提供的类名classList.contains():用于判断某个类名是否存在
remove,add, 均可以写多个参数,实现批量添加。
js
var div = document.querySelector(".container");
var btn = document.querySelector(".btn");
btn.onclick = function () {
div.classList.toggle("red");
}
实际上,
toggle方法还支持第二个参数,如果填入第二个参数,那么第二个参数的布尔判定为真则添加,第二个参数的布尔判定为假则移除。
这种通过控制 class 属性间接控制样式的方式,是一个开发当中非常常用的方式,但这种方式有明显的局限性,那就是样式必须在样式表中提前写好,JS 其实是可以直接控制元素样式的。
读者其实很容易想到如何用 JS 直接控制元素样式,看下面的例子:
js
var div = document.querySelector(".container");
var btn = document.querySelector(".btn");
btn.onclick = function () {
div.style = "background:green;border:20px solid red";
}很显然,我们其实是可以直接修改元素的 style 属性从而给元素直接设置行间样式。但是读者注意,这种方式是不是也不好用,实际上,浏览器早就考虑到了这一点,所以给我们提供的标准里面,style 属性其实是一个对象,所以上面的代码其实等价于:
js
var div = document.querySelector(".container");
var btn = document.querySelector(".btn");
btn.onclick = function () {
div.style.background = "green";
div.style.border = "20px solid red";
}那为什么我们给 style 属性直接赋值为一个字符串也没问题呢?这是因为在给这个对象赋值为一个字符串的时候,它会自动解析你的字符串,然后根据你的字符串给自己的属性赋值。
所以我们其实可以使用 Element.style.AttributeName 的方式获取和修改 Element 的某个行间样式。
对于短横杠命名法的属性,例如
border-color,需要使用小驼峰命名法,即Element.style.borderColor来获取和修改
Element.style实际上是一个类数组,这个类数组中的数组项用于存放现在已经设置的行间样式。
实际上,我们可以使用
Element.style.getPropertyValue(name)来拿到一个行间样式,使用Element.style.setProperty(name,value)来设置行间样式,使用Element.style.removeProperty(name)来移除行间样式,当然这么用好像显得更加麻烦,读者喜欢哪种就用哪种吧。
此时我们已经可以获取和修改元素的内联样式了,但是这其实还是不够好,我现在有这样一个需求,我要把一个盒子的宽度增加 100 像素,这个时候问题就暴露出来了,我现在只能获取到这个元素的内联样式,但是这个盒子的宽度是 CSS 样式表设置的,我完全不知道他目前的宽度是多少。要实现这个功能,核心就在于如何获取元素的计算属性,也就是是显示在页面上真实的 CSS 属性,我们是完全有能力获取的,即通过 window.getComputedStyle() 方法获取。
js
var div = document.querySelector(".container");
var btn = document.querySelector(".btn");
btn.onclick = function () {
var w = Number.parseFloat(window.getComputedStyle(div).width);
div.style.width = w + 100 + "px";
}
我们只需要把 DOM 元素作为参数传入 window.getComputedStyle() ,该方法就会给我们返回一个对象,这个对象里就包含了这元素的所有计算属性,例如上面的 window.getComputedStyle(div).width,就可以获取 div 元素的计算属性中的 width 属性,由于这是一个计算属性,得到的是一个形如 "100px" 的字符串,所以需要使用 parseFloat 函数转为数值,此时就获取到了元素当前的真实宽度,我们再通过设置元素的内联样式完成我们想要的效果即可。
window.getComputedStyle()可以传入第二个参数,用于得到该元素的伪元素的样式,例如第二个参数填入"before",就可以获得before伪元素的样式,其他的都同理。
以下是本节练习
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.container {
height: 50px;
border: 1px solid;
}
.left {
height: 50px;
line-height: 50px;
float: left;
margin: 0 10px;
cursor: pointer;
}
.active {
color: #f40;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<div class="left active">首页</div>
<div class="left">第二页</div>
<div class="left">第三页</div>
<div class="left">第四页</div>
<div class="left">第五页</div>
<div class="left">第六页</div>
</div>
<script>
/**
* 为容器内的所有子元素绑定点击事件,实现激活状态的切换。
* 当点击某个子元素时,只有当前被点击的元素会获得激活状态(active类)。
* 激活状态会随着点击不同的子元素而切换。
*/
var container = document.querySelector('.container'); // 获取容器元素
var currentActive = container.children[0]; // 初始化当前激活元素为容器的第一个子元素
// 遍历容器内的所有子元素,为每个子元素绑定点击事件
for (var i = 0; i < container.children.length; i++) {
container.children[i].onclick = function () {
// 如果当前点击的元素已经是激活状态,则直接返回,不进行任何操作
if (currentActive === this)
return;
// 移除当前激活元素的active类,添加到被点击的元素上
currentActive.classList.remove("active");
this.classList.add("active");
// 更新当前激活元素为被点击的元素
currentActive = this;
}
}
</script>
</body>
</html>html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.container {
width: 200px;
height: 200px;
cursor: pointer;
border: 1px solid black;
}
</style>
</head>
<body>
<div class="container">
</div>
<script>
/**
* 为容器元素绑定点击事件,每次点击随机改变容器的背景颜色。
* @param {Event} event - 点击事件对象,此处未使用。
*/
var container = document.querySelector('.container'); // 获取页面中第一个类名为container的元素
container.onclick = function () {
// 生成一个随机的16进制颜色代码,并设置为容器的背景颜色
var color = "#" + Math.floor(Math.random() * 16777216).toString(16).padStart(6, "0");
//也可以生成三个0-255之间的随机数,然后拼接成rgb颜色代码
// var color = "rgb(" + Math.floor(Math.random() * 256) + "," + Math.floor(Math.random() * 256) + "," + Math.floor(Math.random() * 256) + ")"
this.style.backgroundColor = color; // 修改当前元素(容器)的背景颜色
}
</script>
</body>
</html>html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.container {
width: 200px;
height: 200px;
border: 1px solid black;
text-align: center;
line-height: 200px;
overflow: hidden;
}
</style>
</head>
<body>
<div class="container">
凌
</div>
<button class="add">+</button>
<button class="sub">-</button>
<script>
// 获取页面中具有.add类的元素、具有.sub类的元素以及具有.container类的元素
var add = document.querySelector('.add');
var sub = document.querySelector('.sub');
var container = document.querySelector('.container');
/**
* 获取当前容器的字体大小
* @returns {number} 返回当前容器字体大小的整数值
*/
function getCurrentFontSize() {
return parseInt(window.getComputedStyle(container).fontSize);
}
// 为增加字体大小按钮添加点击事件处理程序
add.onclick = function () {
var fontSize = getCurrentFontSize(); // 获取当前字体大小
// 将容器的字体大小增加5像素
container.style.fontSize = fontSize + 5 + 'px';
}
// 为减少字体大小按钮添加点击事件处理程序
sub.onclick = function () {
var fontSize = getCurrentFontSize(); // 获取当前字体大小
// 将容器的字体大小减少5像素
container.style.fontSize = fontSize - 5 + 'px';
}
</script>
</body>
</html>html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.container img {
float: left;
margin: 5px 10px;
}
</style>
</head>
<body>
<div class="container">
</div>
<script>
// 获取页面中类名为container的元素
var container = document.querySelector('.container');
// 初始化一个空数组用于存放文件名
var fileArr = [];
// 生成1到13的文件名数组,每张牌都有4个面
for (var i = 1; i <= 13; i++) {
for (var j = 1; j <= 4; j++) {
fileArr.push(`${i}_${j}.jpg`);
}
}
// 添加额外的两张牌的文件名
fileArr.push("14_1.jpg", "15_1.jpg");
// 随机打乱文件名数组
fileArr = fileArr.sort(function () {
return Math.random() - 0.5;
});
// 创建一个文档片段,用于批量添加元素到DOM中,减少页面重绘
var frag = document.createDocumentFragment();
// 遍历经过打乱的文件名数组,创建并添加图片元素到文档片段
fileArr.forEach(function (item) {
var img = document.createElement("img");
img.src = `./pokers/${item}`; // 设置图片路径
frag.append(img); // 将图片元素添加到文档片段
});
// 将文档片段添加到页面中的container元素中
container.append(frag);
</script>
</body>
</html>html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.container {
height: 380px;
width: 380px;
position: relative;
border: 1px solid black;
margin: 20px;
}
.container>div {
height: 120px;
width: 120px;
position: absolute;
}
.container .item {
background-image: url("./img/lol.png");
background-size: 360px 360px;
border: 1px solid black;
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="blank"></div>
</div>
<script>
// 获取容器和所有子div元素
var container = document.querySelector('.container');
var divs = document.querySelectorAll('.container > div');
var blank = document.querySelector('.blank');
/**
* 改变元素的位置
* @param {HTMLElement} dom - 要改变位置的元素
* @param {number} x - 新的x坐标
* @param {number} y - 新的y坐标
*/
function changeItemPosition(dom, x, y) {
dom.style.left = x * 120 + (x + 1) * 5 + 'px';
dom.style.top = y * 120 + (y + 1) * 5 + 'px';
}
/**
* 初始化所有元素的位置
*/
function init() {
var index = 0;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
divs[index].style.backgroundPosition = `${-i * 120}px ${-j * 120}px`;
divs[index].currentPosition = [i, j];
changeItemPosition(divs[index], i, j);
index++;
}
}
}
/**
* 判断是否可以点击移动
* @param {HTMLElement} dom - 被点击的元素
* @returns {boolean} 是否可以移动
*/
function isClick(dom) {
if (dom == blank)
return false
for (var i = -1; i <= 1; i++) {
for (var j = -1; j <= 1; j++) {
if (Math.abs(i) + Math.abs(j) != 1) {
continue;
}
if (blank.currentPosition[0] === dom.currentPosition[0] + i && blank.currentPosition[1] === dom.currentPosition[1] + j)
return true
}
}
return false;
}
/**
* 交换两个元素的位置
* @param {HTMLElement} dom1 - 第一个元素
* @param {HTMLElement} dom2 - 第二个元素
*/
function exchangeItem(dom1, dom2) {
if (dom1 === dom2)
return;
changeItemPosition(dom1, dom2.currentPosition[0], dom2.currentPosition[1]);
changeItemPosition(dom2, dom1.currentPosition[0], dom1.currentPosition[1]);
var temp = dom2.currentPosition;
dom2.currentPosition = dom1.currentPosition;
dom1.currentPosition = temp;
}
/**
* 检查是否胜利
* @returns {boolean} 是否胜利
*/
function isVictory() {
var index = 0;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (!(divs[index].currentPosition[0] == i && divs[index].currentPosition[1] == j))
return false;
index++;
}
}
return true;
}
// 初始化元素位置
init();
// 给所有可移动元素绑定点击事件
for (var i = 0; i < divs.length; i++) {
divs[i].onclick = function () {
if (this === blank || !isClick(this))
return;
exchangeItem(blank, this);
if (isVictory()) {
alert('You Win!');
}
}
}
// 随机交换元素位置
for (var i = 0; i < divs.length; i++) {
for (var j = 0; j < divs.length; j++) {
if (Math.random() < 0.5)
exchangeItem(divs[i], divs[j]);
}
}
</script>
</body>
</html>