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>

 

posted @ 2022-01-24 23:37  thomas_001  阅读(82)  评论(0)    收藏  举报