购物车——js小项目实例

发布于:2022-12-24 ⋅ 阅读:(485) ⋅ 点赞:(0)


短短几周,js阶段的学习已然结束,结课作业就是实现一个购物车功能

功能:

1、可以添加商品
2、全选按钮
3、合计
4、数量增减

以上。

先看看成品图
在这里插入图片描述









🍁自在飞花轻似梦,无边丝雨细如愁。 —— 浣溪沙·漠漠轻寒上小楼🍁

首先构思好整体样式

在这里插入图片描述

HTML

<!-- 作业专用html框架 -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>购物车</title>
    <link rel="stylesheet" href="./css/reset.css">
    <link rel="stylesheet" href="./css/index.css">
    <link rel="shortcut icon" href="/icon/icon.png" type="image/x-icon">
</head>

<body>
    <div id="app">
        <div class="framework">
            <header>
                <nav>
                    <ul class="headbar">
                        <li><span>/</span><a href="#">我的购物车</a>/</li>
                    </ul>
                </nav>
            </header>
            <main>
                <div class="container">
                    <div class="title">
                        <span id="realname">商品名称:</span>
                        <input type="text" id="productName" value="">
                        <span>商品价格:</span>
                        <!-- 正则表达式,只能输入数字,且为正值 -->
                        <input type="text" id="productPrice" pattern="[0-9]*">
                        <button id="cartBtn">加入购物车</button>
                    </div>
                    <div class="content">
                        <ul>
                            <li class="listTitle">
                                <input type="checkbox" class="checkbox" id="allCombo">
                                <!-- 全选按钮,让其脱离文档流 -->
                                <span class="float">全选按钮</span>
                                <span>商品名称</span>
                                <span>商品价格</span>
                                <span>数量</span>
                                <span>操作</span>
                            </li>
                        </ul>
                        <ul id="contentBox">

                        </ul>
                        <!-- 添加 -->
                    </div>
                    <div class="info">
                        <span class="allProducts"></span><button id="buttonClear">清空购物车</button>
                    </div>
                </div>
            </main>
            <footer>
            </footer>
        </div>
    </div>
</body>
<script src="./js/index.js"></script>

</html>

CSS

body {
    background-color: rgb(41, 42, 45);
}

header {
    width: 100%;
    height: 50px;
    background-color: rgb(240, 240, 240);
    position: fixed;
    top: 0;
}
.headbar{
    float: right;
    height: inherit;
    line-height: 50px;
    margin-right: 40px;
}
.headbar li a{
    font-size: 14px;
    color: rgb(54,54,55);
    margin: 0 20px;
}
main {
    margin: 0 auto;
    width: 1200px;
    min-height: 100vh;
    background-color: rgb(234, 67, 53);
    padding: 120px 0;
}

.title {
    background-color: rgba(255,255,255,.85);
    margin: 0 20px 25px;
    padding: 15px 10px;
    font-size: 20px;
    line-height: 45px;
    border-radius: 10px;
}

.title input {
    width: 300px;
    height: 45px;
    vertical-align: middle;
}

.title button {
    width: 180px;
    height: 40px;
    border: none;
    font-size: 18px;
    vertical-align: middle;
    background-color: rgb(221, 221, 221);
}

.content,
.info {
    background-color: #fff;
    margin: 0 20px 15px;
    padding: 15px 10px;
    font-size: 20px;
    position: relative;
    border-radius: 10px;
    background-color: rgba(255,255,255,.85);
}

.info {
    position: fixed;
    bottom: 20px;
    width: 1155px;
}

.float {
    position: absolute;
    left: 150px;
}

.content li {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
    vertical-align: middle;
    border-bottom: 1px solid rgb(204, 204, 204);
    padding: 15px 0;
    line-height: 33px;
}

li input,
#checkbox {
    height: 14px;
    margin: auto 0;
    cursor: pointer;

}

#del {
    cursor: pointer;
}

li span {
    text-align: center;
}

.singleNum {
    outline: none;
    width: 80px;
    height: 25px;
    vertical-align: middle;
    margin: 0 5px;
}

#minus,
#plus {
    width: 20px;
    height: 20px;
    font-size: 18px;
    border: none;
    background-color: inherit;
    cursor: pointer;
}


.info button {
    border: none;
    font-size: 20px;
    background-color: inherit;
}

input:invalid {
    animation: floa .4s;
}

@keyframes floa {
    0% {
        transform: translateX(0px);
        background-color: inherit;
    }

    20% {
        transform: translateX(20px);
        background-color: red;
    }

    40% {
        transform: translateX(0px);
        background-color: red;
    }

    60% {
        transform: translateX(-20px);
        background-color: red;
    }

    80% {
        transform: translateX(0px);
        background-color: red;
    }

    100% {
        transform: translateX(0px);
        background-color: inherit;
    }
}

这样一个静态页面就算完成了

接下来,小生要进行页面逻辑的编写了

新建一个类
class cart {
	
}
var cartObj = new cart();
在类中添加数据,事件函数,功能函数,变量合集
class cart {
	    goods = [];
}
//将原型引用到实例
var cartObj = new cart();

小生先在Goods中添加一些数据,方便测试开大

class cart {
	    goods = [
			{id:1,realname:'iPhone14 Pro Max',price:9999,num:1,checked:false},
			{id:2,realname:'iPhone14 Pro',price:8999,num:1,checked:false},
			{id:3,realname:'iPhone14 Max',price:6799,num:1,checked:false},
			{id:4,realname:'iPhone14 Pro Max',price:5999,num:1,checked:false},
	];
}
var cartObj = new cart();
然后是将数据渲染到页面里,这里小生是遍历一遍数据,然后用字符串拼接,将数据加到页面中
    // 渲染到页面
    render() {
        var str = ''
        for (let i = 0; i < this.goods.length; i++) {
            str = str + '<li id="' + this.goods[i].id + '">';
            str = str + '<input type="checkbox" name="" id="checkbox" ' + (this.goods[i].checked ? 'checked' : '') + '>';
            str = str + '<span>' + this.goods[i].realname + '</span>';
            str = str + '<span><em id="singlePrice">' + this.goods[i].price + '</em> 元</span>';
            str = str + '<span><button id="minus">-</button><input class="singleNum" readonly="" id="singleNum" type="text" value="' + this.goods[i].num + '"><button id="plus">+</button></span>';
            str = str + '<span ' + 'id="del" ' + '>删除</span>';
            str = str + '</li>';
        }
        var str2 = ''
        str2 = str2 + '共计' + this.inTotal() + '种商品,合计' + this.inTotalSum() + '元。';


        this.contentBox.innerHTML = str
        this.allProducts.innerHTML = str2

        localStorage.setItem('goods', JSON.stringify(this.goods));
    }

在这里插入图片描述

接下来就是实现一些基础功能

添加点击事件

🍁事件委托

对于“对于事件处理程序过多”,问题的解决方案就是事件委托。事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的事件。
使用事件委托,只需要在DOM树中尽量最高的层次上添加一个事件处理程序

例如接下来的点击事件,有增减数量按钮,删除按钮,选中按钮,全选按钮

小生习惯将事件函数都归类在一起。

首先建立一个事件函数

    addevent() {
    //用来添加点击事件
    };

给购物车按钮添加点击事件

要注意this的指向,弄明白闭包的思想

   addevent() {
        var _this = this;//闭包
        //购物车按钮添加点击事件
        this.cartBtn.addEventListener('click', function () {
            if (!isNaN(_this.productPrice.value) && _this.productPrice.value > 0) {
                _this.add()//执行添加函数
            }
        });
  };

添加全选按钮事件

   addevent() {
		//其他代码
       	// 全选事件
        this.allCombo.addEventListener('click', function (e) {
            _this.selectAll();
        });
  };

接下来是内容区的点击事件,由于比较多,就可以用委派的思想

通过 e.target.id,再配合switch,很方便的实现委派思想

   addevent() {
   //其他代码
        this.contentBox.addEventListener('click', function (e) {
            switch (e.target.id) {
                // 单击删除按钮
                case 'del':
                    console.log('del', e.target.parentNode.id)
                    _this.del(e.target.parentNode.id);
                    break;
                //全选对应全选
                case 'checkbox':
                    // console.log('dian',e.target.parentNode.id);
                    _this.exchangeCheckbox(e.target.parentNode.id);
                    break;
                //减
                case 'minus':
                    console.log('dian', e.target.parentNode.parentNode.id);
                    _this.decrease(e.target.parentNode.parentNode.id);
                    break;
                case 'plus':
                    console.log('dian', e.target);
                    _this.increase(e.target.parentNode.parentNode.id);
                    break;
                // case 'singleNum':
                //     console.log(e.target.onblur)
                //     break;
                default:
                    break;
            }
        });
  };

🍁接下来就是各种函数的书写

添加数据

    //添加数据
    add() {
        var cache = {
            id: new Date().getTime() + String(Math.floor(Math.random() * 10)),
            realname: this.productName.value,
            price: this.productPrice.value,
            num: 1,
            checked: this.allCombo.checked,
        }
        this.goods.unshift(cache);
        this.render();
    }

删除数据函数

    //删除函数
    del(id) {
        for (let i = 0; i < this.goods.length; i++) {
            if (id == this.goods[i].id) {
                this.goods.splice(i, 1)
            }
        }
        this.render();
    };

清空购物车函数

    // 清空函数
    clear() {
        this.goods = [];
        this.allCombo.checked = false;
        this.render();
    };

全选按钮函数

    //全选函数
    selectAll() {
        for (let i = 0; i < this.goods.length; i++) {
            this.goods[i].checked = allCombo.checked;
        }
        this.render();
    };
    //对应函数
    exchangeCheckbox(id) {
        for (let i = 0; i < this.goods.length; i++) {
            if (id == this.goods[i].id) {
                this.goods[i].checked = !this.goods[i].checked;
            }
        }
        let flag = true;
        for (let j = 0; j < this.goods.length; j++) {
            if (!this.goods[j].checked) {
                flag = false;
            }
            this.allCombo.checked = flag;
            this.render();
        }
    };

增减数量按钮

    // 减
    decrease(index) {
        for (let i = 0; i < this.goods.length; i++) {
            if (index == this.goods[i].id) {
                if (this.goods[i].num > 1) {
                    this.goods[i].num--;
                }
                console.log(this.goods[i].num);
            }
        }
        this.render();
    };
    increase(index) {
        for (let i = 0; i < this.goods.length; i++) {
            if (index == this.goods[i].id) {
                this.goods[i].num++;
                console.log(this.goods[i].num);
            }
        }
        this.render();
    };

计算合计

    // 计算价格
    inTotal() {
        var num = 0;
        for (let i = 0; i < this.goods.length; i++) {
            if (this.goods[i].checked) {
                num++;
            }
        }
        return num;
    };
    inTotalSum() {
        var sum = 0
        var sumAll = 0;
        for (let i = 0; i < this.goods.length; i++) {
            if (this.goods[i].checked) {
                sum = this.goods[i].price * this.goods[i].num;
            }
            sumAll += sum;
        }
        return sumAll;
    };

这样大部分业务逻辑就算完成了

还有些细节需要完成

添加构造函数,将数据存储在本地

class cart {
	//商品数据代码
    //构造函数
    constructor() {
        if (localStorage.getItem('goods')) {
            this.goods = JSON.parse(localStorage.getItem('goods'));
        }
        this.render(); //渲染
        this.addevent();//事件函数在运行
    }
	//事件代码
	//函数代码
}

在这里插入图片描述

最后附上完整js代码

class cart {
    //建立一个数组
    goods = [

    ];
    //构造函数
    constructor() {
        if (localStorage.getItem('goods')) {
            this.goods = JSON.parse(localStorage.getItem('goods'));
        }
        this.render(); //渲染
        this.addevent();//事件函数在运行
    }
    cartBtn = document.getElementById('cartBtn');//获取加入购物车的按钮
    productName = document.getElementById('productName');//获取页面中商品名称
    productPrice = document.getElementById('productPrice');//获取页面中商品价格
    contentBox = document.getElementById('contentBox');//获取页面中内容的盒子
    buttonClear = document.getElementById('buttonClear');// 获取页面中清空购物车的盒子
    allProducts = document.querySelector('.allProducts')//获取页面中的统计信息
    allCombo = document.getElementById('allCombo');// 全选按钮

    //事件归类
    addevent() {
        var _this = this;//闭包
        //购物车按钮添加点击事件
        this.cartBtn.addEventListener('click', function () {
            if (!isNaN(_this.productPrice.value) && _this.productPrice.value > 0) {
                _this.add()//执行添加函数
            }
        });
        // 全选事件
        this.allCombo.addEventListener('click', function (e) {
            // console.log(e.target)
            _this.selectAll();
        });
        this.contentBox.addEventListener('click', function (e) {
            switch (e.target.id) {
                // 单击删除按钮
                case 'del':
                    console.log('del', e.target.parentNode.id)
                    _this.del(e.target.parentNode.id);
                    break;
                //全选对应全选
                case 'checkbox':
                    // console.log('dian',e.target.parentNode.id);
                    _this.exchangeCheckbox(e.target.parentNode.id);
                    break;
                //减
                case 'minus':
                    console.log('dian', e.target.parentNode.parentNode.id);
                    _this.decrease(e.target.parentNode.parentNode.id);
                    break;
                case 'plus':
                    console.log('dian', e.target);
                    _this.increase(e.target.parentNode.parentNode.id);
                    break;
                // case 'singleNum':
                //     console.log(e.target.onblur)
                //     break;
                default:
                    break;
            }
        });



        //点击清空购物车
        this.buttonClear.addEventListener('click', function () {
            _this.clear();
        });
    };


    //添加数据

    add() {
        var cache = {
            id: new Date().getTime() + String(Math.floor(Math.random() * 10)),
            realname: this.productName.value,
            price: this.productPrice.value,
            num: 1,
            checked: this.allCombo.checked,
        }
        this.goods.unshift(cache);
        this.render();
    }
    //删除函数
    del(id) {
        for (let i = 0; i < this.goods.length; i++) {
            if (id == this.goods[i].id) {
                this.goods.splice(i, 1)
            }
        }
        this.render();
    };
    // 清空函数
    clear() {
        this.goods = [];
        this.allCombo.checked = false;
        this.render();
    };
    //全选函数
    selectAll() {
        for (let i = 0; i < this.goods.length; i++) {
            this.goods[i].checked = allCombo.checked;
        }
        this.render();
    };
    //对应函数
    exchangeCheckbox(id) {
        for (let i = 0; i < this.goods.length; i++) {
            if (id == this.goods[i].id) {
                this.goods[i].checked = !this.goods[i].checked;
            }
        }
        let flag = true;
        for (let j = 0; j < this.goods.length; j++) {
            if (!this.goods[j].checked) {
                flag = false;
            }
            this.allCombo.checked = flag;
            this.render();
        }
    };
    // 减
    decrease(index) {
        for (let i = 0; i < this.goods.length; i++) {
            if (index == this.goods[i].id) {
                if (this.goods[i].num > 1) {
                    this.goods[i].num--;
                }
                console.log(this.goods[i].num);
            }
        }
        this.render();
    };
    increase(index) {
        for (let i = 0; i < this.goods.length; i++) {
            if (index == this.goods[i].id) {
                this.goods[i].num++;
                console.log(this.goods[i].num);
            }
        }
        this.render();
    };
    // 计算价格
    inTotal() {
        var num = 0;
        for (let i = 0; i < this.goods.length; i++) {
            if (this.goods[i].checked) {
                num++;
            }
        }
        return num;
    };
    inTotalSum() {
        var sum = 0
        var sumAll = 0;
        for (let i = 0; i < this.goods.length; i++) {
            if (this.goods[i].checked) {
                sum = this.goods[i].price * this.goods[i].num;
            }
            sumAll += sum;
        }
        return sumAll;
    };






    
    // 渲染到页面
    render() {
        var str = ''
        for (let i = 0; i < this.goods.length; i++) {
            str = str + '<li id="' + this.goods[i].id + '">';
            str = str + '<input type="checkbox" name="" id="checkbox" ' + (this.goods[i].checked ? 'checked' : '') + '>';
            str = str + '<span>' + this.goods[i].realname + '</span>';
            str = str + '<span><em id="singlePrice">' + this.goods[i].price + '</em> 元</span>';
            str = str + '<span><button id="minus">-</button><input class="singleNum" readonly="" id="singleNum" type="text" value="' + this.goods[i].num + '"><button id="plus">+</button></span>';
            str = str + '<span ' + 'id="del" ' + '>删除</span>';
            str = str + '</li>';
        }
        var str2 = ''
        str2 = str2 + '共计' + this.inTotal() + '种商品,合计' + this.inTotalSum() + '元。';


        this.contentBox.innerHTML = str
        this.allProducts.innerHTML = str2

        localStorage.setItem('goods', JSON.stringify(this.goods));
    }
}


var cartObj = new cart();
本文含有隐藏内容,请 开通VIP 后查看