HTML+CSS+JS购物车案例

1,326 阅读5分钟

本文枯燥乏味,偏长,请耐心

案例的效果图

image.png

html部分 ...

<div class="header">页面顶部</div>
<div class="content">
	<!-- <div class="top">
		<input type="checkbox"> 全选
	</div>
	<ul>
		<li>
			<div class="status">
				<input type="checkbox">
			</div>
			<div class="show">
				<img src="" alt="">
			</div>
			<div class="title">
				asdhgaskjhdgkjasd
			</div>
			<div class="price">
				¥ 100.00
			</div>
			<div class="number">
				<button>-</button>
				<input type="text" value="1">
				<button>+</button>
			</div>
			<div class="sub">
				¥ 100.00
			</div>
			<div class="destory">
				<button>删除</button>
			</div>
		</li>
	</ul>
	<div class="bottom">
		<div class="totalNum">
			总件数 : 3
		</div>
		<div class="btns">
			<button>清空购物车</button>
			<button>去结算</button>
			<button>删除所有已选中</button>
		</div>
		<div class="totalPrice">
			总价格 : ¥ <span>100.00</span>
		</div>
	</div> -->
</div>
<div class="footer">页面底部</div>

<script src="./index.js"></script>

...

css部分 ...

* {
    margin: 0;
    padding: 0;
}

ul,
ol,
li {
    list-style: none;
}

.header,
.footer {
    width: 1200px;
    height: 100px;
    background-color: skyblue;
    color: #fff;
    font-size: 50px;
    display: flex;
    justify-content: center;
    align-items: center;
    margin: 0 auto;
}

.footer {
    height: 400px;
}

.content {
    width: 1200px;
    margin: 0 auto;
    padding: 10px 0;
}

.content > .top,
.content > .bottom {
    height: 50px;
    background-color: pink;
    display: flex;
    align-items: center;
}

.content > .bottom {
    justify-content: space-between;
    box-sizing: border-box;
    padding: 0 10px;
}

.content > .bottom > .totalPrice > span {
    font-size: 20px;
    color: red;
}

.content > .bottom > .btns > button {
    font-size: 18px;
    padding: 5px 10px;
    cursor: pointer;
}

.content > .top > input {
    width: 30px;
    height: 30px;
    margin: 0 15px 0 50px;
}

.content > ul {
    padding-top: 10px;
}

.content > ul > li {
    width: 100%;
    border: 1px solid #333;
    box-sizing: border-box;
    height: 100px;
    margin-bottom: 10px;

    display: flex;
}

.content > ul > li > div {
    display: flex;
    justify-content: center;
    align-items: center;
    border-right: 1px solid #333;
}

.content > ul > li > div:last-child {
    border: none;
}

.content > ul > li > .show,
.content > ul > li > .status {
    width: 100px;
}

.content > ul > li > .status > input {
    width: 30px;
    height: 30px;
}

.content > ul > li > .show > img {
    width: 100%;
    height: 100%;
    display: block;
}

.content > ul > li > .price,
.content > ul > li > .sub {
    width: 200px;
    color: red;
    font-size: 20px;
}

.content > ul > li > .title {
    width: 300px;
    align-items: flex-start;
    justify-content: flex-start;
    box-sizing: border-box;
    padding: 5px;
}

.content > ul > li > .number {
    width: 230px;
}

.content > ul > li > .number > input {
    width: 50px;
    height: 30px;
    text-align: center;
    margin: 0 5px;
    border: none;
    outline: none;
    font-size: 18px;
}

.content > ul > li > .number > button {
    width: 30px;
    height: 30px;
    cursor: pointer;
}

.content > ul > li > .destory {
    flex: 1;
}

.content > ul > li > .destory > button {
    padding: 5px;
    font-size: 18px;
    cursor: pointer;
}

...

js部分 ...

 数组数据分析
 *      1. id: 数据的唯一值
 *      2. status: true代表该商品被选中, false则为没被选中
 *      3. pic: 图片地址
 *      4. name: 商品名
 *      5. price: 价格
 *      6. number: 商品收藏数量
 *      7. total: 库存
 *
 *
 *  数据驱动视图
 *          套路:
 *              查: 查询数据, 渲染到页面
 *              增删改: 找到源数据, 然后对源数据做修改, 修改完成, 重新渲染页面
 *
 *
 *  1. 将通过数据重构页面
 *      1.1 查询数据, 渲染页面
 *  2. 全选
 *      2.1 选中全选按钮后, 根据全选按钮的选中状态, 修改所有商品的选中状态
 *      2.2 重新渲染视图
 *  3. 清空购物车
 *      3.1 清空商品数据
 *      3.2 重新渲染视图
 *  4. 结算 (将所选择商品总价计算出后打印到控制台)
 *      4.1 找到所有选中的商品
 *      4.2 计算所有选中商品各自的总价
 *      4.3 计算所有选中商品的总价之和
 *      4.4 打印到控制台
 *  5. 删除已选中
 *      5.1 在原数组中, 找到选中的商品, 然后删除
 *      5.2 重新渲染视图
 *  6. 商品数量调整
 *      6.1 找到对应的商品, 修改收藏数量
 *      6.2 重新渲染视图
 *  7. 选中商品
 *      7.1 找到对应的商品, 修改选中状态
 *      7.2 重新渲染视图
 *  8. 删除某一项
 *      8.1 找到对应商品, 将删除
 *      8.2 重新渲染视图
 * 9. 数据持久化 (浏览器关闭, 数据能保存)
 *      9.1 本地存储
 *
 *
 *  逻辑:
 *      1. 准备一个渲染函数
 *      2. 首次打开页面时 调用
 *      3. 在各种事件触发之后, 重新调用
 */

// 0. 模拟从后端请求回来的商品数据
// var cartList = [
//     // 每一个对象就是一个购物车内容的数据
//     {
//         id: 111234,
//         status: true,
//         pic: "https://img1.baidu.com/it/u=2511310783,721605137&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=332",
//         name: "我是一个手机, 不知道是啥",
//         price: 100,
//         number: 3,
//         total: 16,
//     },
//     {
//         id: 123456,
//         status: false,
//         pic: "https://img1.baidu.com/it/u=1537709578,2453227648&fm=253&fmt=auto&app=120&f=JPEG?w=809&h=500",
//         name: "我是一个电脑, 不知道是啥",
//         price: 98.72,
//         number: 1,
//         total: 7,
//     },
//     {
//         id: 965874,
//         status: true,
//         pic: "https://img2.baidu.com/it/u=3561506717,735421650&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500",
//         name: "我是一个手纸, 不知道是啥",
//         price: 356.21,
//         number: 2,
//         total: 22,
//     },
// ];

// 获取localStorage的数据时, 为了避免首次进入页面没有数据, 所以通过逻辑或给一个本地数据兜底
var cartList = JSON.parse(window.localStorage.getItem("cartList")) || [
    // 每一个对象就是一个购物车内容的数据
    {
        id: 111234,
        status: true,
        pic: "https://img1.baidu.com/it/u=2511310783,721605137&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=332",
        name: "我是一个手机, 不知道是啥",
        price: 100,
        number: 3,
        total: 16,
    },
    {
        id: 123456,
        status: false,
        pic: "https://img1.baidu.com/it/u=1537709578,2453227648&fm=253&fmt=auto&app=120&f=JPEG?w=809&h=500",
        name: "我是一个电脑, 不知道是啥",
        price: 98.72,
        number: 1,
        total: 7,
    },
    {
        id: 965874,
        status: true,
        pic: "https://img2.baidu.com/it/u=3561506717,735421650&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500",
        name: "我是一个手纸, 不知道是啥",
        price: 356.21,
        number: 2,
        total: 22,
    },
];

// 0. 获取元素
var content = document.querySelector(".content");

// 0. 准备变量

// 1. 准备渲染函数
function bindHtml() {
    var selctItem = 0; // 存储选中商品的数量
    var selctTotalNum = 0; // 存储选中商品的总数量
    var totalPrice = 0; // 存储选中商品的总价
    // 1.0 找到选中商品
    cartList.forEach(function (item) {
        if (item.status) {
            // console.log(item)
            selctItem++;
            selctTotalNum += item.number;
            totalPrice += item.number * item.price;
        }
    });

    // 1.1 查询数据, 渲染页面
    var str = `<div class="top">
        <input class="selcet_all" type="checkbox" ${
            // 选中的商品数量如果等于商品数量, 代表所有商品被选中
            selctItem === cartList.length ? "checked" : ""
        }> 全选
    </div>
    <ul>`;

    cartList.forEach(function (item) {
        str += `<li>
            <div class="status">
                <input data-id="${item.id}" class="item" type="checkbox" ${
            item.status ? "checked" : ""
        }>
            </div>
            <div class="show">
                <img src="${item.pic}" alt="">
            </div>
            <div class="title">
                ${item.name}
            </div>
            <div class="price">
                ¥ ${item.price.toFixed(2)}
            </div>
            <div class="number">
                <button class="sub_btn" data-id=${item.id}>-</button>
                <input type="text" value="${item.number}">
                <button class="add_btn" data-id="${item.id}">+</button>
            </div>
            <div class="sub">
                ¥ ${(item.number * item.price).toFixed(2)}
            </div>
            <div class="destory">
                <button data-id="${item.id}" class="del">删除</button>
            </div>
        </li>`;
    });

    str += `</ul>
    <div class="bottom">
        <div class="totalNum">
            总件数 : ${selctTotalNum}
        </div>
        <div class="btns">
            <button class="clear">清空购物车</button>
            <button class="go_pay" data-TotalPrice="${totalPrice}">去结算</button>
            <button class="del_item">删除所有已选中</button>
        </div>
        <div class="totalPrice">
            总价格 : ¥ <span>${totalPrice.toFixed(2)}</span>
        </div>
    </div>`;

    content.innerHTML = str;

    // 4. 将数据存储至 localStorage 一份, 以便下次进入是可以获取上一次的数据
    window.localStorage.setItem("cartList", JSON.stringify(cartList));
}

// 2. 首次打开页面, 调用渲染函数
bindHtml();

// 3. 利用事件冒泡, 将所有的事件委托给统一的父级
content.onclick = function (e) {
    // console.log(e.target.className)

    // 3.1 全选按钮事件
    if (e.target.className === "selcet_all") {
        // 3.1.1 选中全选按钮后, 根据全选按钮的选中状态, 修改所有商品的选中状态
        cartList.forEach(function (item) {
            item.status = e.target.checked;
        });
        // 3.1.2 重新渲染视图
        bindHtml();
    }

    // 3.2 清空购物车
    if (e.target.className === "clear") {
        var boo = confirm("请问您确定清空吗");
        if (boo) {
            // 3.2.1 清空购物车数据
            cartList = [];
            // 3.2.2 重新渲染页面
            bindHtml();
        }
    }

    // 3.3 结算 (将所选择商品总价计算出后打印到控制台)
    if (e.target.className === "go_pay") {
        // 3.3.1 在 bindHtml 渲染页面时, 将计算好的总价 放置在标签中, 此时只需要获取到标签上的属性, 打印即可
        console.log(e.target.dataset.totalprice);
    }

    // 3.4 删除已选中  (没有选中项时 禁止执行)
    if (e.target.className === "del_item") {
        var boo = confirm("请问您确定删除已选中吗");
        if (boo) {
            // 3.4.1 过滤数组, 只留下选中状态为 false 的
            cartList = cartList.filter(function (item) {
                return !item.status;
            });
            // 3.4.2 重新渲染视图
            bindHtml();
        }
    }

    // 3.5.1 减少商品数量
    if (e.target.className === "sub_btn") {
        // 3.5.1.1 减少商品数量, 不能为0
        cartList.forEach(function (item) {
            // 通过商品ID找到点击的是哪一个商品, 然后给对应商品的数量减1
            if (item.id == e.target.dataset.id && item.number >= 2) {
                item.number--;
            }
        });
        // 3.5.1.2 重新渲染视图
        bindHtml();
    }

    // 3.5.2 增加商品数量
    if (e.target.className === "add_btn") {
        // 3.5.2.1 增加商品数量
        cartList.forEach(function (item) {
            // 通过商品ID找到点击的是哪一个商品, 然后给对应商品的数量加1
            if (item.id == e.target.dataset.id && item.number < item.total) {
                item.number++;
            }
        });
        // 3.5.2.2 重新渲染视图
        bindHtml();
    }

    // 3.6 选中商品
    if (e.target.className === "item") {
        // 3.6.1 遍历数组找到要修改的商品
        cartList.forEach(function (item) {
            if (item.id == e.target.dataset.id) {
                // 修改商品的选中状态
                item.status = !item.status;
            }
        });
        // 3.6.2 重新渲染视图
        bindHtml();
    }

    // 3.7 删除某一项
    if (e.target.className === "del") {
        var boo = confirm("请问您确定删除当前项吗");
        // 3.7.0 询问用户是否需要删除
        if (!boo) return;
        // 3.7.1 遍历数组, 找到需要删除的项, 将其过滤掉
        cartList = cartList.filter(function (item) {
            return item.id != e.target.dataset.id;
        });
        // 3.7.2 重新渲染视图
        bindHtml();
    }
};

...