购物车: 数量组件:
class StepNum { constructor(parent, data) { this._data = data; this.num = data.num; this.step = 1; this.evt = new Event(StepNum.CHANGENUM); // this.flag = false; if (!parent) parent = document.body; this.div = this.createDiv(parent); this.animate(); } createDiv(parent) { if (this.div) return this.div; let div = document.createElement('div'); div.className = 'quantity-form'; div.evt = new Event(StepNum.CHANGENUM); Object.assign(div.style, { // fontSize: '0', textAlign: 'center', position: 'absolute', overflow: 'hidden', }) let divCon = document.createElement('div'); let p1 = document.createElement('p'); p1.textContent = this.num; // let p2 = document.createElement('p'); // p2.textContent = 2; Object.assign(divCon.style, { position: 'absolute', top: '0', left: '17px', width: '47px', backgroundColor: '#fff', textAlign: 'center', fontSize: '13px', }) Object.assign(p1.style, { margin: '0', lineHeight: '22px', }) divCon.appendChild(p1); div.appendChild(divCon); this.leftBtn = this.createBtn(div); this.leftBtn.className = 'reduce'; this.leftBtn.textContent = '-'; this.leftBtn.disabled = this.num>1?false:true; // this.leftBtn.disabled = true; // this.createNumInput(div); this.input = this.createNumInput(div); this.rightBtn = this.createBtn(div); this.rightBtn.className = 'increase'; this.rightBtn.textContent = '+'; // this.leftBtn.input = input; // this.rightBtn.input = input; this.leftBtn.divCon = divCon; this.rightBtn.divCon = divCon; divCon.input = this.input; this.input.divCon = divCon; // divCon.add = true; divCon.self = this; div.self = this; parent.appendChild(div); return div; } setNum(num) { this.input.value = num; this.num = num; this.div.firstElementChild.firstElementChild.textContent = num; // this.evt = new Event(StepNum.CHANGENUM); // 触发事件,传递参数 this.evt.data = this._data; this.evt.num = num; document.dispatchEvent(this.evt); } createBtn(parent) { let btn = document.createElement('button'); let btnStyle = { outline: 'none', border: '1px solid #ddd', padding: '0', height: '22px', width: '17px', cursor: 'pointer', position: 'relative' }; Object.assign(btn.style, btnStyle); parent.appendChild(btn); btn.addEventListener('click', this.clickHandler); this.end = true; return btn; } clickHandler(e) { this.divCon.style.opacity = '1'; if (this.parentNode.self.flag === true) return; // 隐藏input的value值 this.parentNode.self.input.value = ''; this.parentNode.self.input.style.backgroundColor = 'rgba(255,255,255,.3)'; // 点击加减 // 点击 - 就在p的前面增加一个p top 移动到 0px 然后删除最后一个p // 点击 + 就在p的后面增加一个p top移动到-22px 然后删除第一个p if (this === this.parentNode.self.leftBtn) { // if(this.parentNode.self.num<2){ // this.disabled = true; // return; // }; this.divCon.style.top = '-22px'; // this.divCon.flag = false; this.parentNode.self.num--; let p2 = document.createElement('p'); Object.assign(p2.style, { margin: '0', lineHeight: '22px', }) p2.textContent = this.parentNode.self.num; this.divCon.insertBefore(p2, this.divCon.firstElementChild); this.divCon.add = false; if (this.parentNode.self.num === 1) { this.disabled = true; } // // this.disabled = false; // } else { // 点击了+ // this.divCon.flag = true; this.parentNode.self.leftBtn.disabled = false; this.parentNode.self.num++; let p2 = document.createElement('p'); Object.assign(p2.style, { margin: '0', lineHeight: '22px', }) p2.textContent = this.parentNode.self.num; this.divCon.add = true; this.divCon.appendChild(p2); // this.divCon.style.top = '-22px'; // // 1s 后删除 } // console.log(this.parentNode.self); this.parentNode.self.flag = true; // 修改数据 this.parentNode.self._data.num = this.parentNode.self.num; this.parentNode.self._data.sum = Number(this.parentNode.self._data.num)*Number(this.parentNode.self._data.price); // this.parentNode.self.end = false; // 抛出 // this.parentNode.self.evt.data = this.parentNode.self._data; // this.parentNode.self.evt.num = num; // document.dispatchEvent(this.parentNode.self.evt); } createNumInput(parent) { let input = document.createElement('input'); // input.value = 1; Object.assign(input.style, { outline: 'none', textAlign: 'center', padding: '0', boxSizing: 'border-box', border: '1px solid #ddd', height: '22px', width: '46px', backgroundColor: 'rgba(255,255,255,.3)', borderLeftWidth: '0', borderRightWidth: '0', position: 'relative', color: 'rgba(0,0,0,.8)', fontSize: '13px' }), parent.appendChild(input); input.addEventListener('blur', this.blurHandler); input.addEventListener('focus', this.focusHandler); return input; } focusHandler(e) { this.value = ''; this.style.backgroundColor = 'rgb(255,255,255)'; this.style.opacity = 1; this.divCon.style.opacity = '0'; } blurHandler(e) { // 改变数据时将数据抛出; // 是数字 且大于1 if (isNaN(this.value) || parseInt(this.value) < 1 || !this.value) { if(!this.parentNode.self.note){ this.parentNode.self.note = this.parentNode.self.createNote(this.parentNode.self.div); console.log(this.parentNode.self.note); } this.parentNode.self.note.style.display = 'block'; this.parentNode.self.setNum(1); // this.value = 1; this.parentNode.self.leftBtn.disabled = true; } else if (this.value === '1') { this.parentNode.self.setNum(1); this.parentNode.self.leftBtn.disabled = true; } else { this.parentNode.self.setNum(this.value); this.parentNode.self.leftBtn.disabled = false; } } createNote(parent, text = '商品数量超限') { if(this.note) return this.note; let div = document.createElement('div'); Object.assign(div.style, { backgroundColor: '#fff', border: '1px solid #ddd', textAlign: 'center', position: 'fixed', zIndex: '1000', transform: 'translate(-50%,-50%)', left: '50%', top: '50%', display:'none' }) let p = document.createElement('p'); p.textContent = text; p.style.margin = '60px 60px 0'; let pCon = document.createElement('p'); pCon.style.margin = '40px'; let a = document.createElement('a'); a.addEventListener('click', function () { this.parentElement.parentElement.style.display = 'none'; }) Object.assign(a.style, { textDecoration: 'none', backgroundColor: 'red', display: 'inline-block', padding: '10px 20px', color: 'rgb(255, 255, 255)', }) a.href = 'javascript:;'; a.textContent = '知道了'; Object.assign(a.style, { backgroundColor: 'red', padding: '10px 20px', color: '#fff', }) pCon.appendChild(a); div.appendChild(p); div.appendChild(pCon); parent.appendChild(div); return div; } static get CHANGENUM() { return 'changenum'; } animate() { requestAnimationFrame(this.animate.bind(this)); // 当点击了按钮时 改变flag; if (!this.flag) return; if (this.div.firstElementChild.add) { // + if (this.div.firstElementChild.offsetTop <= -22) { console.log('滚动结束'); this.flag = false; this.div.firstElementChild.firstElementChild.remove(); this.div.firstElementChild.style.top = '0'; this.div.evt.data = this._data; this.div.evt.num = this.div.firstElementChild.firstElementChild.textContent; document.dispatchEvent(this.div.evt); return; } this.div.firstElementChild.style.top = this.div.firstElementChild.offsetTop - this.step + 'px'; } else { // - if (this.div.firstElementChild.offsetTop >= 0) { this.flag = false; this.div.firstElementChild.lastElementChild.remove(); this.div.firstElementChild.style.top = '0'; this.div.evt.data = this._data; this.div.evt.num = this.div.firstElementChild.firstElementChild.textContent; document.dispatchEvent(this.div.evt); return; } this.div.firstElementChild.style.top = this.div.firstElementChild.offsetTop + this.step + 'px'; } } }购物列表:
class GoodsItem{ constructor(_props,parent){ this.prop = _props; this.goods=this.initGoods(parent); this.render(_props); } initGoods(parent){ if(this.goods) return this.goods; let div=document.createElement("div"); Object.assign(div.style,{ width: "290px", height: "390px", marginLeft: "10px", marginBottom: "20px", backgroundColor: "white", position: "relative", float:"left" }); this.createImgGoods(div); this.createPriceCon(div); parent.appendChild(div); return div; } createImgGoods(parent){ let div=document.createElement("div"); Object.assign(div.style,{ textAlign: "center", height: "330px", width: "100%", position: "relative" }); this.goodsImg=new Image(); Object.assign(this.goodsImg.style,{ width:'85%', marginTop: "20px", transition: "all 1s", }); this.goodsInfo=document.createElement("p"); Object.assign(this.goodsInfo.style,{ fontSize: "14px", width: "230px", overflow: "hidden", height: "40px", left: 0, right: 0, margin: "auto", position: "absolute", lineHeight: "22px", bottom: "10px" }); div.appendChild(this.goodsImg); div.appendChild(this.goodsInfo); div.addEventListener("mouseenter",this.mouseHandler); div.addEventListener("mouseleave",this.mouseHandler); parent.appendChild(div); } createPriceCon(parent){ let div=document.createElement("div"); Object.assign(div.style,{ border: "2px solid #e01222", height: "58px", width: "286px", bottom: 0, position: "absolute" }); let priceDiv=document.createElement("div"); this.goodsBn=document.createElement("div"); Object.assign(this.goodsBn.style,{ width: "87px", height: "58px", position: "absolute", right: 0, top:0, color: "white", fontSize: "16px", textAlign: "center", lineHeight: "58px", cursor: "pointer", backgroundColor: "#e01222" }); this.goodsBn.textContent="立即抢购"; this.goodsBn.self = this; this.createPriceDiv(priceDiv); div.appendChild(priceDiv); div.appendChild(this.goodsBn); this.goodsBn.addEventListener("click",this.clickHandler); parent.appendChild(div); } createPriceDiv(parent){ let priceDivs=document.createElement("div"); let soldDivs=document.createElement("div"); this.nowPrice=document.createElement("span"); this.initPrice=document.createElement("span"); priceDivs.appendChild(this.nowPrice); priceDivs.appendChild(this.initPrice); this.percent=document.createElement("span"); let percentCon=document.createElement("span"); this.soldPercent=document.createElement("span"); percentCon.appendChild(this.soldPercent); soldDivs.appendChild(this.percent); soldDivs.appendChild(percentCon); Object.assign(this.percent.style,{ fontSize: "16px", marginLeft:"10px", lineHeight:"10px" }); Object.assign(this.nowPrice.style,{ fontSize: "24px", color: "#e01222", marginLeft: "5px", lineHeight: "35px" }); Object.assign(this.initPrice.style,{ fontSize: "14px", textDecoration: "line-through", marginLeft: "5px" }); Object.assign(percentCon.style,{ display: "inline-block", width: "100px", height: "10px", border: "1px solid #e01222", marginLeft:"20px", marginTop:"5px", position: "absolute" }); Object.assign(this.soldPercent.style,{ position: "absolute", width: "0%", height: "10px", top:0, bottom: 0, backgroundColor: "#e01222" }); parent.appendChild(priceDivs); parent.appendChild(soldDivs); } mouseHandler(e){ if(e.type==="mouseenter"){ this.firstElementChild.style.marginTop="0px"; }else if(e.type==="mouseleave"){ this.firstElementChild.style.marginTop="20px"; } } clickHandler(e){ // 点击添加购物车,这里是要向外抛发事件,并且用event将数据传出; let evt = new Event(GoodsItem.ADD_SHOPPING_CAR); evt.data = this.self.prop; document.dispatchEvent(evt); } render(_props){ this.goodsImg.src=_props.icon; this.goodsInfo.textContent=_props.goods; this.nowPrice.textContent="¥"+_props.nowPrice; this.initPrice.textContent="¥"+_props.initPrice; this.goodsBn.id=_props.id; if(_props.sold>1) return; this.percent.textContent=(_props.sold*100).toFixed(2)+"%"; this.soldPercent.style.width=_props.sold*100+"%"; } static get ADD_SHOPPING_CAR(){ return 'add_shopping_car'; } }购物页面:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>类与其他之间的事件抛发</title> <script src="./goodItem.js"></script> <scrip src="./stepNum.js"></scrip> </head> <body> <div> <button> 进入购物车 </button> <!-- <a href='' target="_blank">进入购物车</a>--> </div> <script> let shoppinglist = []; let data=[ {id:1001,icon:"imgs/03.jpg",goods:"德国DMK进口牛奶 欧德堡(Oldenburger)超高温处理全脂纯牛奶1L*12盒",info:"1L*12",nowPrice:139,initPrice:156,sold:0.5}, {id:1002,icon:"imgs/04.jpg",goods:"帮宝适(Pampers)超薄干爽绿帮纸尿裤L164片(9-14kg)大码 尿不湿箱装",info:"L【9-14kg】",nowPrice:225,initPrice:275,sold:0.5}, {id:1003,icon:"imgs/05.jpg",goods:" 意大利进口 百乐可(BALOCCO) 千层酥饼 脆皮酥薄脆饼干 焦糖味 200g",info:"焦糖脆皮酥200g",nowPrice:27.9,initPrice:40,sold:0.5}, {id:1004,icon:"imgs/06.jpg",goods:"百草味 坚果零食干果 每日坚果 奶油味夏威夷果200g/袋(内含开果器)",info:"夏威夷果奶油味200g/袋",nowPrice:16.9,initPrice:30,sold:0.5}, {id:1005,icon:"imgs/07.jpg",goods:"三星 Galaxy S10 8GB+512GB炭晶黑(SM-G9730)3D超声波屏下指纹超感官全视屏骁龙855双卡双待全网通4G游戏手机 ",info:"炭晶黑\n8GB+512GB",nowPrice:7699,initPrice:9899,sold:0.5}, {id:1006,icon:"imgs/01.jpg",goods:"罗技(G)G102 游戏鼠标 8000DPI RGB鼠标 黑色 吃鸡鼠标 绝地求生",info:"G102有线游戏鼠标 黑色",nowPrice:119,initPrice:146,sold:0.5}, {id:1007,icon:"imgs/02.jpg",goods:"联想(Lenovo)拯救者Y7000英特尔酷睿i5 15.6英寸游戏笔记本电脑( i5-8300H 8G 512G SSD GTX1050 黑)",info:"Y7000【1050 i5 512",nowPrice:5699,initPrice:6200,sold:0.5} ]; let drag = document.createDocumentFragment(); for(let i =0;i<data.length;i++){ let good = new GoodsItem(data[i],drag); } document.body.appendChild(drag); document.addEventListener(GoodsItem.ADD_SHOPPING_CAR,addcarHandler) function addcarHandler(e) { // 添加商品 let prop = e.data; // 如果商品id存在 let item = shoppinglist.filter((i)=>{ return i.id === prop.id; }) // 找不到值返回空数组 if(!item.length){ let good = { selected:false, id:prop.id, icon:prop.icon, goods:prop.goods, info:prop.info, price:prop.nowPrice, num:1, sum:prop.nowPrice, delete:false } shoppinglist.push(good); }else{ // 浅拷贝 item[0].num++; } // 渲染商品购物车 } let a = document.querySelector('button'); a.addEventListener('click',clickEvent); function clickEvent(e) { let url = 'carpage.html?list='+encodeURI(JSON.stringify(shoppinglist)); location.href = url; } </script> </body> </html>购物车表格:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>购物车页面</title> <script src="stepNum.js"></script> <style> table td{ /*display: inline-block;*/ } </style> </head> <body> <script> let url = location.search; let deUrl = decodeURI(url) let data =JSON.parse(deUrl.split('?')[1].split('=')[1]); let table; let tdWidth = [40,82,220,170,160,80,140,75]; let inputs; init(); function init() { clearTable(table); table = createTable(document.body,data); inputs = document.querySelectorAll('input[type="checkbox"]'); document.addEventListener(StepNum.CHANGENUM,stepNumHandler); } function stepNumHandler(e) { // 在data中找到e.data,修改里面的num data.forEach((i)=>{ if(i.id === e.data.id){ i.num = e.data.num; i.sum = e.data.sum; } }) clearTable(table); table = createTable(document.body,data); } function createTable(parent,data) { let table = document.createElement('table'); Object.assign(table.style,{ borderCollapse:'collapse', border:'1px solid #ddd', width:'990px' }) createTableHead(table); createTableBody(table,data); parent.appendChild(table); return table; } function createTableBody(parent,data) { let fragment = document.createDocumentFragment(); for(let i =0;i<data.length;i++){ let tr = document.createElement('tr'); Object.assign(tr.style,{ verticalAlign: 'top', border: '1px solid #ddd', backgroundColor: 'antiquewhite', fontSize: '13px' }) for(let prop in data[i]){ if(prop === 'id')continue; let td = document.createElement('td'); td.style.textAlign = 'center'; td.style.padding = '20px 0'; switch (prop) { case 'selected': let input = document.createElement('input'); input.type = 'checkbox'; input.checked = data[i][prop]; input.addEventListener('click',inputSelectHandler) Object.assign(input.style,{ verticalAlign: 'baseline', height: '10px', marginRight: '20px', marginLeft:'10px', textAlign:'left', }) td.appendChild(input); td.style.width = '60px'; td.style.textAlign = 'left'; break; case 'icon': let img = new Image(); img.src = data[i][prop]; img.width = 100; td.appendChild(img); td.style.textAlign = 'left'; td.style.width = '200px'; break; case 'num': // parent,data let stepNum = new StepNum(td,data[i]); Object.assign(td.style,{ width: '80px', textAlign: 'center' }) break; case 'sum': td.style.width = '100px'; case 'price': td.textContent ='¥'+data[i][prop].toFixed(2); break; case 'delete': td.textContent = '删除'; td.style.padding='20px'; td.addEventListener('click',delHandler); td.style.cursor = 'pointer'; break; case 'goods': data[i][prop]=data[i][prop].slice(0,36); td.style.width = '200px'; td.textContent = data[i][prop]; td.style.textAlign = 'left'; break; default: td.textContent = data[i][prop]; break; } tr.appendChild(td); } fragment.appendChild(tr); } parent.appendChild(fragment); } function inputSelectHandler(e) { let flag = true; for(let i =1;i<inputs.length;i++){ if(inputs[i].checked === false){ flag = false; break; } } inputs[0].checked = flag; } function delHandler(e) { // 将data中对应的datalist 删除 let trs = Array.from(table.children); let idx = trs.indexOf(this.parentNode); data = data.filter((i,index)=>{ return index !== idx-1; }) this.parentNode.remove(); } function createTableHead(parent) { let thlist = ['全选','商品','单价','数量','小计','操作']; let tr = document.createElement('tr') for(let i =0;i<thlist.length;i++){ let th = document.createElement('th'); Object.assign(th.style,{ textAlign:'center', padding:'10px 0', fontSize:'14px', fontWeight:'normal' }) if(thlist[i]==='商品'){ th.colSpan = 2; th.style.textAlign = 'left'; } th.textContent = thlist[i]; if(thlist[i]==='全选'){ let selectAll = document.createElement('input'); selectAll.addEventListener('click',selectAllHandler); selectAll.type = 'checkbox'; Object.assign(selectAll.style,{ verticalAlign: 'baseline', height: '10px', marginRight: '20px', marginLeft:'10px' }) th.colSpan = 2; th.style.textAlign = 'left'; th.insertBefore(selectAll,th.firstChild); } tr.appendChild(th); } parent.appendChild(tr); } function selectAllHandler(e) { // let inputs = document.querySelectorAll('input[type="checkbox"]'); for(let i =1;i<inputs.length;i++){ inputs[i].checked = this.checked; } } function clearTable(table) { if(table){ table.remove(); } } </script> </body> </html>