JavaScript动态设置类与相关经典案例
一、JS动态设置类
1.className
属性可以用来设置元素的类(class)值或返回元素的class属性值。
- 语法:
obj.className = classNameValue;
- 案例:
<style>
.red {
color: red;
}
.bg {
background-color: #ff0;
}
</style>
<p>Hello World!</p>
现在有一个p标签,我想把文字的颜色和背景色替换一下,可以这样操作:
<script>
const p = document.querySelector('p');
p.className = "red bg";
</script>
- 效果图:
如果想要读取p元素的class属性值,也可以使用className属性:
console.log(p.className); // red bg
2.classList
属性值可以获取元素的类名,用于为元素添加、移除、切换CSS类名。
- 属性
length:统计元素类名称的个数。
- 案例
<p class="red bg">Hello World!</p>
<script>
console.log(p.classList.length); // 2
</script>
- 常用方法
方法名称 | 解释说明 |
---|---|
add(class1,class2,…) | 向元素添加类名,如果添加的类名已经存在,则不添加 |
remove(class1,class2,…) | 移除元素指定的类名,如果该类名不存在,则会报错 |
toggle(className, boolean) | 切换元素的类名,如果要切换的类名不存在,则会自动向元素里添加这个类名。第二个为可选参数,布尔值用于设置元素是否强制添加或移除类,不管该类名是否存在。 |
contains(class) | 判断指定的类名是否存在。存在返回true,不存在返回false |
- 用法
p.classList.add("green");
p.classList.remove("red");
p.classList.toggle('red1');
p.classList.contains('red1'));
二、dataset对象
读取HTML元素上的用户自定义属性,使用dataset属性。
- 语法
element.dataset.className
- 案例
<p id="aaa" data-email="admin@admin.com" data-my-age="90" data-index="1">我的资料</p>
以上定义了个p元素,如果需要获取id值,可以直接使用:
console.log(p.id); // aaa
若是想要获取data-eamil的属性值,就不能使用p.data-email了,这个用法是错误的,需要使用dataset属性:
console.log(p.dataset.email); // admin@admin.com
如果需要获取data-my-age
属性该怎么办呢?此时,就需要使用驼峰式的方法来获取:
console.log(p.dataset.myAge); // 90
- 总结:
自定义属性必须是以“data-”开头的;
如果要获取自定义的属性,需要去掉前缀“data-”;
在获取的属性值的时候如果需要带(-)的属性值需要转成驼峰式的写法。
三、实战案例
3.1 选项卡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>选项卡</title>
<link rel="stylesheet" href="./static/css/tabs.css">
</head>
<body>
<div class="box">
<!-- tabs -->
<ul class="tabs">
<li class="tab active" data-index="1">综合新闻</li>
<li class="tab" data-index="2">官方新闻</li>
<li class="tab" data-index="3">赛事新闻</li>
<li class="tab" data-index="4">更新日志</li>
</ul>
<!-- panels -->
<ul class="panels">
<li class="panel active" data-index="1">
<div class="panel-lists">
<a class="recommend" href="#">
<h3>完美世界电竞将主办DPC中国区比赛 1月18日举行线下开幕式及揭幕战</h3>
<div class="contents">
<div class="item-avatar">
<img src="https://img.dota2.com.cn/dota2/63/c5/63c52fb6846974b76797c433064a23fa1609475659.jpg" alt="完美世界电竞将主办DPC中国区比赛 1月18日举行线下开幕式及揭幕战">
</div>
<div class="msg">
<p>全球DOTA2玩家期待已久的DPC即将开启,很荣幸地宣布完美世界电竞将主办中国区联赛,全球六大赛区将同步启动。</p>
<span class="date">2021年01月12日</span>
</div>
</div>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2绝品龙钩,竟能够在嘉年华一键领取?</p>
<span class="p_date">2021-01-11</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2 1月11日更新日志:7.28b平衡性更新</p>
<span class="p_date">2021-01-11</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>PC定级赛DAY2赛况:Aster力克RNG,率先杀入S级联赛!</p>
<span class="p_date">2021-01-10</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2展区谍照曝光:探秘Roshan巢穴,与军团决斗</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>没有人给你评级,你来为你自己定级!DPC中国联赛定级赛今日开战</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2 1月8日更新日志:技能征召更新</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DPC中国联赛定级赛1月8日11:00开战 谁能进入S级联赛?</p>
<span class="p_date">2021-01-07</span>
</a>
</div>
</li>
<li class="panel" data-index="2">
<div class="panel-lists">
<a class="recommend" href="#">
<h3>完美世界电竞将主办DPC中国区比赛 1月18日举行线下开幕式及揭幕战</h3>
<div class="contents">
<div class="item-avatar">
<img src="https://img.dota2.com.cn/dota2/63/c5/63c52fb6846974b76797c433064a23fa1609475659.jpg" alt="完美世界电竞将主办DPC中国区比赛 1月18日举行线下开幕式及揭幕战">
</div>
<div class="msg">
<p>全球DOTA2玩家期待已久的DPC即将开启,很荣幸地宣布完美世界电竞将主办中国区联赛,全球六大赛区将同步启动。</p>
<span class="date">2021年01月12日</span>
</div>
</div>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2绝品龙钩,竟能够在嘉年华一键领取?</p>
<span class="p_date">2021-01-11</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2 1月11日更新日志:7.28b平衡性更新</p>
<span class="p_date">2021-01-11</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>PC定级赛DAY2赛况:Aster力克RNG,率先杀入S级联赛!</p>
<span class="p_date">2021-01-10</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2展区谍照曝光:探秘Roshan巢穴,与军团决斗</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>没有人给你评级,你来为你自己定级!DPC中国联赛定级赛今日开战</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2 1月8日更新日志:技能征召更新</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DPC中国联赛定级赛1月8日11:00开战 谁能进入S级联赛?</p>
<span class="p_date">2021-01-07</span>
</a>
</div>
</li>
<li class="panel" data-index="3">
<div class="panel-lists">
<a class="recommend" href="#">
<h3>完美世界电竞将主办DPC中国区比赛 1月18日举行线下开幕式及揭幕战</h3>
<div class="contents">
<div class="item-avatar">
<img src="https://img.dota2.com.cn/dota2/63/c5/63c52fb6846974b76797c433064a23fa1609475659.jpg" alt="完美世界电竞将主办DPC中国区比赛 1月18日举行线下开幕式及揭幕战">
</div>
<div class="msg">
<p>全球DOTA2玩家期待已久的DPC即将开启,很荣幸地宣布完美世界电竞将主办中国区联赛,全球六大赛区将同步启动。</p>
<span class="date">2021年01月12日</span>
</div>
</div>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DPC定级赛DAY2赛况:Aster力克RNG,率先杀入S级联赛!</p>
<span class="p_date">2021-01-10</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2 1月11日更新日志:7.28b平衡性更新</p>
<span class="p_date">2021-01-11</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>PC定级赛DAY2赛况:Aster力克RNG,率先杀入S级联赛!</p>
<span class="p_date">2021-01-10</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2展区谍照曝光:探秘Roshan巢穴,与军团决斗</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>没有人给你评级,你来为你自己定级!DPC中国联赛定级赛今日开战</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2 1月8日更新日志:技能征召更新</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DPC中国联赛定级赛1月8日11:00开战 谁能进入S级联赛?</p>
<span class="p_date">2021-01-07</span>
</a>
</div>
</li>
<li class="panel" data-index="4">
<div class="panel-lists">
<a class="recommend" href="#">
<h3>完美世界电竞将主办DPC中国区比赛 1月18日举行线下开幕式及揭幕战</h3>
<div class="contents">
<div class="item-avatar">
<img src="https://img.dota2.com.cn/dota2/d0/27/d027f043743b5127a9afaf5a2f5730191610354489.jpg" alt="DOTA2 1月11日更新日志:7.28b平衡性更新">
</div>
<div class="msg">
<p>全球DOTA2玩家期待已久的DPC即将开启,很荣幸地宣布完美世界电竞将主办中国区联赛,全球六大赛区将同步启动。</p>
<span class="date">2021年01月12日</span>
</div>
</div>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2绝品龙钩,竟能够在嘉年华一键领取?</p>
<span class="p_date">2021-01-11</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2 1月11日更新日志:7.28b平衡性更新</p>
<span class="p_date">2021-01-11</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>PC定级赛DAY2赛况:Aster力克RNG,率先杀入S级联赛!</p>
<span class="p_date">2021-01-10</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2展区谍照曝光:探秘Roshan巢穴,与军团决斗</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>没有人给你评级,你来为你自己定级!DPC中国联赛定级赛今日开战</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DOTA2 1月8日更新日志:技能征召更新</p>
<span class="p_date">2021-01-08</span>
</a>
<a class="p_list p_highlight" href="#">
<i class="p_icon p_icon_dot"></i>
<p>DPC中国联赛定级赛1月8日11:00开战 谁能进入S级联赛?</p>
<span class="p_date">2021-01-07</span>
</a>
</div>
</li>
</ul>
</div>
<script>
console.log(document.documentElement);
const tabs = document.querySelector('.tabs');
// 卡盘导航
const tabsNum = document.querySelectorAll('.tabs > *');
console.log(tabsNum);
// 内容列表
const panels = document.querySelectorAll('.panel');
console.log(panels);
tabs.addEventListener('click', ev => {
// 事件触发对象(事件绑定者)
console.log(ev.currentTarget);
// 事件触发者
console.log(ev.target.classList.item[0]);
// 激活点击的对象只需要两步:
// 1.清空之前所有处于激活状态的选项卡,并将当前点击的对象激活
console.log(tabs.children);
[...tabs.children].forEach(tab => tab.classList.remove('active'));
ev.target.classList.add('active');
// 2.根据自定义的属性data-index找到对应的列表并显示出来
panels.forEach(panels => panels.classList.remove('active'));
// console.log([...panels].filter(item => item.dataset.index === ev.target.dataset.index));
// filter返回的是一个数组,因此需要:
[...panels].filter(item => item.dataset.index === ev.target.dataset.index)[0].classList.add('active');
});
</script>
</body>
</html>
- 效果图
3.2 一键换肤
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>一键换肤</title>
<style>
.container {
width: 300px;
display: grid;
grid-template-columns: repeat(6, 1fr);
column-gap: 10px;
}
.container > img {
width: 100%;
border: 3px solid #ffffff;
}
body {
width: 100%;
height: 100%;
background: url('./static/images/1.jpg') no-repeat scroll 0 0;
background-size: cover;
}
</style>
</head>
<body>
<div class="container">
<img src="./static/images/1.jpg" alt="">
<img src="./static/images/2.jpg" alt="">
<img src="./static/images/3.jpg" alt="">
<img src="./static/images/4.jpg" alt="">
<img src="./static/images/5.jpg" alt="">
<img src="./static/images/6.jpg" alt="">
</div>
<script>
let clientW = window.screen.width;
let clientH = window.screen.height;
console.log(clientW, clientH);
console.log(document.body);
document.body.style.width = clientW;
document.body.style.height = clientH;
const container = document.querySelector('.container');
container.onclick = ev => {
console.log(ev.currentTarget);
console.log(ev.target);
document.body.style.backgroundImage = `url(${ev.target.src})`;
}
</script>
</body>
</html>
3.3 图片懒加载原理
- 原理
如果当前图片距离文档顶部的高度(偏移量) < 文档可视区 + 滚动高度,则表示图片进入到可视区,需要将图片加载出来。
1.元素距离文档顶部的偏移量
let offsetTop = ele.offsetTop;
2.文档可视区高度
let clientHeight = document.documentElement.clientHeight;
3.滚动高度
let scrollTop = document.documentElement.scrollTop;
- 案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>懒加载原理</title>
</head>
<body>
<img src="./static/images/1.jpg" width="1000" alt="" data-src="./static/images/2.jpg">
<script>
// 视口高度
let clientHeight = document.documentElement.clientHeight;
// 滚动高度
const img = document.querySelector('img');
console.log(img);
let scrollTop = document.documentElement.scrollTop;
window.onscroll = ev => {
if (img.offsetTop < clientHeight + document.documentElement.scrollTop) {
img.src = img.dataset.src;
}
}
</script>
</body>
</html>
3.4 轮播图片
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轮播图片</title>
<style>
/* 初始化 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
text-decoration: none;
}
/* 轮播图的容器 */
.container {
width: 62.5em;
height: 22em;
margin: 1em auto;
/* 转为定位元素/定位父级 */
position: relative;
}
/* 图片组 */
.container>.imgs img {
width: 100%;
height: 100%;
/* 默认全部隐藏 */
display: none;
/* 将所有的图片进行绝对定位,确保每一次只看到一张,所有图片共享这个容器 */
position: absolute;
left: 0;
top: 0;
}
/* 设置默认显示的图片(第一张) */
.container>.imgs img.active {
display: block;
}
/* 按钮组(独立按钮) */
.container>.btns {
position: absolute;
left: 0;
right: 0;
bottom: 0;
/* 水平居中 */
text-align: center;
}
.container>.btns a {
/* 转成行内块元素: 即能水平排列,双支持宽度设置 */
display: inline-block;
padding: 0.5em;
margin: 0 0.2em;
background-color: #fff;
border-radius: 50%;
}
.container>.btns a.active {
background-color: #000;
}
/* 翻页按钮 */
.container .skip a {
position: absolute;
width: 2.5rem;
height: 5rem;
line-height: 5rem;
text-align: center;
opacity: 0.3;
top: 9rem;
font-weight: lighter;
font-size: 2rem;
background-color: #ccc;
}
.container .skip .prev {
left: 0;
}
.container .skip .next {
right: 0;
}
.container .skip *:hover {
opacity: 0.6;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<!-- 1. 图片组 -->
<nav class="imgs">
<a href=""><img src="./static/images/banner/banner1.jpg" alt="" data-index="1" class="active" /></a>
<a href=""><img src="./static/images/banner/banner2.jpg" alt="" data-index="2" /></a>
<a href=""><img src="./static/images/banner/banner3.jpg" alt="" data-index="3" /></a>
<a href=""><img src="./static/images/banner/banner4.jpg" alt="" data-index="4" /></a>
</nav>
<!-- 2. 图片小按钮 -->
<nav class="btns">
</nav>
<!-- 3. 翻页 -->
<nav class="skip">
<a href="#" class="prev"><</a>
<a href="#" class="next">></a>
</nav>
</div>
<script src="./static/js/banner.js"></script>
</body>
</html>
JS代码
// 获取所有图片
const imgs = document.querySelectorAll('.container .imgs img');
console.log(imgs);
// 获取所有的按钮
const btnsGroup = document.querySelector('.container > .btns');
// 翻页按钮
const skips = document.querySelectorAll('.container > .skip');
console.log(skips);
// 实际上,轮播图片中的小圆点是根据轮播图片的数量来进行创建的,图片数量 === 圆点数量
// 创建一个文档片段,将生成的小圆点一次性添加到文档中。
const frag = document.createDocumentFragment();
for (let i = 0; i < imgs.length; i++) {
const a = document.createElement('a');
a.href = "#";
a.dataset.index = i + 1;
// 默认激活第一个元素
i === 0 ? a.classList.add('active') : '';
frag.appendChild(a);
}
btnsGroup.appendChild(frag);
/**
* 获取激活的图片
* @param {obj} ele 元素对象
*/
function getActiveEle (ele) {
let active = [...ele].filter(img => img.classList.contains('active'))
return active.shift();
}
console.log(getActiveEle(imgs));
// 获取刚刚生成的小按钮
const btns = document.querySelectorAll('.container .btns > *');
console.log(btns);
// 1.获取激活的元素
btns.forEach(btn => {
btn.addEventListener('click', ev => setActiveEle(ev.target));
});
/**
* 设置激活的元素active
* @param {obj} ele 元素
*/
function setActiveEle(ele) {
[imgs, btns].forEach(item => {
// 获取激活状态的图片和圆点按钮
let activeEle = [...item].filter(ele => ele.classList.contains('active')).shift();
// 清除所有的active状态的元素
activeEle.classList.remove('active');
console.log(activeEle);
item.forEach(arr => {
console.log(arr);
if (arr.dataset.index === ele.dataset.index) {
arr.classList.add('active');
}
});
});
}
// 获取翻页按钮
const prev = document.querySelector('.prev');
const next = document.querySelector('.next');
prev.addEventListener('click', ev => {
let activeEle = [...imgs].filter(item => item.classList.contains('active'));
console.log(typeof activeEle[0].dataset.index); //
// 获取当前激活元素的索引值
let activeElemIndex = activeEle[0].dataset.index;
console.log("1.当前索引值为:", activeElemIndex);
// 如果当前的激活元素的索引值(index)为1,那么图片就是第一张,再次点击prev按钮,会调到最后一张图
if (activeElemIndex === '1') {
console.log('1111');
setActiveEle(Array.from(imgs)[imgs.length - 1]);
activeElemIndex = [...imgs].filter(item => item.classList.contains('active'))[0].dataset.index;
console.log("2.当前索引值为:", activeElemIndex);
} else {
setActiveEle(Array.from(imgs)[activeElemIndex - 2]);
}
});
// 如果当图片处于最后一张时,点击next按钮应该调到第一张,即:激活第一张图
next.addEventListener('click', ev => {
// 获取当前激活的图片
let activeEle = [...imgs].filter(img => img.classList.contains('active'));
// 获取当前图片的索引值
if (Number(getActiveEleIndex(activeEle)) === imgs.length) {
setActiveEle(Array.from(imgs)[0]);
} else {
setActiveEle(Array.from(imgs)[getActiveEleIndex(activeEle)]);
}
});
/**
* 获取激活状态下的元素的索引值(index)
* @param {obj} ele 处于激活状态下的元素
*/
function getActiveEleIndex(ele) {
return ele[0].dataset.index;
}
- 效果图