轮播图示例及购物车示例
轮播图
html部分
<div class="container">
<!-- 1. 图片组 -->
<div class="img-group"></div>
<!-- 2. 图片中下部的小按钮 -->
<div class="btn-group"></div>
<!-- 3. 翻页 -->
<div class="skip">
<a class="prev"><</a>
<a class="next">></a>
</div>
</div>
css部分
/* 初始化 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
text-decoration: none;
}
/* 轮播图的容器 */
.container {
width: 62.5em;
height: 22em;
margin: 1em auto;
/* 转为定位元素/定位父级 */
position: relative;
}
.container:hover {
cursor: pointer;
}
/* 图片组 */
.container > .img-group img {
width: 100%;
height: 100%;
border-radius: 0.6em;
/* 默认全部隐藏 */
display: none;
/* 将所有的图片进行绝对定位,确保每一次只看到一张,所有图片共享这个容器 */
position: absolute;
left: 0;
top: 0;
}
/* 设置默认显示的图片(第一张) */
.container > .img-group img.active {
display: block;
}
/* 按钮组(独立按钮) */
.container > .btn-group {
position: absolute;
left: 0;
right: 0;
bottom: 1rem;
/* 水平居中 */
text-align: center;
}
.container > .btn-group span {
/* 转成行内块元素: 即能水平排列,双支持宽度设置 */
display: inline-block;
padding: 0.5em;
margin: 0 0.2em;
background-color: #fff;
border-radius: 50%;
}
.container > .btn-group span:hover {
cursor: pointer;
}
.container > .btn-group .active {
background-color: #10d1f3;
}
/* 翻页按钮 */
.container .skip a {
position: absolute;
width: 2.5rem;
height: 2.5rem;
line-height: 2.5rem;
border-radius: 50%;
text-align: center;
opacity: 0.3;
top: 9rem;
font-weight: lighter;
font-size: 2rem;
background-color: #787373;
}
.container .skip .prev {
left: 1rem;
}
.container .skip .next {
right: 1rem;
}
.container .skip *:hover {
opacity: 0.6;
color: #666;
}
js部分
class Swiper {
constructor(imgs, code, imgcode, btncode) {
this.imgs = imgs;
this.code = code;
this.imgcode = imgcode;
this.btncode = btncode;
this.next = null;
this.prev = null;
this.timer = null;
this.clickEvent = new Event("click");
}
init() {
this.next = document.querySelector(".skip .next");
this.prev = document.querySelector(".skip .prev");
this.next.addEventListener('click', ()=>this.nextImg());
this.prev.addEventListener('click', ()=>this.prevImg());
// 1. 生成轮播图所有图片
this.createImgs(this.imgcode, this.imgs.length);
// 2. 生成与轮播图数量对应的小按钮
this.createBtns(this.btncode, this.imgs.length);
this.autoPlay();
// 鼠标移入时停止自动播放,移出时启动自动播放
this.code.addEventListener("mouseover", ()=>this.stopPlay());
this.code.addEventListener("mouseout", ()=>this.autoPlay());
}
// 生成图片
createImgs(parent, length) {
// 正确的做法, 应该是将所有图片,先在内存中创建,然后再统一的插入到页面中, 这样就只需要渲染一次dom一次
// 文档片断元素
const frag = document.createDocumentFragment();
for (let i = 0; i < length; i++) {
const img = document.createElement("img");
img.src = this.imgs[i];
img.alt = `banner${i + 1}`;
// 为每一张图片添加一个自定义属性"data-index", 用它与小按钮进行绑定
img.dataset.index = `${i + 1}`;
if (i === 0) img.classList.add("active");
// 内存中执行了四次
frag.append(img);
}
// 页面中只渲染了一次,效率杠杠的
parent.append(frag);
}
// 生成按钮
createBtns(parent, length) {
const frag = document.createDocumentFragment();
for (let i = 0; i < length; i++) {
const span = document.createElement("span");
span.dataset.index = `${i + 1}`;
if (i === 0) span.classList.add("active");
// 给新生成的按钮,添加点击事件,用来切换图片
span.onclick = ev => this.showImgs(ev);
frag.append(span);
}
parent.append(frag);
}
// 按钮事件
showImgs(ev) {
// 1. 获取所有图片和按钮
const imgArr = this.imgcode.querySelectorAll("img");
const btnArr = this.btncode.querySelectorAll("span");
// 2. 因为我们要根据用户的点击确定当前应该显示哪一个,所以应该将之前的激活全部取消掉
// 但是我们又不知道当前是哪个处于激活状态, 全部过一遍
// btnArr.forEach(item => {
// if (item.classList.contains("active")) item.classList.remove("active");
// });
// imgArr.forEach(item => {
// if (item.classList.contains("active")) item.classList.remove("active");
// });
// 将上面二段代码合并
[btnArr, imgArr].forEach(items => {
items.forEach(item => {
if (item.classList.contains("active")) item.classList.remove("active");
});
// 3. 再给当前正在点击的按钮添加激活,然后再根据当前激活的按钮确定应该显示哪一张图片
ev.target.classList.add("active");
imgArr.forEach(img => {
// 这张应该显示的图片的data-index应该与按钮 的data-index相等,就显示出来
if (ev.target.dataset.index === img.dataset.index) img.classList.add("active");
});
});
}
// 翻页事件
// 向前翻页
prevImg() {
//1. 当前图片和当前的按钮
const currentImg = this.imgcode.querySelector("img.active");
const currentBtn = this.btncode.querySelector("span.active");
// 2. 去掉当前图片和按钮的激活样式
currentImg.classList.remove("active");
currentBtn.classList.remove("active");
// 3. 获取当前图片和按钮的前一个兄弟元素
const prevImg = currentImg.previousElementSibling;
const prevBtn = currentBtn.previousElementSibling;
// 4. 判断,如果存在前一张图片,就设置为激活
if (prevImg !== null && prevBtn !== null) {
prevImg.classList.add("active");
prevBtn.classList.add("active");
} else {
// 将最后一个图片设置为激活显示,实现循环显示
this.imgcode.lastElementChild.classList.add("active");
this.btncode.lastElementChild.classList.add("active");
}
}
// 向后翻页
nextImg() {
//1. 当前图片和当前的按钮
const currentImg = this.imgcode.querySelector("img.active");
const currentBtn = this.btncode.querySelector("span.active");
// 2. 去掉当前图片和按钮的激活样式
currentImg.classList.remove("active");
currentBtn.classList.remove("active");
// 3. 获取当前图片和按钮的前一个兄弟元素
const nextImg = currentImg.nextElementSibling;
const nextBtn = currentBtn.nextElementSibling;
// 4. 判断,如果存在前一张图片,就设置为激活
if (nextImg !== null && nextBtn !== null) {
nextImg.classList.add("active");
nextBtn.classList.add("active");
} else {
// 将最后一个图片设置为激活显示,实现循环显示
this.imgcode.firstElementChild.classList.add("active");
this.btncode.firstElementChild.classList.add("active");
}
}
// 自动播放
autoPlay() {
const self = this;
// ev: 事件对象,在方法总是有效的
this.timer = setInterval(() => {
// console.log(this.next);
this.next.dispatchEvent(this.clickEvent, self.nextImg);
}, 2000);
}
// 自动停止
stopPlay() {
clearInterval(this.timer);
}
}
const imgs = ["images/banner_1.jpg", "images/banner_2.jpg", "images/banner_3.jpg", "images/banner_4.jpg"];
const container = document.querySelector(".container");
// 图片组
const imgGroup = document.querySelector(".container > .img-group");
// 按钮组
const btnGroup = document.querySelector(".container > .btn-group");
// 提供4个参数: 图片数组、轮播图盒子节点元素、图片组盒子节点元素、轮播图底部按钮节点
const swiper = new Swiper(imgs, container, imgGroup, btnGroup);
window.onload = () => swiper.init();
成品效果
购物车
html部分
<table>
<caption>
我的购物车
</caption>
<thead>
<th><input type="checkbox" name="checkAll" id="check-all" checked /><label for="check-all">全选</label></th>
<th>图片</th>
<th>品名</th>
<th>单位</th>
<th>单价/元</th>
<th>数量</th>
<th>金额/元</th>
</thead>
<tbody>
<tr>
<td>
<input type="checkbox" name="item" class="item" value="SN-1020" checked />
</td>
<td>
<a href=""><img src="images/p1.png" alt="" /></a>
</td>
<td>JavaScript权威指南(第七版)</td>
<td>本</td>
<td class="price">100</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">0</td>
</tr>
<tr>
<td>
<input type="checkbox" name="item" class="item" value="SN-1020" checked />
</td>
<td>
<a href=""><img src="images/p2.png" alt="" /></a>
</td>
<td>JavaScript高级程序设计(第四版)</td>
<td>本</td>
<td class="price">129</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">0</td>
</tr>
<tr>
<td>
<input type="checkbox" name="item" class="item" value="SN-1030" checked />
</td>
<td>
<a href=""><img src="images/p3.png" alt="" /></a>
</td>
<td>JavaScript忍者秘籍(第二版)</td>
<td>台</td>
<td class="price">99</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">0</td>
</tr>
<tr>
<td>
<input type="checkbox" name="item" class="item" value="SN-1040" checked />
</td>
<td>
<a href=""><img src="images/p4.png" alt="" /></a>
</td>
<td>ThinkPad X1 Carbon 2021</td>
<td>台</td>
<td class="price">12999</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">0</td>
</tr>
<tr>
<td>
<input type="checkbox" name="item" class="item" value="SN-1050" checked />
</td>
<td>
<a href=""><img src="images/p5.png" alt="" /></a>
</td>
<td>MacBook Pro 16 10代i7 16G 512G</td>
<td>台</td>
<td class="price">23800</td>
<td><input type="number" min="1" value="1" /></td>
<td class="amount">0</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">总计:</td>
<td id="sum">0</td>
<td id="total-amount">0</td>
</tr>
</tfoot>
</table>
css部分
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 10px;
}
box {
font-size: 1.6rem;
}
table {
border-collapse: collapse;
width: 90%;
text-align: center;
margin: 1rem auto;
color: #666;
}
table caption {
margin-bottom: 1rem;
font-size: 2rem;
}
table th,
table td {
padding: 0.5rem;
font-weight: normal;
}
table thead tr:first-of-type {
background-color: rgb(26, 187, 187);
height: 3rem;
color: white;
}
table thead tr:first-of-type:hover {
opacity: 0.8;
cursor: pointer;
}
table input[type="checkbox"] {
width: 1rem;
height: 1rem;
}
/* table tr:nth-of-type(even) {
background-color: #eee;
} */
table tbody tr:hover {
background-color: lightcyan;
transition: 0.5s;
cursor: pointer;
}
table input[type="number"] {
height: 2em;
width: 4em;
border: none;
border-bottom: 1px solid;
outline: none;
text-align: center;
}
/* table input[type="number"]:focus {
background-color: rgb(26, 187, 187);
} */
tbody img {
width: 3em;
transition: all 0.6s;
cursor: pointer;
}
tbody img:hover {
transform: scale(4);
}
tfoot tr {
height: 3rem;
color: coral;
background-color: lightcyan;
}
button {
width: 15rem;
height: 2rem;
outline: none;
border: none;
background-color: rgb(26, 187, 187);
color: white;
letter-spacing: 5px;
}
button:hover {
background-color: coral;
transition: 0.2s;
cursor: pointer;
}
@media screen and (min-width: 400px) {
html {
font-size: 12px;
}
}
@media screen and (min-width: 600px) {
html {
font-size: 14px;
}
}
@media screen and (min-width: 800px) {
html {
font-size: 16px;
}
}
js部分
// 功能一: 完成全选和全不选功能
// 1. 全选复选框
const checkAll = document.querySelector("#check-all");
// 2. 每个商品的复选框
const checkItems = document.getElementsByName("item");
// ==========================================================
// 功能二: 自动计算
// 分析: 所有的计算,都是基于"数量的变化", 第一步就要获取到所有商品数量控件
const numInput = document.querySelectorAll('input[type="number"]');
// 给每一个数量控件绑定一个change事件,进行监听它的变量
// 当控件的值发生变化时, 自动进行重新 计算
numInput.forEach(input => (input.onchange = autoTotal));
// 当页面加载完成时,应该将自动计算函数执行一次(初始化)
window.onload = autoCalculate;
function autoCalculate() {
// 1. 获取每个商品的金额, 金额 = 数量 * 单价
// 数量, 当前有多个商品,所以应该返回一个由数量组成的集合/数组
const numbers = document.querySelectorAll('input[type="number"]');
// console.log(numbers);
// [...numbers].forEach(num => console.log(typeof parseInt(num.value)));
// [...numbers].forEach(num => console.log(num.value * 1));
//map()代替forEach: 因为forEach()没有返回值,map()功能与forEach一样的,但是有一个数组返回值
const numArr = [...numbers].map(num => num.value * 1);
// 获取单价组成的数组
const prices = document.querySelectorAll("tbody .price");
// console.log(prices);
const priceArr = [...prices].map(price => price.textContent * 1);
// console.log(numArr, priceArr);
// 每一个商品与单价的乘积是它的金额,有多个商品,所以应该返回 一个由"金额"组成的数组
// reduce()
const amountArr = [priceArr, numArr].reduce((prev, curr) => prev.map((item, key) => item * curr[key]));
// console.log(amountArr);
// 2. 商品总数
let sum = numArr.reduce((prev, curr) => prev + curr);
// 3. 所有商品总金额
let total = amountArr.reduce((prev, curr) => prev + curr);
// 4. 将以上的计算结果,渲染到页面中
// forEach(function(正在遍历的元素,该元素的索引,数组))
document.querySelectorAll(".amount").forEach((item, index) => (item.textContent = amountArr[index]));
document.querySelector("#sum").textContent = sum;
document.querySelector("#total-amount").textContent = total;
}
// 全选时
checkAll.onchange = (ev) => {
checkItems.forEach(item => (item.checked = ev.target.checked));
if (!ev.target.checked) {
document.querySelector("#sum").textContent = 0;
document.querySelector("#total-amount").textContent = 0;
} else {
autoCalculate()
}
}
// 选择某个商品时, 数量,总金额实时更新
function autoTotal() {
// 1. 获取每个商品的金额, 金额 = 数量 * 单价
// 数量, 当前有多个商品,所以应该返回一个由数量组成的集合/数组
const numbers = document.querySelectorAll('input[type="number"]');
const checkeds = [...checkItems].filter((item, idx) => {
if (item.checked) {
item.dataset.checkidx = idx;
return true;
}
return false;
}).map((item) => item.dataset.checkidx);
const numArr = [...numbers].filter((v, idx) => checkeds.includes(`${idx}`)).map(num => num.value * 1);
console.log([...numbers].filter((v, idx) => checkeds.includes(`${idx}`)));
// 获取单价组成的数组
const prices = document.querySelectorAll("tbody .price");
const priceArr = [...prices].filter((v, idx) => checkeds.includes(`${idx}`)).map(price => price.textContent * 1);
const amountArr = [priceArr, numArr].reduce((prev, curr) => prev.map((item, key) => item * curr[key]));
// 2. 商品总数
let sum = numArr.reduce((prev, curr) => prev + curr);
// 3. 所有商品总金额
let total = amountArr.reduce((prev, curr) => prev + curr);
// 4. 将以上的计算结果,渲染到页面中
document.querySelector("#sum").textContent = sum;
document.querySelector("#total-amount").textContent = total;
}
checkItems.forEach((item)=> item.onchange = ()=>{
// 全选复选框选中状态根据所有商品的复选框状态更改
checkAll.checked = [...checkItems].every(item => item.checked);
autoTotal();
});
成品效果
```