需求
需求分析:
品牌,系统,尺寸,网络这些类别下的子选项只能单选,
你的选择处的标签应该按照下面的类别的顺序排好,
实现思路:
这一题我用的面向对象的方式写的,有以下这些方法:
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('网络');