您的位置:首页 > Web前端 > JavaScript

购物车 (JS面向对象实现)

2020-08-09 12:36 453 查看

效果图:
代码实现:

1.js文件

#####(1)商品列表模块

import Utils from "./Utils.js";
export default class GoodsItem {

//设置静态变量styleBool,用来控制多次循环,CSS只设置一次
static styleBool = false;
data; //将外部传入的数组的item数据赋给data属性
preIcon;  //miniicon内目标图片
constructor() {
this.elem = this.createElem();

//首先判断 循环创建的div是否添加CSS样式,当不存在时,执行添加CSS函数
if (!GoodsItem.styleBool) GoodsItem.setStyle();
//将添加HTML标签
this.renderHTML();
}

//创建商品容器
createElem() {
if (this.elem) return this.elem;
var div = Utils.ce("div");
div.className = "goodsItem";
return div;
}
//添加到传入的父元素内
appendTo(parent) {
if (typeof parent === "string") parent = document.querySelector(parent);
parent.appendChild(this.elem);
}

//给新创建的标签元素添加CSS属性
setData(_data) {
this.data = _data;
this.icon = this.elem.querySelector(".icon");
let presell = this.elem.querySelector(".presell");
this.miniIconCon = this.elem.querySelector(".miniIconCon");
var price = this.elem.querySelector(".price");
var infoCon = this.elem.querySelector(".infoCon");
var evaluate = this.elem.querySelector(".evaluate");
var shopCon = this.elem.querySelector(".shopCon");
var tagCon = this.elem.querySelector(".tagCon");

//如果item内presell不是空字符串
if (this.data.presell.trim().length > 0) {
//在presel标签里添加内容,内容为传入数组内
presell.textContent = this.data.presell;
} else {
//如果presell是空字符串,隐藏presell标签
presell.style.display = "none"
}
//为添加miniicon里面的图片设置字符串
var miniIconStr = "";
//遍历item内miniIcon内的每一项
this.data.miniIcon.forEach(item => {
//将miniIcon内每一项图片路径转换为图片标签
miniIconStr += `<img src=${item} class="miniIcon">`
})
//将添加标签的字符串加入到miniIcon内,生成图片标签
this.miniIconCon.innerHTML = miniIconStr;
//给每一项小标签添加鼠标滑出事件
this.miniIconCon.addEventListener("mouseover", e => this.iconMouseHandler(e))

//先给icon设置默认值 默认第一张图片
this.changeIcon(0);

//价格标签内填入item对象price内容
price.textContent = this.data.price.toFixed(2);
//信息标签内填入item对象info内容
infoCon.textContent = this.data.info
//评价标签内填入item对象evaluate内容
evaluate.textContent = this.data.evaluate;
//店铺标签内填入item对象shopName内容
shopCon.textContent = this.data.shopName;

//根据dic对象内key的名称对应item对象内tag数组内的字符串匹配,匹配成功的给该标签设置对应的class name,从而拥有CSS样式
var dic={
"自营":"goods_icon_0",
"放心购":"goods_icon_1",
"本地仓":"goods_icon_0",
"赠":"goods_icon_2",
"京东物流":"goods_icon_2",
"秒杀":"goods_icon_2",
"免邮":"goods_icon_2",
"险":"goods_icon_3",
"券":"goods_icon_3",
}
var tagStr="";

//匹配item里tag内容和dic内prop内容,匹配成功的 赋给class
this.data.tag.forEach(item=>{
for(let prop in dic){
if(item.indexOf(prop)>=0)tagStr+=`<span class='${dic[prop]}'>${item}</span>`;
}

});
tagCon.innerHTML=tagStr;
}

iconMouseHandler(e) {

//如果目标事件不是图片类型,直接返回
if (e.target.constructor !== HTMLImageElement) return;
//匹配目标事件为侦听事件的哪一个子元素 将找到的下标赋给index。
var index = Array.from(e.currentTarget.children).indexOf(e.target);

this.changeIcon(index);
}

changeIcon(index) {
//判断preIcon是否存在
if (this.preIcon) {
//如果存在,设置preIcon 的边框
this.preIcon.style.border = "1px solid #CCCCCC";
}
//如果不存在 ,设置icon内图片的路径为miniicon对应的大图片
this.icon.src = this.data.icon[index];
//然后给preIcon赋值为miniIcon内图片的下标
this.preIcon = this.miniIconCon.children[index];
//给miniicon内目标事件图片设置红色边框
this.preIcon.style.border = "2px solid #e4393c";
}
renderHTML() {
this.elem.innerHTML = `
<div class="iconCon">
<img class="icon">
<div class="presell">
</div>
<div class="miniIconCon"></div>
<div class="priceCon">
<span class="money">¥</span>
<span class="price"></span>
</div>
<a class="infoCon" href="#"></a>
<div class="evaluateCon"><span class="evaluate"></span>条评价</div>
<div class="shopCon"></div>
<div class="tagCon">
</div>
`
}

static setStyle() {
GoodsItem.styleBool = true;
Utils.setStyle({
".goodsItem": {
width: "240px",
height: "466px",
float: "left",
margin: "5px"
},
".goodsItem:hover": {
boxShadow: "0px 0px 4px #999999"
},
".iconCon": {
width: "220px",
height: "220px",
marginBottom: "5px",
position: "relative",
margin: "auto",
left: 0,
right: 0,
},
".presell": {
width: "200px",
height: "25px",
position: "absolute",
bottom: "0px",
backgroundColor: "rgba(0,0,0,0.4)",
fontSize: "12px",
color: "#FFFFFF",
paddingLeft: "20px",
lineHeight: "25px"
},
".miniIcon": {
width: "25px",
height: "25px",
// border:"2px solid #e4393c00",
border: "1px solid #CCCCCC",
marginRight: "5px"
},
".priceCon": {
width: "220px",
height: "22px",
color: "#e4393c",
fontSize: "20px",
marginTop:"5px",
},
".money": {
fontSize: "16px",
},
".price": {
marginLeft: "-10px"
},
".infoCon": {
width: "220px",
height: "40px",
wordWrap: "break-word",
overflow: "hidden",
display: "block",
fontSize: "12px",
color: "#333333",
lineHeight: "20px",
marginTop: "10px",
textDecoration: "none"
},
".evaluateCon": {
width: "220px",
height: "18px",
fontSize: "12px",
color: "#333333",
marginTop: "5px"
},
".evaluate": {
color: "#646FB0",
fontWeight: "600",
},
".shopCon": {
fontSize: "12px",
marginTop: "5px",
color: "#AAAAAA",
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
width: "122px",
height: "18px"
},
".goods_icon_0": {
float: "left",
height: "16px",
lineHeight: "16px",
padding: "0 3px",
marginRight: "3px",
overflow: "hidden",
textAlign: "center",
fontStyle: "normal",
fontSize: "12px",
fontFamily: '"Helvetica Neue","Hiragino Sans GB",SimSun,serif',
background: "#e23a3a",
color: "#FFF",
cursor: "default",
borderRadius: "2px",
},
".goods_icon_1": {
border: "1px solid #e23a3a",
borderColor: "#4d88ff",
color: "#4d88ff",
float: "left",
height: "14px",
lineHeight: "14px",
padding: "0 3px",
marginRight: "3px",
overflow: "hidden",
textAlign: "center",
fontStyle: "normal",
fontSize: "12px",
fontFamily: '"Helvetica Neue","Hiragino Sans GB",SimSun,serif"',
borderRadius: "2px",
},
".goods_icon_2": {
float: 'left',
height: '14px',
lineHeight: '14px',
padding: '0 3px',
border: '1px solid #e23a3a',
marginRight: '3px',
overflow: 'hidden',
textAlign: 'center',
fontStyle: 'normal',
fontSize: '12px',
fontFamily: '"Helvetica Neue","Hiragino Sans GB",SimSun,serif',
borderRadius: '2px',
color: '#e23a3a',
},
".goods_icon_3": {
float: 'left',
height: '16px',
lineHeight: '16px',
padding: '0 3px',
marginRight: '3px',
overflow: 'hidden',
textAlign: 'center',
fontStyle: 'normal',
fontSize: '12px',
fontFamily: '"Helvetica Neue","Hiragino Sans GB",SimSun,serif',
background: '#e23a3a',
color: '#FFF',
cursor: 'default',
borderRadius: '2px',
background: "#4b9bfc",
},
".tagCon":{
marginTop:"10px"
}
})
}
}
(2)选择框模块
export default class CheckBox extends EventTarget{
elem;
label;  //接收外部以参数传入的lable
checked=false; //是否选中

//创建多选框
constructor(_data,_label){
super();

//将外部传入的参数赋给全局属性
this.data=_data;
this.label=_label;
this.elem=this.createElem();
}
//创建多选框容器
createElem(){
if(this.elem) return this.elem;
let div=document.createElement("div");
div.style.float="left";
div.style.marginRight="5px";
div.style.marginTop="4px";
div.style.position="relative";
let icon=document.createElement("span");
Object.assign(icon.style,{
width:"13px",
height:"13px",
position:"relative",
display:"inline-block",
marginRight:"3px",
border:"1px solid #666666",
borderRadius:"3px",

});
//创建多选框选中后对号
let a=document.createElement("div");
Object.assign(a.style,{
width: "3px",
height: "6px",
marginLeft:"4px",
marginTop:"1px",
borderColor: "#FFFFFF",
borderStyle: "solid",
borderWidth: "0 2px 3px 0",
transform: "rotate(45deg)",
})
icon.appendChild(a)
div.appendChild(icon);

//创建多选框后面的文本容器
let labelSpan=document.createElement("span");
labelSpan.textContent=this.label;
labelSpan.style.userSelect="none";
labelSpan.style.position="relative"
div.appendChild(labelSpan);

//div设置点击事件
div.addEventListener("click",e=>this.clickHandler(e));
//div设置鼠标滑入事件
div.addEventListener("mouseover",e=>this.mouseHandler(e));
//div设置鼠标滑出事件
div.addEventListener("mouseout",e=>this.mouseHandler(e));
return div;
}
//将创建的元素插入到指定父元素内
appendTo(parent){
if(typeof parent==="string") parent=document.querySelector(parent);
parent.appendChild(this.elem);
}
//将元素插入到指定的父元素内,且在指定子元素前面
insertTo(parent,elem){
if(typeof parent==="string") parent=document.querySelector(parent);
if(typeof elem==="string") elem=document.querySelector(elem);
parent.insertBefore(this.elem,elem);
}

//鼠标点击后执行的函数
clickHandler(e){
//给复选框按钮设置开关
this.checked=!this.checked;

//执行setCheck()函数,且将this.checked(即复选框是否选中)作为参数代入到函数内。
this.setCheck(this.checked);

//创建一个事件名为:change的事件
var evt=new Event("change");
//将this.checked和this.data赋值给事件 e ,这样就可以把this.checked和this.data抛发出去了。
evt.checked=this.checked;
evt.data=this.data;
//将事件抛发出去
this.dispatchEvent(evt);
}
//点击事件后,具体执行的函数
setCheck(_check){
//获得传入的参数,并赋值给this.checked
this.checked=_check;
//根据checked的值编辑icon的背景颜色和边框。
Object.assign(this.elem.firstElementChild.style,{
backgroundColor:this.checked ? "rgb(54 161 251)" : "#FFFFFF",
border:this.checked ? "1px solid rgb(54 161 251)" : "1px solid #666666"
});
//编辑多选框内小对号是否出现
this.elem.firstElementChild.firstElementChild.display=this.checked ? "block" : "none"
}
//设置鼠标滑入滑出事件函数
mouseHandler(e){
//如果 复选框为选中,即this.checked为true
if(this.checked){
//当复选框为选中时,无论鼠标滑入滑出,边框始终为固定值
this.elem.firstElementChild.style.borderColor="1px solid rgb(54 161 251)";
return;
}
//当鼠标滑入时,改变边框颜色
if(e.type==="mouseover"){
this.elem.firstElementChild.style.borderColor="#aaaaaa";
}
//当鼠标滑出时,改变边框颜色
else{
this.elem.firstElementChild.style.borderColor="#666666";
}
}
}
(3)计数器模块
import Utils from "./Utils.js";
export default class StepNumber extends EventTarget {
leftBn;
rightBn;
input;
ids;
step = 1;
data;
constructor(_data) {
super();
this.data = _data;
this.elem = this.creatElem();
}
creatElem() {
if (this.elem) return this.elem;
var div = Utils.ce("div", {
width: "80px",
height: "22px",
position: "relative",
});
this.leftBn = Utils.ce("a", {
width: "15px",
height: "20px",
border: "1px solid #cccccc",
display: "block",
textDecoration: "none",
color: "#333333",
textAlign: "center",
lineHeight: "20px",
float: "left",
backgroundColor: "#FFFFFF",
});
this.href = "javaScript:void(0)";
this.textContent = "-";
this.input = Utils.ce("input", {
border: "none",
borderTop: "1px solid #cccccc",
borderBottom: "1px solid #cccccc",
textAlign: "center",
outline: "none",
float: "left",
width: "42px",
height: "18px",
});
this.input.value = "1";
this.rightBn = this.leftBn.cloneNode(false);
this.textContent = "+";
div.appendChlid(this.leftBn);
div.appendChlid(this.input);
div.appendChlid(this.righttBn);
this.leftBn.addEventListener("click", (e) => this.clickHandler(e));
this.rightBn.addEventListener("click", (e) => this.clickHandler(e));
this.input.addEventListener("input", (e) => inputHandler(e));
return div;
}
appendTo(parent) {
if (typeof parent === "string") parent = document.querySelector(parent);
parent.appendChild(this.elem);
}
inputHandler(e) {
if (this.ids !== undefined) return;
this.ids = setTimeout(() => {
clearTimeout(ids);
this.ids = undefined;
this.setStep(this.input.value, true);
});
}
clickHandler(e) {
var bn = e.currentTarget;
if (bn.bool) return;
if (bn === this.leftBn) {
this.step--;
} else {
this.step++;
}
this.setStep(this.step, true);
}
setStep(_step, bool) {
if (typeof _step === "string") _step = Number(_step.replace(/\D/g, ""));
if (_step >= 99) {
_step = 99;
this.rightBn.style.color = "#CCCCCC";
this.rightBn.bool = true;
}
if (_step <= 1) {
_step = 1;
this.leftBn.style.color = "#CCCCCC";
this.leftBn.bool = true;
} else {
this.rightBn.bool = false;
this.rightBn.style.color = "#000000";
this.leftBn.bool = false;
this.leftBn.style.color = "#000000";
}
this.step = _step;
this.input.value = this.step;
if (bool) {
var evt = new Event("change");
evt.data = this.data;
evt.step = this.step;
this.dispatchEvent();
}
}
}
(4)购物车模块
import Utils from "./Utils.js";
import CheckBox from "./CheckBox注释.js";
import StepNumber from "./StepNumber注释.js";

export default class Shopping extends EventTarget{

//table存放购物车放在的表格
table;

//设置静态属性styleBoll 存放是否已经创建CSS样式
static styleBool=false;

//存放表头内容的数组
headList=["全选","","商品","","单价","数量","小计","操作"];

//创建三个静态属性用于抛发事件名
static CHECK_CHANGE="check_change_event";
static STEP_CHANGE="step_change_event";
static DELETE_CHANGE="delete_change_event";

//创建购物车
constructor(){
super();

//如果没有创建CSS样式 ,添加CSS样式
if(!Shopping.styleBool) Shopping.setStyle();
this.elem=this.createElem();
}

//创建购物车外部容器
createElem(){
if(this.elem) return this.elem;
return Utils.ce("div");
}
appendTo(parent){
if(typeof parent==="string") parent=document.querySelector(parent);
parent.appendChild(this.elem);
}

//根据 参数带入的列表 设置数据,根据数据生成表格
setData(list){
//如果表格存在,删除表格,用于更新表格
if(this.table) this.table.remove();

//创建表格,并且给表格元素赋予class名
this.table=Utils.ce("table");
this.table.className="tableClass";

//根据list给表格创建表头
this.createHead(list);
//根据list给表格创建每一行
this.createListTr(list);
//将表格 添加进购物车div里
this.elem.appendChild(this.table);
}

//创建表头
createHead(list){

//创建第一行,并且赋给clssName
var thr=Utils.ce("tr");
thr.className="thr";
//根据表头内容长度,创建单元格
for(var j=0;j<this.headList.length;j++){

//根据需求,将第一列跟第二列合并,此时不创建第二列。
if(j===1) continue;
//创建单元格
var th=Utils.ce("th");
//单元格内文本为数组对应的内容
th.textContent=this.headList[j];
//单元格内元素左对齐
th.style.textAlign="left";
//设置单元格左填充
th.style.paddingLeft="15px";

//如果为第一个单元格时
if(j===0){
//合并第一,第二列
th.setAttribute("colspan","2");

//第一个单元格设置全选按钮
let ck=new CheckBox();

//当list内每一项元素内所有的checked 对应的值为true时,bool才为true
var bool=list.every(item=>{
return item.checked
})
//设置全选按钮点击事件函数
ck.setCheck(bool);
//将ck插入到th内,且在th子元素前面
ck.insertTo(th,th.firstChild);
//触发change事件时,执行checkHandler函数
ck.addEventListener("change",e=>this.checkHandler(e))
}
//第三个单元格内容居中,且左填充为0
if(j===2){
th.style.textAlign="center"
th.style.paddingLeft="0";
}
//将单元格插入到表头内
thr.appendChild(th);
}
//将表头插入到表格里
this.table.appendChild(thr)
}

//创建每一行
createListTr(list){
//根据列表里的数据创建每一行
for(var i=0;i<list.length;i++){
var tr=Utils.ce("tr");
//给tr赋予class名
tr.className="trs";
//如果是第一行,改变边框
if(i===0) tr.style.borderTop="2px solid #999999";
//遍历数组内每一个对象,根据信息创建单元格
for(var prop in list[i]){
if(prop==="id") continue;
var td=Utils.ce("td");
td.style.padding="15px 0 10px";
td.style.wordWrap="break-word";
//执行createTdContent函数,给单元格赋上属性和添加CSS样式
this.createTdContent(td,list[i],prop);
tr.appendChild(td);
}
this.table.appendChild(tr);
}
}

//给单元格添加内容和CSS属性
createTdContent(td,data,prop){
//根据对象内容,判断单元格内容
switch(prop){
case "checked":
//创建单选框
let ck=new CheckBox(data);
//传入checked此时的值,判断是否选中
ck.setCheck(data[prop]);
ck.appendTo(td);
//设置change事件侦听,获取CheckBox类内抛发的数据
ck.addEventListener("change",e=>this.checkHandler(e))
td.style.padding="0 15px 0 11px";
break;
case "icon":
//创建图片
var img=new Image();
//从列表中获取图片src
img.src=data[prop];
//给图片赋予CSS样式
Object.assign(img.style,{
width:"80px",
height:"80px"
})
td.appendChild(img);
break;
case "total":
td.style.fontWeight="600";
case "price":
td.textContent="¥"+data[prop].toFixed(2);
break
case "deleted":
//创建删除标签
var span=Utils.ce("span");
span.style.marginLeft="10px";
span.textContent="删除";
//将data赋值为span的属性
span.data=data;
td.appendChild(span);
//设置点击事件侦听
span.addEventListener("click",e=>this.deleteHandler(e));
break;
case "num":
//创建数据累加器
var step=new StepNumber(data);
//将列表内数据以参数的形式传入setStep函数
step.setStep(data[prop]);
step.appendTo(td);
//设置change事件侦听,获取StepNumber类内抛发的数据
step.addEventListener("change",e=>this.stepChangeHandler(e))
break;
default :
//其他单元格内填入列表内对应数据
td.textContent=data[prop];
td.style.paddingLeft="15px"
break
}
}

//设置事件抛发,调用该类的该方法时,侦听获取数据
stepChangeHandler(e){
var evt=new Event(Shopping.STEP_CHANGE);
evt.data=e.data;
evt.step=e.step;
this.dispatchEvent(evt);
}
checkHandler(e){
var evt=new Event(Shopping.CHECK_CHANGE);
evt.data=e.data;
evt.checked=e.checked;
this.dispatchEvent(evt);
}
deleteHandler(e){
var evt=new Event(Shopping.DELETE_CHANGE);
evt.data=e.currentTarget.data;
this.dispatchEvent(evt);
}
//静态函数设置CSS样式
static setStyle(){
//首先把静态变量styleBool设置为true,即已经设置CSS属性。
Shopping.styleBool=true;
Utils.setStyle({
".tableClass":{
width:"990px",
position:"relative",
margin:"auto",
left:0,
right:0,
borderCollapse:"collapse"
},
".thr":{
width:"100%",
height:"32px",
lineHeight:"32px",
padding:"5px 0px",
backgroundColor:"#F3F3F3",
color:"#666666",
fontSize:"12px",
margin:"0px 0px 10px"
},
".trs":{
width:"100%",
backgroundColor:"#fff4e8",
color:"#666666",
fontSize:"12px",
borderTop:"1px solid #CCCCCC",
},
".thr>th:nth-child(1)":{
width:"122px",
paddingLeft:"11px"
},
".thr>th:nth-child(2)":{
width:"208px",

},
".thr>th:nth-child(3)":{
width:"150px",
padding:"0 10px 0 20px"
},
".thr>th:nth-child(4)":{
width:"100px",
paddingRight:"40px"
},
".thr>th:nth-child(5)":{
width:"120px",

},
".thr>th:nth-child(6)":{
width:"100px",
paddingRight:"40px"
},
".thr>th:nth-child(7)":{
width:"75px",
paddingLeft:"11px"
}
})
}
}
(5)合成页面模块
import GoodsItem from "./GoodsItem注释.js";
import Shopping from "./Shopping注释.js";
import Utils from "./Utils.js";

export default class Main {
list;  //接收以参数传入的列表
goodsList = [];
shoppingList = []; //创建一个空数组,然后存放点击商品后加入的商品数据
shopping;  //shopping实例化
constructor(_list) {
this.list = _list;
//创建div容器存放商品列表
var div = Utils.ce("div");
for (var i = 0; i < _list.length; i++) {
//根据列表长度创建商品显示内容
var goods = new GoodsItem();
//将商品显示内容放入div中
goods.appendTo(div);
//将列表内对应数据加入商品显示内容
goods.setData(_list[i]);
//给商品显示内容添加点击侦听事件
goods.addEventListener("click", e => this.clickHandler(e));
}
document.body.appendChild(div);

//生成购物车实例化对象
this.shopping=new Shopping();
//将购物车放入body里
this.shopping.appendTo("body");

//购物车侦听多选框是否被选中
this.shopping.addEventListener(Shopping.CHECK_CHANGE,e=>this.checkChangeHandler(e));
//侦听累加器是否被修改
this.shopping.addEventListener(Shopping.STEP_CHANGE,e=>this.stepChangeHandler(e));
//侦听是否删除数据
this.shopping.addEventListener(Shopping.DELETE_CHANGE,e=>this.deleteChangeHandler(e));

//创建总价容器
this.totalDiv=Utils.ce("div",{
fontSize:"30px",
textAlign:"right",
paddingRight:"150px",
color:"red"
},"body");
}

//点击商品显示区后,执行的点击事件,将点击的商品加入购物车
clickHandler(e) {
//将点击商品的信息赋值给data
var data = e.currentTarget.data;
//当数据重复时,使用reduce将数据筛除,只留一条数据。
var item=this.shoppingList.reduce((value,item)=>{
if(item.id===data.id) value=item;
return value;
},null);
//如果
if (item) {
item.num++;
item.total=item.price*item.num;
} else {
var obj = {
id: data.id,
checked: false,
icon: e.icon,
name: data.info,
info: "",
price: data.price,
num: 1,
total: data.price,
deleted: false
}
this.shoppingList.push(obj);
}
this.shopping.setData(this.shoppingList)
}
checkChangeHandler(e){
if(!e.data){
this.shoppingList.forEach(item=>{
item.checked=e.checked;
})
}else{
this.shoppingList.forEach(item=>{
if(item.id===e.data.id) item.checked=e.checked;
})
}
this.shopping.setData(this.shoppingList);
this.totalPrice();
}
stepChangeHandler(e){
this.shoppingList.forEach(item=>{
if(item.id===e.data.id){
item.num=e.step;
item.total=e.step*item.price;
}
});
this.shopping.setData(this.shoppingList);
this.totalPrice();
}
deleteChangeHandler(e){
this.shoppingList=this.shoppingList.filter(item=>{
return item.id!==e.data.id;
});
this.shopping.setData(this.shoppingList)
this.totalPrice();
}
totalPrice(){
this.totalDiv.textContent=this.shoppingList.reduce((value,item)=>{
if(item.checked) value+=item.total;
return value;
},0)
}
}
(5)创建元素板块
export default class Utils{
static ce(type,style,parent){
var elem=document.createElement(type);
if(style){
for(var prop in style){
elem.style[prop]=style[prop];
}
}
if(typeof parent==="string") parent=document.querySelector(parent);
if(parent) parent.appendChild(elem);
return elem;
}}

2.html页面

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>

</head>
<body>
<script type="module">
import Main from "./js/Main注释.js";
var arr = [
{
id: 1001,
icon: ["./img/a1.jpg"],
miniIcon: ["./img/mini_a1.jpg"],
price: 1199,
info: "荣耀Play4T 全网通6GB+128GB大内存 蓝水翡翠 4000mAh大电池 4800万AI摄影 6.39英寸魅眼屏",
info1: "",
used: false,
evaluate: "31万+",
shopName: "荣耀京东自营旗舰店",
presell: "",
tag: ["自营", "放心购"]
},

{
id: 1002,
icon: ["./img/a2.jpg"],
miniIcon: ["./img/mini_a2.jpg"],
price: 1389,
info: "荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 幻夜黑",
info1: "",
used: false,
evaluate: "10万+",
shopName: "荣耀京东自营旗舰店",
presell: "预售中",
tag: ["自营", "放心购", "秒杀", "赠"]
},

{
id: 1003,
icon: ["./img/b1_1.jpg", "./img/b1_2.jpg", "./img/b1_3.jpg"],
miniIcon: ["./img/mini_b1_1.jpg", "./img/mini_b1_2.jpg", "./img/mini_b1_3.jpg"],
price: 1389,
info: "荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 蓝水翡翠",
info1: "",
used: false,
evaluate: "37万+",
shopName: "荣耀京东自营旗舰店",
presell: "",
tag: ["自营", "放心购", "本地仓", "秒杀", "赠"]
},

{
id: 1004,
icon: ["./img/b2_1.jpg", "./img/b2_2.jpg", "./img/b2_3.jpg"],
miniIcon: ["./img/mini_b2_1.jpg", "./img/mini_b2_2.jpg", "./img/mini_b2_3.jpg"],
price: 1199,
info: "荣耀Play4T 全网通6GB+128GB大内存 蓝水翡翠 4000mAh大电池 4800万AI摄影 6.39英寸魅眼屏",
info1: "",
used: false,
evaluate: "31万+",
shopName: "荣耀京东自营旗舰店",
presell: "",
tag: ["自营", "放心购", "本地仓", "赠"]
},

{
id: 1005,
icon: ["./img/c1.jpg"],
miniIcon: ["./img/mini_c1.jpg"],
price: 1389,
info: "荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 幻夜黑",
info1: "",
evaluate: "10万+",
used: false,
shopName: "荣耀京东自营旗舰店",
presell: "",
tag: ["自营", "放心购", "秒杀", "赠"],
},

{
id: 1006,
icon: ["./img/c2_1.jpg", "./img/c2_2.jpg", "./img/c2_3.jpg", "./img/c2_4.jpg"],
miniIcon: ["./img/mini_c2_1.jpg", "./img/mini_c2_2.jpg", "./img/mini_c2_3.jpg", "./img/mini_c2_4.jpg"],
price: 1389,
info: "麒麟980芯片;超感光徕卡四摄10倍混合变焦;店铺首页领白条免息券",
info1: "",
evaluate: "81万+",
used: true,
shopName: "华为京东自营官方旗舰店",
presell: "",
tag: ["自营", "放心购", "秒杀", "赠"],
},

{
id: 1007,
icon: ["./img/d1_1.jpg", "./img/d1_2.jpg", "./img/d1_3.jpg", "./img/d1_4.jpg", "./img/d1_5.jpg"],
miniIcon: ["./img/mini_d1_1.jpg", "./img/mini_d1_2.jpg", "./img/mini_d1_3.jpg", "./img/mini_d1_4.jpg", "./img/mini_d1_5.jpg"],
price: 2489,
info: "荣耀Play4 Pro 5G双模 麒麟990 4000万超感光暗拍 40W超级快充 8GB+128GB幻夜黑",
info1: "",
used: false,
evaluate: "21万+",
shopName: "荣耀京东自营官方旗舰店",
presell: "",
tag: ["自营", "放心购", "秒杀", "赠"]
},

{
id: 1008,
icon: ["./img/d2_1.jpg", "./img/d2_2.jpg", "./img/d2_3.jpg", "./img/d2_4.jpg"],
miniIcon: ["./img/mini_d2_1.jpg", "./img/mini_d2_2.jpg", "./img/mini_d2_3.jpg", "./img/mini_d2_4.jpg"],
price: 2399,
info: " HUAWEI nova 5 Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片8GB+128GB亮黑色全网通双4G",
info1: "",
used: true,
evaluate: "57万+",
shopName: "华为京东自营官方旗舰店",
presell: "",
tag: ["自营", "放心购", "秒杀"]
},

{
id: 1009,
icon: ["./img/e1_1.jpg", "./img/e1_2.jpg", "./img/e1_3.jpg", "./img/e1_4.jpg", "./img/e1_5.jpg"],
miniIcon: ["./img/mini_e1_1.jpg", "./img/mini_e1_2.jpg", "./img/mini_e1_3.jpg", "./img/mini_e1_4.jpg", "./img/mini_e1_5.jpg"],
price: 2489,
info: "荣耀V30 5G 双模 麒麟990 突破性相机矩阵 游戏手机 8GB+128GB 幻夜星河 移动联通电信5G 双卡双待",
info1: "",
used: false,
evaluate: "21万+",
shopName: "荣耀京东自营旗舰店",
presell: "",
tag: ["自营", "放心购", "秒杀", "赠"],

},

{
id: 1010,
icon: ["./img/e2_1.jpg", "./img/e2_2.jpg", "./img/e2_3.jpg", "./img/e2_4.jpg", "./img/e2_5.jpg"],
miniIcon: ["./img/mini_e2_1.jpg", "./img/mini_e2_2.jpg", "./img/mini_e2_3.jpg", "./img/mini_e2_4.jpg", "./img/mini_e2_5.jpg"],
price: 1889,
info: "荣耀Play4 5G双模 6400万锐力四摄 4300mAh大电池 VC液冷散热 8GB+128GB 幻夜黑TNNH-AN00",
info1: "",
used: false,
evaluate: "170万+",
shopName: "荣耀京东自营旗舰店",
presell: "",
tag: ["自营", "放心购", "秒杀", "赠"],

},

{
id: 1011,
icon: ["./img/f1_1.jpg", "./img/f1_2.jpg", "./img/f1_3.jpg"],
miniIcon: ["./img/mini_f1_1.jpg", "./img/mini_f1_2.jpg", "./img/mini_f1_3.jpg"],
price: 1889,
info: "荣耀Play4 5G双模 6400万锐力四摄 4300mAh大电池 VC液冷散热",
info1: "",
used: true,
evaluate: "1.3万+",
shopName: "荣耀京东自营旗舰店",
presell:"",
tag: ["自营", "放心购", "秒杀", "赠"],
},

{
id: 1012,
icon: ["./img/f2_1.jpg", "./img/f2_2.jpg", "./img/f2_3.jpg"],
miniIcon: ["./img/mini_f2_1.jpg", "./img/mini_f2_2.jpg", "./img/mini_f2_3.jpg"],
price: 1889,
info: "华为畅享10e 手机 翡冷翠 移动全网通(4G+64G)",
info1: "【4 + 64移动绿秒杀低至828元!直降170元!】现货速发,5000mAh超长续航,支持反向充电~!《畅享20pro咨询减钱》戳~",
used: true,
evaluate: "1.3万+",
shopName: "荣耀京东自营旗舰店",
presell:"",
tag: ["京东物流", "放心购", "秒杀", "免邮", "险"],
}
]

new Main(arr)
</script>
</body>
</html>

3.购物车思路

(1)Utils 创建元素 设置样式

CheckBox 多选框
constructor 将多选框关联的对象存入
createElem 创建多选框
appendTo 将多选框插入在父容器尾部
insertTo 将多选框插入在父容器中某个元素前面
clickHandler 点击多选框切换 并且抛发事件 现在多选框是否选中,将带入的对象也携带抛出
setCheck 设置多选框的内容
mouseHandler 改变多选框经过时的样式

(2)GoodsItem
constructor 创建整个显示容器
createElem 创建容器
appendTo  插入在父容器
setData   设置数据,根据数据生成商品显示内容
iconMouseHandler 鼠标经过小图标时事件
changeIcon 修改大图标
renderHTML 渲染所有HTML标签内容
setStyle  设置样式
clickHandler 当点击当前商品时,抛发事件将当前商品的对象数据抛出
(3) StepNumber 数据累加器
constructor  创建数据累加器,并且将当前数据的对象存入
createElem 创建数据累加器内容
appendTo   将当前元素放入父容器中
inputHandler 当输入内容处理值
clickHandler 当点击+-按钮时处理内容
setStep 输入和点击按钮,以及可以外部设置值,根据设置值改变input的显示内容,并且判断是否抛发事件,事件中带有当前数据的对象和累加的结果(当输入和点击按钮时抛发,如果外部设置不抛发)
(4)Shopping
constructor 创建购物车
createElem  创建购物车容器
appendTo 将购物车添加在父容器中
setData  设置数据,根据数据生成表格
createHead 创建表头, 表头使用到CheckBox,并且设置侦听         CheckBox事件内容,设置了根据数据判断是否需要全选
createListTr 创建表格各行
createTdContent 创建表格每行当中的内容,分别使用到           CheckBox,   stepNumber,CheckBox设置了多选框的data数据,设置多选框根据数据内容选择是否被选中,侦听多选事件,stepNumber放入时设置当前对象存储,侦听累加器是否改变,设定累加器初始值
stepChangeHandler 侦听累加器收到修改事件,并且重新抛发新的事件通知外层类执行
checkHandler 侦听多选框选中事件,并且重新抛发事件通知外层类执行
deleteHandler 侦听删除事件,并且重新抛发事件通知外层类执行
setStyle  设置样式
(5)Main

constructor 创建购物车页面内容 根据给入的数据创建了商品列表,创建商品列表的侦听事件,创建了购物车对象,创建总价容器。购物车侦听多选框是否被选中,侦听累加器是否被修改,侦听是否删除数据
clickHandler 当点击商品列表中商品时,判断再购物车shoppingList数组中是否有当前点击的商品,如果有,就给数据做累加,如果没有就添加新数据到shoppingList,并且根据当前shoppingList灌入到购物车中,重新创建购物车表格
checkChangeHandler 如果多选框被选中,判断有没有数据,如果没有数据就是表头的多选框,有数据就是每个商品数据,根据这个内容修改shoppingList数据中对应的内容,并且重新灌入到购物车中,重新生成购物车表格
stepChangeHandler 累加器被修改时,修改对应的shoppingList中对应的数据,并且重新灌入到购物车中,重新生成购物车表格
deleteChangeHandler 从shoppingList中删除需要删除的数据商品,并且重新灌入到购物车中,重新生成购物车表格
totalPrice 修改商品数量,删除商品,选中商品,都执行这个方法,并且根据数据是否选中,将商品总价累积并且显示

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: