JS 重构购物车案例 初始版(详细注释)
有一些地方还未做完善,比如当商品点击减号为0时,自动删除这个商品对象,后续会进行完善

代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.shopItem {
width: 180px;
height: 60px;
position: relative;
border: 1px solid #000000;
float: left;
}
.shopItem>img {
width: 50px;
height: 50px;
margin-left: 5px;
margin-top: 5px;
}
.shopItem>div,
.shopItem>span {
position: absolute;
top: 5px;
width: 125px;
text-align: center;
right: 0;
}
.shopItem>span {
display: block;
top: 30px;
right: 0;
bottom: 5px;
}
.stepNum {
width: 150px;
height: 30px;
margin: auto;
position: relative;
}
.stepNum>button {
width: 30px;
height: 30px;
background-color: white;
border: 1px solid #000000;
}
.stepNum>input {
width: 68px;
height: 26px;
border: 1px solid #000000;
border-left: none;
border-right: none;
text-align: center;
}
table {
border-collapse: collapse;
width: 600px;
position: absolute;
left: 0;
top: 150px;
}
td,
th {
height: 30px;
line-height: 30px;
border: 1px solid #000000;
text-align: center;
}
td>img {
width: 25px;
height: 25px;
display: inline-block;
position: relative;
top: 5px;
}
</style>
</head>
<body>
<script>
var data = [ {
id: 1001,
icon: "img/1.png",
name: "餐饮0",
num: 1,
price: 10
},
{
id: 1002,
icon: "img/2.png",
name: "餐饮1",
num: 1,
price: 20
},
{
id: 1003,
icon: "img/3.png",
name: "餐饮2",
num: 1,
price: 30
},
{
id: 1004,
icon: "img/4.png",
name: "餐饮3",
num: 1,
price: 40
},
{
id: 1005,
icon: "img/5.png",
name: "餐饮4",
num: 1,
price: 50
},
{
id: 1006,
icon: "img/6.png",
name: "餐饮5",
num: 1,
price: 60
},
{
id: 1007,
icon: "img/7.png",
name: "餐饮6",
num: 1,
price: 70
},
{
id: 1008,
icon: "img/8.png",
name: "餐饮7",
num: 1,
price: 80
},
{
id: 1009,
icon: "img/9.png",
name: "餐饮8",
num: 1,
price: 90
},
{
id: 1010,
icon: "img/10.png",
name: "餐饮9",
num: 1,
price: 100
}
];
localStorage.shopData = JSON.stringify( data );
var shopData, table, shoppingList;
const ADD_ITEM_EVENT = "add_item_event";
const STEP_NUM_CHANGE = "step_num_change";
const DELETE_ITEM_SHOP = "delete_item_shop";
const SELECT_ITEM_SHOP = "select_item_shop";
init();
/*
*
* 初始化函数
* 1、获取存储中的商品列表数据
* 2、如果商品购物车列表在存储器中存在,那么就将这个数据获取赋值给购物车列表
* 如果不存在,创建一个空数组
* 3、 侦听添加商品事件,侦听删除商品事件,侦听修改商品数量事件,侦听选中商品事件
* 4、循环商品列表,创建所有的商品标签
* 5、根据购物车列表创建表格
*
*
* */
function init() {
shopData = JSON.parse( localStorage.shopData );
if ( localStorage.shoppingList ) {
shoppingList = JSON.parse( localStorage.shoppingList );
} else {
shoppingList = [];
}
// shoppingList.push({select:true,id:1002,icon:"img/2.png",name:"餐饮1",num:2,price:20,sum:0,deleted:false});
document.addEventListener( ADD_ITEM_EVENT, addItemClickHandler );
document.addEventListener( DELETE_ITEM_SHOP, removeItemHandler );
document.addEventListener( STEP_NUM_CHANGE, stepNumChangeHandler );
document.addEventListener( SELECT_ITEM_SHOP, selectItemShopHandler );
for ( var i = 0; i < shopData.length; i++ ) {
createShopItem( shopData[ i ], document.body );
}
createTable();
}
/*
* 添加商品事件
* 1、如果需要添加商品是新商品时,我们需要创建该商品对象,这个商品对象,在创建
* 表格时,会使用for in循环,在for in循环时,是根据对象属性创建先后来循环的
* 因此为了让循环有先后顺序,我们先给对象添加一个属性select,这样在后面的循环中
* 第一个就可以创建出多选框了,紧接着将商品列表对应的对象中所有的属性添加到这个
* 对象中,再添加sum属性,并且计算该sum的值,添加deleted属性,为了在循环中
* 可以创建删除按钮
* 2、数组筛选,商品列表中寻找事件传递过来的id相同的物品,如果筛选到有这个商品
* 我们就将这个商品放在数组中,注意这个商品对象和购物车列表的商品的对象他们都是
* 引用同一个地址的对象,并且如果找到该物品被放在数组中的对象仅有一个。如果没有
* 找到,这个数组就是一个空数组,它的长度为0;
* 3、如果数组的长度是0.这就表示没有找到任何相同的商品对象,我们认为在购物车
* 中没有相同商品类型,因此我们将当前创建的这个商品对象放入到购物车数组列表中
* 4、如果找到了,它里面就只有一个对象,数组中的第0个对象就是被找到的商品对象
* 也就是购物车列表中对应相同id的那个对象,他们之间是引用关系,因此修改这个数组
* 中的第0个对象就是修改了购物车列表中对应id的对象,我们在这里修改将它的num+1,
* 增加了数量,然后修改sum值。
* 5、将购物车列表存储
* 6、创建表格
*
*
* */
function addItemClickHandler( e ) {
var obj = {
select: false
};
for ( var str in e.data ) {
obj[ str ] = e.data[ str ];
}
obj.sum = obj.num * obj.price;
obj.deleted = false;
var arr = shoppingList.filter( function ( item ) {
return item.id === obj.id;
} );
if ( arr.length === 0 ) {
shoppingList.push( obj );
} else {
arr[ 0 ].num++;
arr[ 0 ].sum = arr[ 0 ].num * arr[ 0 ].price;
}
localStorage.shoppingList = JSON.stringify( shoppingList );
createTable();
}
/*
* 删除商品事件
* 1、筛选购物车列表中的数据,将所有商品对象中查找id是否与发来事件中携带数据id相同
* 将不相同的数据放入到数据覆盖原来的数组,这样就等于将原数组中是发来的id相同数据
* 删除。
* 2、重新存储购物列表数据
* 3、重新创建表格
*
* */
function removeItemHandler( e ) {
shoppingList = shoppingList.filter( function ( item ) {
return item.id !== e.data.id;
} );
localStorage.shoppingList = JSON.stringify( shoppingList );
createTable();
}
/*
* 修改物品数量事件执行内容
* 1、遍历购物车列表,在购物车列表中找到与发来的事件中data下面的id属性相同
* 商品,修改该商品的数量是事件发来的num属性值,重新计算这个对象sum合计值
* 2、存储购物车数据列表
* 3、创建表格
*
*
* */
function stepNumChangeHandler( e ) {
shoppingList.map( function ( item ) {
if ( item.id === e.data.id ) {
item.num = e.num;
item.sum = item.price * item.num;
}
} );
localStorage.shoppingList = JSON.stringify( shoppingList );
createTable();
}
/*
* 选中商品事件内容
* 1、全选:e.all是true
* 遍历购物车列表,将所有的商品的select修改为发过来事件中的select属性值
* 单选:e.all是false
* 遍历购物车列表,寻找购物车中商品的id与事件发来的data中id属相相同的商品
* 并且修改它的select属性为事件发来的select属性
* 2、存储购物车列表数据
* 3、创建表格
*
*
* */
function selectItemShopHandler( e ) {
if ( e.all ) {
shoppingList.map( function ( item ) {
item.select = e.select;
} )
} else {
shoppingList.map( function ( item ) {
if ( item.id === e.data.id ) {
item.select = e.select;
}
} )
}
localStorage.shoppingList = JSON.stringify( shoppingList );
createTable();
}
// -------------以上代码是整体的内容代码,下面是各个小部分的代码
// 商品标签
function createShopItem( data, parent ) {
var div = document.createElement( "div" );
div.className = "shopItem";
var img = new Image();
img.src = data.icon;
div.appendChild( img );
var title = document.createElement( "div" );
title.textContent = data.name;
div.appendChild( title );
var price = document.createElement( "span" );
price.textContent = data.price + "元";
div.appendChild( price );
div.data = data;
div.addEventListener( "click", addItemHandler );
parent.appendChild( div );
return div;
}
/*
* 1、创建新事件,事件中type是一个常量,并且是一个字符串
* 这个type作用就是为了对应侦听。
* 2、将事件对象添加属性,这个属性裹挟着一些数据,抛发出去,收到的
* 对象可以使用这个数据内容,私有局部变量的传递
* 3、针对性抛法这个事件对象,这里是全局对象的抛发,任何地方都可以找到并且侦听
* 这样任何位置都能收到抛发事件和数据
*
* */
function addItemHandler( e ) {
var evt = new Event( ADD_ITEM_EVENT );
evt.data = this.data;
document.dispatchEvent( evt );
}
// 计数器
function createStepNum( parent, data ) {
var div = document.createElement( "div" );
parent.appendChild( div );
div.className = "stepNum";
var leftBn = document.createElement( "button" );
leftBn.textContent = "-";
div.appendChild( leftBn );
var input = document.createElement( "input" );
input.type = "text";
input.value = "1";
div.appendChild( input );
var rightBn = document.createElement( "button" );
rightBn.textContent = "+";
div.appendChild( rightBn );
div.input = input;
div.data = data;
leftBn.addEventListener( "click", addItemBnHandler );
rightBn.addEventListener( "click", addItemBnHandler );
input.addEventListener( "input", changeNumInputHandler );
input.addEventListener( "blur", changeNumInputHandler );
return div;
}
function addItemBnHandler( e ) {
var input = this.parentElement.input;
// 当点击的对象内容是-就是意味着是减1
if ( this.textContent === "-" ) {
// 如果等于1,不能减了,跳出不做处理,仍然是1
if ( parseInt( input.value ) === 1 ) return;
// 其它情况-1
input.value = parseInt( input.value ) - 1;
} else if ( this.textContent === "+" ) {
if ( parseInt( input.value ) === 99 ) return;
input.value = parseInt( input.value ) + 1;
}
// 事件中包裹了1、修改的数量,2、修改的商品对象
var evt = new Event( STEP_NUM_CHANGE );
evt.num = parseInt( input.value );
evt.data = this.parentElement.data;
document.dispatchEvent( evt );
}
function changeNumInputHandler( e ) {
if ( e.type === "input" ) {
// 替换非数值变为""
this.value = this.value.replace( /[^0-9]/g, "" );
// 如果=0,就设为1
if ( this.value === "0" ) {
this.value = "1";
}
// 如果什么都没有输入,也设为1
if ( this.value.length === 0 ) {
this.value = "1";
}
// 如果输入的内容大于2位,让这个值为99
if ( this.value.length > 2 ) {
this.value = "99";
}
} else if ( e.type === "blur" ) {
// 失焦时抛出当前修改数量和修改的对象事件
var evt = new Event( STEP_NUM_CHANGE );
evt.num = parseInt( this.value );
evt.data = this.parentElement.data;
document.dispatchEvent( evt );
}
}
// 表格
function createTable() {
if ( table ) {
table.remove();
}
if ( shoppingList.length === 0 ) return;
table = document.createElement( "table" );
document.body.appendChild( table );
var thr = document.createElement( "tr" );
table.appendChild( thr );
for ( var prop in shoppingList[ 0 ] ) {
var th = document.createElement( "th" );
if ( prop === "select" ) {
var input = document.createElement( "input" );
input.type = "checkbox";
input.checked = getCheckAll();
input.addEventListener( "change", checkBoxChangeHandler );
th.appendChild( input );
} else {
th.textContent = prop;
}
thr.appendChild( th );
}
for ( var i = 0; i < shoppingList.length; i++ ) {
var tr = document.createElement( "tr" );
table.appendChild( tr );
for ( var str in shoppingList[ i ] ) {
var td = document.createElement( "td" );
td.data = shoppingList[ i ];
addTdContent( td, shoppingList[ i ], str );
tr.appendChild( td );
}
}
}
/*
* 判断是否全选
* 1、获取购物车列表中的数据,筛选所有数据中select属性是false的商品
* 并且返回到数组中
* 2、如果这个数组中没有元素,表示没有select是false的商品,也就是全选了
* 这时候我们返回这个数组长度与0的对比,arr.length===0这个值是true或者false
* 如果没有元素,就返回true,意味着全选了。否则就是false没有全选
*
*
* */
function getCheckAll() {
var arr = shoppingList.filter( function ( item ) {
return !item.select;
} );
return arr.length === 0;
}
function addTdContent( td, data, prop ) {
switch ( prop ) {
case "select":
var input = document.createElement( "input" );
input.type = "checkbox";
input.checked = data.select;
input.addEventListener( "change", checkBoxChangeHandler );
td.appendChild( input );
break;
case "icon":
var img = new Image();
img.src = data.icon;
td.appendChild( img );
break;
case "num":
var step = createStepNum( td, data );
step.input.value = data.num;
break;
case "deleted":
var bn = document.createElement( "button" );
bn.textContent = "删除";
td.appendChild( bn );
bn.addEventListener( "click", deleteHandler );
break;
default:
td.textContent = data[ prop ];
break;
}
}
function checkBoxChangeHandler( e ) {
var data = this.parentElement.data;
var evt = new Event( SELECT_ITEM_SHOP );
/* if(!data){
evt.all=true;
}else{
evt.all=false;
}*/
evt.all = !data;
evt.select = this.checked;
evt.data = data;
document.dispatchEvent( evt );
}
function deleteHandler( e ) {
var evt = new Event( DELETE_ITEM_SHOP );
evt.data = this.parentElement.data;
document.dispatchEvent( evt );
}
</script>
</body>
</html>

浙公网安备 33010602011771号