产品文档
商城排序的产品需求文档(PRD ProductionRequestDescription)是由产品经理产出的描述应用功能的;我们的排期,技术方案都要根据这个文档产出
- 做一个华为手机的电商页面
- 页面中的数据是动态获取的(如有新手机发布,页面上要自动展示新手机)
- 点击商家时间、价格、热度、销量可以按照相应的维度排序
- 第一次点击按钮升序排序,再次点击该按钮降序排序
- 点击某个维度后,如价格,再次点击该维度是降序,如果第一次点击的是价格,第二次点击销量按照销量升序排列
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>商城排序DEMO</title>
<!-- IMPORT CSS -->
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<!-- BT 中建议所有内容都存放在 CONTAINER 容器中 -->
<div class="container">
<!-- NAV导航 -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="javascript:;">商城排序</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav">
<!-- DATA-PAI记录的是每个按钮应该按照哪个维度进行排序 -->
<li class="nav-item" data-pai="data-time">
<a class="nav-link" href="javascript:;">上架时间</a>
</li>
<li class="nav-item" data-pai="data-price">
<a class="nav-link" href="javascript:;">价格</a>
</li>
<li class="nav-item" data-pai="data-hot">
<a class="nav-link" href="javascript:;">热度</a>
</li>
</ul>
</div>
</nav>
<!-- CARD-LIST商品列表 -->
<div class="card-deck">
<!-- <div class="card">
<img class="card-img-top" src="img/1.jpg" alt="">
<div class="card-body">
<h6 class="card-title">HUAWEI Mate 10 4GB+64GB 全网通版(亮黑色)</h6>
<p class="card-text">价格:¥5000</p>
<p class="card-text">好评:100000</p>
<p class="card-text"><small class="text-muted">上架时间:2019-08-07</small></p>
</div>
</div> -->
</div>
</div>
<!-- IMPORT JS -->
<script src="js/index.js"></script>
</body>
</html>
js
~ function () {
let _DATA = null;
//第一步:从服务器获取数据(AJAX),然后绑定在页面中
// 1. 基于 AJAX 获取服务器端数据,把数据存储到 DATA 中
let xhr = new XMLHttpRequest;
// 创建 AJAX 的实例
xhr.open('GET', 'json/product.json', false);
// 打开一个请求的链接,基于 GET 请求和同步编程完成
xhr.onreadystatechange = function () {
if (xhr.status === 200 && xhr.readyState === 4)
// 监听服务器返回的状态信息(在 HTTP 状态码为200,请求状态为4的时候能拿到数据
{
_DATA = xhr.responseText;
// 基于 responseText 获取响应回来的信息(JSON 字符串)
}
};
xhr.send();
// 发送 AJAX 请求
_DATA = JSON.parse(_DATA); //=>_DATA:获取的都是 JSON 字符串,我们要让其变为对象
// 第二步:把获取的数据绑定在页面中
// 根据获取的 DATA:DATA 当中有多少项,我就动态创建出多少个 card 盒子(项目中都是基于字符串拼接的方式,把需要创建的 card 拼出来
let htmlStr = ``;
_DATA.forEach(item => {
// ITEM 是每一项(对象),包含需要展示的每一个产品的详细信息:我们需要拿出每一项信息来展示到页面中(拼到模板字符串中)
let {title, price, time, hot, img} = item;
// 基于解构赋值获取信息
htmlStr += `<div class="card"
data-price="${price}"
data-time="${time}"
data-hot="${hot}">
<img class="card-img-top" src="${img}" alt="">
<div class="card-body">
<h6 class="card-title">${title}</h6>
<p class="card-text">价格:¥${price}</p>
<p class="card-text">好评:${hot}</p>
<p class="card-text"><small class="text-muted">上架时间:${time}</small></p>
</div>
</div>`;
});
// 把需要的数据绑定在元素 card 的自定义属性 data-xxx 上(后期需要这些数据,直接基于自定义属性获取即可)
let cardDeck = document.querySelector('.card-deck');
cardDeck.innerHTML = htmlStr;
// 把拼接好的 card 字符串,放到页面指定容器中(card-deck)
// 第三步:点击实现升降序排序
let navList = document.querySelectorAll('.navbar-nav li'),
cardList = cardDeck.querySelectorAll('.card');
// 循环给所有的按钮绑定点击事件,点击的时候按照指定的规则排序
for (let i = 0; i < navList.length; i++) {
let item = navList[i];
item['data-type'] = -1; // 控制升降序
item.onclick = function () {
// 点击当前的某个按钮,让其按照升降序切换,而其余的都应该回归原始-1
[].forEach.call(navList, item => (item === this ? this['data-type'] *= -1 :
item['data-type'] = -1));
cardList = [].slice.call(cardList, 0);
cardList.sort((next, cur) => {
// 获取当前按钮记录的排序方式 data-time / data-price / data-hot
let pai = this.getAttribute('data-pai');
cur = cur.getAttribute(pai);
next = next.getAttribute(pai);
if (pai === "data-time") {
// 获取的是日期数据:我们要把字符串中的 “-” 给去掉
cur = cur.replace(/-/g, '');
next = next.replace(/-/g, '');
}
return (next - cur) * this['data-type'];
});
cardList.forEach(item => cardDeck.appendChild(item));
}
}
/*
// 给价格按钮绑定点击事件
// 给按钮设置一个自定义属性 DATA-TYPE 存储排序方式:-1降序 1升序
navList[1]['data-type'] = -1;
navList[1].onclick = function () {
// 控制升降序切换
this['data-type'] *= -1;
// 把元素集合转换为数组,让其按照价格进行排序
cardList = Array.prototype.slice.call(cardList, 0);
cardList.sort((next, cur) => {
// 绑定数据的时候,把产品价格信息设置为元素的自定义属性,需要的时候获取
cur = cur.getAttribute('data-price');
next = next.getAttribute('data-price');
return (next - cur) * this['data-type'];
});
// 循环数组中的每一项,让其按照最新的顺序依次添加到页面中,完成页面排序
cardList.forEach(item => cardDeck.appendChild(item));
}
navList[2]['data-type'] = -1;
navList[2].onclick = function () {
// 控制升降序切换
this['data-type'] *= -1;
// 把元素集合转换为数组,让其按照价格进行排序
cardList = Array.prototype.slice.call(cardList, 0);
cardList.sort((next, cur) => {
// 绑定数据的时候,把产品价格信息设置为元素的自定义属性,需要的时候获取
cur = cur.getAttribute('data-hot');
next = next.getAttribute('data-hot');
return (next - cur) * this['data-type'];
});
// 循环数组中的每一项,让其按照最新的顺序依次添加到页面中,完成页面排序
cardList.forEach(item => cardDeck.appendChild(item));
} */
// 使用jQuery格式实现商城排序
// 通过单例模式来管理商城排序的代码
let shopModule = (function ($) {
// 想要操作谁就先获取谁(项目中尽可能把创建变量提前并放在一起)
let $navList = $('.navbar-nav li'), // $()是创建这个类的实例
$cardBox = $('.card-deck'),
$cardList = null,
_DATA = null;
// 从服务器获取数据
function queryData () {
// 基于JQ中的ajax方法获取数据
$.ajax ({
url:'json/product.json',
method:'GET',
async:false,
success:result => {
// 从服务器获取数据成功会执行success,result存储的就是获取到的数据,并且默认就已经
转换为 json 格式的对象
_DATA = result;
}
});
}
// 把数据绑定在页面中
function bindHTML () {
if (!_DATA) return;
let HTML = ``;
$.each(_DATA,(index, item) => {
let {title, img, price, hot, time} = item;
HTML += `<div class="card" time = "${time}" prict = "${price}" hot = "${hot}">
<img class="card-img-top" src="${img}" alt="">
<div class="card-body">
<h6 class="card-title">${title})</h6>
<p class="card-text">${price}</p>
<p class="card-text">${hot}</p>
<p class="card-text">
<small class="text-muted">${time}</small></p>
</div>`;
});
$cardBox.html(HTML);
// 获取所有的card
$cardList = $cardBox.children('.card');
}
// 实现商城排序
function sortHandle () {
$navList.attr('typeA', -1);
$navList.on(click, function () {
// this:当前点击的li(原生js对象)通过$(this)变成JQ对象
let $this = $(this),
pai = $this.attr('pai'); // time / price / hot
$this.attr('typeA', $this.attr('typeA') * -1).siblings().attr('typeA',-1);
$cardList.sort((A, B) => {
let $A = $(A),
$B = $(B);
$A = $A.attr(pai);
$B = $B.attr(pai);
pai === "time" ? ($A = $A.replace(/-/g,""), $B = $B.replace(/-/g,"")) : null;
return ($A - $B) * $this.attr('typeA');
});
$cardList.each((index, item) => $cardBox.append(item));
});
}
return {
// 在单例模式当中引用命令模式
// 当前模块的入口:想让商城排序开始执行,我们只需要执行init,在init中会按照顺序依次完成具体
// 的业务逻辑
init () { // 相当于 init:function () {}
queryData();
bindHTML();
sortHandle();
}
}
})(jQuery); // 传一个 jQuery 然后形参写 $,保证闭包里永远不受别的类库影响
shopModule.init();
}();