淘宝手机频道商品选择 【总结】

需求

 

 

 

需求分析:

品牌,系统,尺寸,网络这些类别下的子选项只能单选,

你的选择处的标签应该按照下面的类别的顺序排好,

实现思路:

这一题我用的面向对象的方式写的,有以下这些方法:

addNewSort:增加分类

deleteSort:删除分类

addGoods:增加商品条目

deleteGoods:删除商品条目

alertState:状态更改

closePickA:关闭标签恢复正常

难点:

没有解决不了的难点,难在怎样实现高度重用,节约代码,节约资源

难点解决方案:

多实践

涉及的新知识:

OOP

优化方向:

其实这个还可以抽象成增删改,这三个基本动作,对应的原型也可以这么写

备注:

面向对象的关键不在于代码,在于怎样把问题抽象出来,抽象成最本质的东西,我觉得面向对象要先学会怎样去抽象问题。

另外,一个方法最后只做一件事。

 

 

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
    <link rel="stylesheet" type="text/css" href="index.css">
</head>
<body>



<div class="bg">

    <div id="phones">
        
        <div class="wrap">
            
            <div class="pWrap">
                <span class="tit">你的选择:</span>
                <div class="pickCon"></div>
            </div>
            <div class="items">
                <!-- <div class="lines"></div> -->
            </div>

        </div>

    </div>

</div>




<script src="getId.js"></script>
<script src="main.js"></script>

</body>
</html>

 

 

*{
    margin: 0;
    padding: 0;
    margin: 0;
    font-family: '微软雅黑';
    font-size: 14px;
}

a{
    text-decoration: none;
}

li{
    list-style: none;
}


.clear{
    zoom:1;
}

.clear:after{
    content: '';
    display: block;
    clear: both;
    vertical-align: middle;
}


.bg{
    width: 980px;
    height: 490px; 
    margin: 70px auto;
    background:url(bg.jpg) no-repeat 0 0; 
}

.wrap{ 
    width: 760px; 
    margin: 0 auto;
    position: relative;
    top: 145px;
    background: rgba(255,255,255,0.9); 
}

.pWrap{ 
    height: 50px;
    padding: 0 21px;
    line-height: 50px;
    background: #e0f0ee;
}

.pWrap .tit{ 
    float: left;
}

.pickCon{
    float: left;
    position: relative;
    margin-top: 13px;
}

.items{
    min-height: 30px;
    padding: 0 30px;
    margin-top: 20px;
}

.items .lines{ 
    height: 42px;
    line-height: 43px;
    border-bottom: 1px dashed #d4edf3;
    color: #999;
}


.items .ed{
    border-bottom: none;
}


.items .lines span{
    margin-right: 14px;
}



.items .lines a{
    margin: 0 10px;
    color: #333;
}

.items .lines a:hover{
    color: #3dabc7;
}

.items .lines .active{
    color: '#3dabc7';
}


.pickA{
    float: left;
    position: relative;
    height: 24px;
    line-height: 24px;
    border: 1px solid #28a5c4;
    color: #28a5c4;
    padding: 0 24px 0 6px;
    margin-right: 16px;
    display: none;

}

.exit{
    content: 'x';
    position: absolute;
    width: 18px;
    height: 18px;
    background: #28a5c4;
    color: #fff;
    font-size: 14px;
    font-style: normal;
    text-align: center;
    line-height: 18px;
    right: 3px;
    top: 2px;
    cursor: pointer;
}


.pickA:hover{
    color: #196c81;
    border-color: #196c81;
}

.pickA:hover .exit{
    background: #196c81;
}

 

 

//数据


var data = [

    [ 
        '苹果', '小米', '荣耀', '魅族', '华为','三星',
        'OPPO', 'vivo', '乐视', '360','中兴','索尼'
    ],
    [
        '3.0英寸以下',
        '3.0-3.9英寸',
        '4.0-4.5英寸',
        '4.6-4.9英寸',
        '5.0-5.5英寸',
        '6.0英寸'
    ],
    [
        '安卓(Android)',
        '苹果(IOS)',
        '微软(Window Phone)',
        '无',
        '其他'
    ],
    [
        '联通3G',
        '联通4G',
        '双卡单4G',
        '双卡双4G',
        '电信3G',
        '电信4G',
        '移动3G',
        '移动4G'
    ]


]




/*

FormatDate构造器使用方法:

var obj = new FormatDate(父容器);

obj.addNewSort(分类条目);
obj.deleteSort(分类条目);
obj.addGoods(商品条目);




FormatDate:

    this.pickCon
        用来找到存放pickA的容器

    this.state
        状态对象,pickA的改变会反应到这里面来

    this.items
        用来找到存放类别的容器

    this.pickConChild
        容器,用来存放pickA

    this.itemsChild
        容器,用来存放分类

    this.active
        样式对象

    this.normal
        样式对象



原型有四个方法:

addNewSort:
    任务一
    *增加新分类,参数是任意个字符串或一个数组
    *创建对应数量的div,对应数量的span
    *span用来存放分类名称,名称是参数
    *div的第一个子节点是span
    *div要插入到this.items里

    任务二
    *根据分类个数(参数个数),创建对应的pickA的数量
    *pickA插入对应的位置
    *将创建的pickA存进一个副本里,后期用要这个副本

    任务三
    *调用closePickA,触发pickA的关闭按钮



deleteSort:
    任务:
    *用来删除已有的分类



addGoods:
    任务一:
    *必须接收分类名称参数且作为第一个参数传入
    *第一个参数后面的参数不限,可以为任意个字符串,或一个数组
    *根据第一个参数后接的参数数目来新增商品条目


    任务二:
    *为每个创建的商品条目绑定点击事件alertState


deleteGoods:
    任务:
    *用来删除已有的商品



alertState:

    必须接收三个参数,
        ele:要改变样式的标签
        preEle:上一个标签
        key:属于哪个分类

    任务一:
    *改变点击的商品的颜色并加粗
    *取消上一个点击的商品的颜色,取消加粗

    任务二:
    *提取点击的商品的名称(innerHTML)
    *用提取的名称插入对应的pickA
    *通过key来判断要插入哪个pickA里

    任务三:
    *通过key更新this.state对应属性的状态



closePickA:
    不需要参数

    任务:
    *通过枚举给每个pickA的“叉叉”添加点击事件
    *点击使pickA隐藏
    *通过相同名称的key是商品中已被点击的商品恢复原样
    *更新this.state状态里对应的属性,使其值为null





*/







function FormatData(parent){
    
    this.pickCon = $class('div','pickCon',parent)[0];    //找到“你的选择”那个框框
    this.state = {};        //存储状态
    this.items = $class('div','items',parent)[0];        //找到类别
    this.pickConChild = {};        //中介容器,用来存放“你的选择”那个框框里面的小按钮
    this.itemsChild = {};        //中介容器,用来存放分类
    //更改css
    this.active = {
        'color':'#3dabc7',
        'fontWeight':'bold'
    };
    this.normal = {
        'color':'#333',
        'fontWeight':'normal'
    };

}



FormatData.prototype = {


    //addNewSort增加一个新类别
    addNewSort:function(){

        /*
        格式化接收的参数
        接收的参数可以是任意个以逗号分隔的字符串,也可以是一个数组
        如果是单个的字符串要将它们拼成一个数组
        */
        var arr = [],
            newNode = [];
        if( arguments.length == 1 && Array.isArray( arguments[0] ) ){
            arr = arguments[0];
        }else{
            for(var i=0,len=arguments.length; i<len; i++){
                arr.push( arguments[i] );
            }
        }



        /*
        根据传入参数的数量生成div
        类别的名字用span装起来作为div的第一个子节点;

        根据传入参数的数量生成pickA(“你的选择”后面的小框框)
        现在生成,后期通过改变display或opacity来让它们隐藏显示,不用增删节点的方法
        */
        for(var i=0,len=arr.length; i<len; i++){
            //创造节点
            var eDiv = document.createElement('div'),
                eSpan = document.createElement('span'),
                etxt = document.createTextNode(arr[i]);

            //追加内容,设置节点
            eSpan.appendChild(etxt);
            eDiv.setAttribute('name', arr[i]);
            eDiv.className = 'lines';
            eDiv.appendChild(eSpan);

            //this.items是类别div的父容器
            this.items.appendChild(eDiv);

            //创造节点
            var ePickA = document.createElement('a'),
                ePickSpan = document.createElement('span'),
                ePickI = document.createElement('i'),
                ePickITxt = document.createTextNode('X');

            //设置节点
            ePickA.setAttribute('href','javasrcit:;');
            ePickA.className = 'pickA';
            ePickI.className = 'exit';


            /*
            追加节点,this.pickCon是“你的选择”后面的一个容器用来存放pickA,
            this.pickConChild是这些节点的副本,为了方便后面的使用
            */        
            ePickI.appendChild(ePickITxt);
            ePickA.appendChild(ePickSpan);
            ePickA.appendChild(ePickI);
            this.pickCon.appendChild(ePickA);
            this.pickConChild[arr[i]] = ePickA;
            this.itemsChild[arr[i]] = eDiv;

        }

        this.closePickA();

    },


    /*
    删除分类
    str是要删除分类的名字
    */
    deleteSort:function(str){
        var oDiv = this.items.getElementsByTagName('div');
        
        for(var i=0,len=oDiv.length; i<len; i++){
            if( oDiv[i].name == str ){
                this.items.removeChild( oDiv[i] );
                break;
            }
        }
    },


    /*
    增加商品
    第一个参数之后都是商品的名称,可以是分开的字符串,也可以是一个数组
    第一个参数是商品的分类,规定后面的商品要插入到那个类别里去
    */
    addGoods:function(){

        //将接收的参数格式化成数组
        var arr = [],
            oDiv = this.items.getElementsByTagName('div');

        if( arguments.length == 2 && Array.isArray( arguments[1] ) ){

            arr = arguments[1];

        }else{
            for(var i=1,len=arguments.length; i<len; i++){
                arr.push( arguments[i] );
            }
        }
        //END将接收的参数格式化成数组


        //this.itemsChild是一个以分类为属性名的数组
        for(var j=0,jLen=arr.length; j<jLen; j++){

            var eA = document.createElement('a'),
                eAtxt = document.createTextNode(arr[j]),
                key = arguments[0];
            eA.setAttribute('href','javasrcit:;');
            eA.setAttribute('name',arr[j]);

            eA.appendChild(eAtxt);
            this.itemsChild[key].appendChild(eA);


            /*
            为每一个在货架上的商品添加一个点击事件调用alertState方法,
            不过onclick绑定的函数的this指向的是eA,所以要在外面先存一下this值,在里面才可以正确访问

            */
            var outThis = this;

            eA.onclick = function(){
                /*
                outThis.state[i]即this.state,是一个数组,用来存放商品选中的状态,
                如果选择分类中的某一项就把它添加到outThis.state数组对应的位置,
                本例题只有4个分类,所以outThis.state的长度也是4
                */

                //禁止重复点击
                if(this == outThis.state[key]){
                    return;
                }


                outThis.alertState(this,outThis.state[key],key);
                outThis.state[key] = this;

            }

        }//for结束大括号

    },


    deleteGoods:function(){
        //将接收的参数格式化成数组
        var arr = [],
            key = arguments[0];

        if( arguments.length == 2 && Array.isArray( arguments[1] ) ){

            arr = arguments[1];

        }else{
            for(var i=1,len=arguments.length; i<len; i++){
                arr.push( arguments[i] );
            }
        }


        var parent = this.itemsChild[key];

        var tGoods = parent.getElementsByTagName('a');

        for(var i=0,len=tGoods.length; i<len; i++){
            for(var j=0,jLen=arr.length; j<jLen; j++){
                if( tGoods[i].getAttribute('name') == arr[j] ){
                    console.log('yes');
                    parent.removeChild(tGoods[i]);
                    arr.splice(j,1);
                    jLen = arr.length;
                    i--;
                    len--;
                }
            }
            
        }



        //END将接收的参数格式化成数组
    },



    //alertState用来改变被点中的商品的样式,“你的选择”那里的样式
    alertState:function(ele,preEle,key){

        //判断有没有上一个,如果有其颜色设为初始值
        if(preEle){
            for(var attr in this.normal){
                preEle.style[attr] = this.normal[attr];
            }
        }

        //ele为当前被点击的商品,改变其颜色并加粗
        for(var attr in this.active){
            ele.style[attr] = this.active[attr];
        }

        //更新状态对象this.state
        this.state[key] = ele;

        this.pickConChild[key].style.display = 'block';
        this.pickConChild[key].getElementsByTagName('span')[0].innerHTML = ele.innerHTML;

    },



    closePickA:function(){

        //为每个ePickA的ePickI(叉叉用来关闭的)绑定一个点击事件,用来关闭pickA。

        for(var key in this.pickConChild){


            var outThis = this;
            


            (function(key){

                outThis.pickConChild[key].getElementsByTagName('i')[0].onclick = function(){

                    outThis.pickConChild[key].style.display = 'none';

                    for(var attr in outThis.normal){
                        outThis.state[key].style = outThis.normal[attr];
                    }

                    outThis.state[key] = null;

                }

            })(key);

        }

    },


}






var phones = $('phones');

var obj = new FormatData(phones);


obj.addNewSort('品牌','尺寸','系统','网络');

obj.addGoods('品牌',data[0]);
obj.addGoods('尺寸',data[1]);
obj.addGoods('系统',data[2]);
obj.addGoods('网络',data[3]);
obj.deleteGoods('网络');

 

posted @ 2018-09-14 19:22  蜜蜂老牛黄瓜  阅读(619)  评论(0编辑  收藏  举报