Correcting teacher:天蓬老师
Correction status:qualified
Teacher's comments:
本次简单选项卡主要是一个三选容器,当点击其中某个选项时,其他的选项会被覆盖。
html代如下:
<div class="tabs">
<!-- 导航 -->
<ul class="tab">
<li class="active" data-index="1">家事</li>
<li data-index="2">国事</li>
<li data-index="3">世事</li>
</ul>
<!-- details -->
<ul data-index="1" class="item active">
<li><a href="">腊月完婚好开心</a></li>
<li><a href="">开始小新婚生活</a></li>
<li><a href="">开始造人小计划</a></li>
</ul>
<ul data-index="2" class="item">
<li><a href="">疫情开始有转机</a></li>
<li><a href="">新年春运不停站</a></li>
<li><a href="">勤洗手讲究卫生</a></li>
</ul>
<ul data-index="3" class="item">
<li><a href="">特朗普离职演讲</a></li>
<li><a href="">多国疫情再变异</a></li>
<li><a href="">丰田扩中国市场</a></li>
</ul>
</div>
对应css代码如下:
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
color: #555;
text-decoration: none;
}
a:hover {
color: red;
text-decoration: underline;
}
li {
list-style: none;
line-height: 1.6rem;
}
li:hover {
cursor: default;
}
.tabs {
width: 300px;
height: 300px;
margin: 30px;
background-color: #e6e6e6;
display: flex;
flex-direction: column;
}
.tab {
display: flex;
height: 36px;
}
.tab li {
flex: auto;
text-align: center;
height: 36px;
background-color: #fff;
}
.tab li.active {
background-color: #e6e6e6;
}
.tab li:hover {
cursor: pointer;
}
/* 默认所有选项卡只有一个显示,其它隐藏 */
.item {
padding: 20px;
display: none;
}
/* .item和.active之间不能有空格 */
.item.active {
display: block;
}
</style>
js代码如下:
<script>
const tab = document.querySelector(".tab");
const items = document.querySelectorAll(".item");
tab.onclick = (ev) => {
// console.log(ev.target);
// console.log(ev.currentTarget);
//点中选项卡中任一个,先将所有选项卡active清空,然后再将找到触发对象设置active
[...tab.children].forEach((item) => item.classList.remove("active"));
ev.target.classList.add("active");
//联动操作选项卡对应的新闻列表
//同样要先清空所有item的active,通过ev触发对象data-index属性来找到对应item,然后添加active
items.forEach((item) => {
item.classList.remove("active");
});
//通过filter来找对应的新闻内容列表,添加active类属性
[...items]
.filter((item) => item.dataset.index === ev.target.dataset.index)[0]
.classList.add("active");
};
</script>
总体上的思路:设置选项卡与对应的新闻列表的某个属性一致,如代码中的 ul.tab.li 和 下面的ul.item都会有一个相同自定义属性data-index,选中ul.tab.li时,通过其data-index属性找到其他的ul并设置为不显示(display:none) ,剩下ul设置为display:block;
解析:
当遇到页面上需要加载特别多的图片或者视频文件时,我们希望加载页面时,不要一次性将所有文件渲染到页面中,这样会浪费较多计算机的资源,我们希望当文件进入到用户视野时,再渲染文件,这样不仅灵活,也能节省计算机资源。
两个概念:
可视区高度:打开浏览器,在没有滚动条时,能加载内容的最大高度。
滚动高度:下拉进度条离可视区顶部的距离
所以什么情况下,文件算是进入到用户的视野
文件的顶部距离 < 可视区刻度+滚动高度
示范代码:
//假设html代码中添加了80张图片,
<img src="images/temp.jpg" alt="" data-src="images/img-1.jpg" />
。。。
<img src="images/temp.jpg" alt="" data-src="images/img-80.jpg" />
js代码:
控制当文件的顶部距离<可视区高度+滚动高度时,用自定义属性data-src的属性值替代原有src属性值,同时为了不让图片显示比较突兀,还通过setTimeout来让图片在0.5秒后开始加载。由于高度数据在不断变化,所以需要给window添加监听器。
<script>
const imgs = document.querySelectorAll(".container img");
const clientHeight = document.documentElement.clientHeight;
window.addEventListener("scroll", layzyload);
// load:页面加载完成自动执行
window.addEventListener("load", layzyload);
function layzyload() {
// 滚动高度
let scrollTop = document.documentElement.scrollTop;
// 遍历图片并判断是否进入到了可视区
imgs.forEach((img) => {
// 只要当前图片距离文档顶部的偏移量,小于可视区高度与滚动高度之间则表示进入到可视区,应该显示出来
// clientHeight是可视区高度,这是一个固定的值,滚动高度是动态的
if (img.offsetTop < clientHeight + scrollTop) {
setTimeout(() => (img.src = img.dataset.src), 500);
}
});
}
</script>
我们希望实现的轮播图,如京东等购物网站首页
- 图片下方有一组小按钮,能够指定显示图上;
- 在图片的两侧分别有一个箭头,可以实现上一张和下一张浏览
- 鼠标事件,当鼠标移出轮播图区域时,图片以两秒的间隔进行轮播
html代码:
<div class="container" >
<!-- 1 图片组 -->
<nav class="imgs">
<img src="../../21/0111/banner/banner1.jpg" alt="" class="active" data-index="1" />
<img src="../../21/0111/banner/banner2.jpg" data-index="2" alt="" />
<img src="../../21/0111/banner/banner3.jpg" data-index="3" alt="" />
<img src="../../21/0111/banner/banner4.jpg" data-index="4" alt="" />
</nav>
<!-- 2 按钮组 -->
<nav class="btns">
<!-- 不直接定义按钮数量,根据图片数据生成 -->
</nav>
<!-- 3. 翻页 -->
<nav class="skip">
<a href="#" class="prev"><</a>
<a href="#" class="next">></a>
</nav>
</div>
css代码:
* {
margin: 0px;
padding: 0px;
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.active {
background-color: #000;
}
.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;
}
js代码:
<script>
// 所有图片
const imgs = document.querySelectorAll(".container > .imgs img");
// 按钮组
const btnGroup = document.querySelector(".container > .btns");
// 翻页按钮
const skip = document.querySelectorAll(".container > .skip > a");
// 方法1:获取图片数量生成对应的按钮组,遍历时每次插入一个a标签
// imgs.forEach((img, i = 0) => {
// let btnHtml = `<a href="#" data-index="${i + 1}"></a>`;
// if (i === 0)
// btnHtml = `<a href="#" class="active" data-index="${i + 1}"></a>`;
// console.log(btnHtml);
// btns.insertAdjacentHTML("beforeEnd", btnHtml);
// i++;
// });
// 方法2:函数,创建一个文件片断,先创建对应数量的a标签,再统一将文件片断添加到对应的btnGroup里面
function autoCreateBtns(ele, imgLength) {
const frag = document.createDocumentFragment();
for (let i = 0; i < imgLength; i++) {
let a = document.createElement("a");
a.href = "#";
a.dataset.index = i + 1;
if (i === 0) a.classList.add("active");
frag.appendChild(a);
}
ele.appendChild(frag);
}
// 调用函数,传入按钮组和图片数量到btnGroup
autoCreateBtns(btnGroup, imgs.length);
//思考:要求1中按钮组事件与要求2翻页按钮具有共性,假设具有active类属性的对象为显示状态,不具有active类属性的对象显示隐藏,那么当我们触发一个事件时,都需要将某一个集合的对象全部移除active属性,同时将需要将某个集合中的指定元素添加acitve类属性,所以这里可以写三个公共函数:
//全部移除active属性
function removeActive(ele){
[...ele].forEach((item) => item.classList.remove("active"));
}
//弹出集合中中具有active属性的对象
function getActiveEle(ele) {
let activities = [...ele].filter((item) =>
item.classList.contains("active")
);
return activities.shift();
}
//将指定元素Index添加active属性,其余去掉active属性
function setActiveEle2(ele, index) {
[...ele].forEach((item) => {
if (item.dataset.index == index) {
item.classList.add("active");
} else {
item.classList.remove("active");
}
});
//要求1:图片下方有一组小按钮,能够指定显示图上
btns.forEach((btn) =>
btn.addEventListener("click", (ev) => {
removeActiveEle([...btns]);
removeActiveEle([...imgs]);
setActiveEle2([...btns], ev.target.dataset.index);
setActiveEle2([...imgs], ev.target.dataset.index);
})
);
//要求2. 在图片的两侧分别有一个箭头,可以实现上一张和下一张浏览
//思路:当时没到尽头时,图片直接显示index-1就行,到尽头时,需要跳转到另一端的尽头
//简化:这里面都是非黑即白的判断,前一张或者后一张,到尽头还是没到尽头,可以用三元运算符来简化代码
skip.forEach((item) =>
item.addEventListener("click", (ev) => {
let previndex = getActiveEle(imgs).dataset.index - 1;
let nextindex = parseInt(getActiveEle(imgs).dataset.index) + 1;
if (ev.target.className == "prev") {
if (previndex != 0) {
//没到尽头,直接显示previndex
removeActiveEle([...btns]);
removeActiveEle([...imgs]);
setActiveEle2([...imgs], previndex);
setActiveEle2([...btns], previndex);
} else {
//到尽头,直接跳转另一端的尽头
removeActiveEle([...btns]);
removeActiveEle([...imgs]);
setActiveEle2([...imgs], 4);
setActiveEle2([...btns], 4);
}
} else if (ev.target.className == "next") {
if (nextindex != 5) {
//没到尽头,直接显示nextindex
removeActiveEle([...btns]);
removeActiveEle([...imgs]);
setActiveEle2([...imgs], nextindex);
setActiveEle2([...btns], nextindex);
} else if (nextindex == 5) {
//到尽头,直接跳转另一端的尽头
removeActiveEle([...btns]);
removeActiveEle([...imgs]);
setActiveEle2([...imgs], 1);
setActiveEle2([...btns], 1);
}
}
})
);
//要求3. 鼠标事件,当鼠标移出轮播图区域时,图片以两秒的间隔进行轮播
//思考:自动事件setInterval,图片第2秒跳转,类似于每2秒点击一次后一张的按钮事件
//定时器
let clock = null;
//定义点击事件类型
const clickEvent = new Event("click");
//获取容器区域
const container = document.qureySelector(".container");
//点击对象
const autonext = document.querySelector(".skip .next");
//鼠标移出就开始定时轮播图片,定义start方法定时方法
function start(){
clock = setInteval(function(){
//每2秒需要触发一次的事件类型为点击事件类型
autonext.dispatchEvent(clickEvent);
},2000);
}
//鼠标移出就停止定时器
function stop(){
clearInterval(clock);
}
//最后,将事件添加到容器对应的鼠标移出onmouseout和鼠标移入onmouerover事件监听器中
container.addEventListener("mouseout",stop);
container.addEventListener("mouseover",start);