npm i bootstrap dist文件夹 当前模块编译后,供开发者调取使用的 bootstrap.css 没压缩 bootstrap.min.css 压缩的 UI组件库 所有的都自己写好了,我们只需要调用就可以了
数据绑定 1.基于AJAX(或者其他方式fetch/跨域)向服务器端发送请求,服务器把需要呈现的数据,返给我们 (JSON格式的数据) 2.客户端获取到数据后,基于相应的方法实现数据的绑定(把数据能够动态绑定在页面中,呈现给用户)
商城排序案例【利用数据驱动思想】
- 1.利用AJAX获取获取到JSON格式对象
- 2.利用拿到的JSON对象 进行渲染到页面
- 3.点击价格 时间 销量 进行排序
- 数据驱动思想:直接更改数据源【获取的JSON对象】
- 点击按钮排序完后,再次渲染页面
JSON
[
{
"id": 1,
"title": "HUAWEI Mate 10 4GB+64GB 全网通版(亮黑色)",
"price": 3899,
"time": "2017-03-15",
"hot": 198,
"img": "img/1.jpg"
},
{
"id": 2,
"title": "HUAWEI 麦芒6 4GB+64GB 全网通版(曜石黑)",
"price": 2399,
"time": "2017-02-08",
"hot": 25,
"img": "img/2.jpg"
},
{
"id": 3,
"title": "华为畅享7 2GB+16GB 全网通标配版(香槟金)",
"price": 899,
"time": "2017-01-25",
"hot": 568,
"img": "img/3.jpg"
},
{
"id": 4,
"title": "HUAWEI nova 2 Plus 4GB+64GB 全网通版(曜石黑)",
"price": 2399,
"time": "2016-12-30",
"hot": 20000,
"img": "img/4.jpg"
},
{
"id": 5,
"title": "HUAWEI nova 2 4GB+64GB 全网通版(玫瑰金)",
"price": 2199,
"time": "2016-01-30",
"hot": 1032654,
"img": "img/5.jpg"
},
{
"id": 6,
"title": "华为畅享7 Plus 4GB+64GB 全网通高配版(香槟金)",
"price": 1699,
"time": "2018-01-01",
"hot": 1,
"img": "img/6.jpg"
},
{
"id": 7,
"title": "HUAWEI nova 青春版 4GB+64GB 全网通版(樱语粉)",
"price": 1799,
"time": "2017-02-19",
"hot": 400,
"img": "img/7.jpg"
},
{
"id": 8,
"title": "HUAWEI P10 4GB+64GB 全网通版(曜石黑)",
"price": 3488,
"time": "2017-01-25",
"hot": 240,
"img": "img/8.jpg"
},
{
"id": 9,
"title": "HUAWEI P10 Plus 6GB+128GB 全网通版(钻雕金)",
"price": 4488,
"time": "2014-01-01",
"hot": 12345678,
"img": "img/9.jpg"
},
{
"id": 10,
"title": "HUAWEI Mate 9 保时捷设计 6GB+256GB 全网通版(曜石黑)",
"price": 8999,
"time": "2014-01-01",
"hot": 12345678,
"img": "img/10.jpg"
}
]
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>商城排序</title>
<!-- IMPORT CSS -->
<link rel="stylesheet" href="css/bootstrap.min.css" />
<link rel="stylesheet" href="css/index.css" />
</head>
<body>
<!-- 一般都是根据模块 最外层的增加id -->
<div class="container" id="productBox">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="javascript:;">商城排序</a>
<div class="collapse navbar-collapse show" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" data-pai="price" href="javascript:;">价格</a>
</li>
<li class="nav-item">
<a class="nav-link" data-pai="time" href="javascript:;">时间</a>
</li>
<li class="nav-item">
<a class="nav-link" data-pai="hot" href="javascript:;">热度</a>
</li>
</ul>
</div>
</nav>
<div class="content">
<!-- <div class="card">
<img src="https://res.vmallres.com/pimages//product/6941487220748/428_428_19EA25FFB51AB0AB79D82E3C8D317F67BEC741CBA0849F78mp.png"
class="card-img-top" alt="..." />
<div class="card-body">
<h5 class="card-title">HUAWEI Mate 40 Pro</h5>
<p class="card-text">价格:¥6999</p>
<p class="card-text">时间:2021-01-22</p>
<p class="card-text">热度:666</p>
</div>
</div> -->
</div>
</div>
<!-- IMPORT JS -->
<script src="index.js"></script>
</body>
</html>
CSS
.container{
/* background-color: lightcoral; */
min-width: 1000px;
}
.navbar-collapse{
flex-basis: 80%;
/* 设置弹性盒子初始长度 */
}
/* ul 里设置 */
.navbar-nav{
flex-direction: row;
}
.nav-item{
padding: 0 10px;
}
.content{
display: flex;
flex-wrap: wrap; /* 换行 */
justify-content: space-between;
}
.card{
width: 15rem;
margin-bottom: 10px;
}
.card.no_bg{
border: none;
background: none;
}
JS 使用单例模式
// =====使用高级单例模式
let shopModel = (function () {
// 1.获取数据,把获取到的数据渲染到页面上
let data;
const getData = function getData() {
let xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', './json/product.json', false); //false同步
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
data = JSON.parse(xmlHttp.responseText);
}
}
xmlHttp.send();
}
//2 根据数据渲染视图
const render = function render() {
let contet = document.querySelector('.content');
let str = '';
data.forEach(item => {
str += `<div class="card">
<img src="./${item.img}" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">${item.title}</h5>
<p class="card-text">价格:¥${item.price}</p>
<p class="card-text">时间:${item.time}</p>
<p class="card-text">销量:${item.hot}</p>
<a href="javascript:;" class="btn btn-primary">购买</a>
</div>
</div>`;
});
// 最后一行补齐
let num = 4 - data.length % 4;//num表示最后一行需要补上几个div
for (let i = 0; i < num; i++) {
str += `<div class="card no-bg"></div>`;
}
contet.innerHTML = str;
}
//清除其他按钮的排列效果
let clear = function (btns) {
//this是点击的按钮
//形参btns是所有的点击按钮
//让这个函数不是当前点击的按钮的元素flag=-1;
// ==相对等于 !=相对不等于 ===绝对等于 !==绝对不等于
btns.forEach(item => {
if (item !== this) {
item.flag = -1;
}
})
}
// 点击排序
const dealEle = function dealEle() {
let btns = document.querySelectorAll('.nav-link');
btns.forEach(ele => {
ele.flag = -1;//用flag判断升序还是降序
ele.onclick = function () {
//清除其他按钮的排列效果
clear.call(this, btns);
//让clear的this等于当前的点击按钮
//再把所有点击按钮的Btns传过去
let type = this.innerText;
switch (type) {
case '价格':
type = 'price';
break;
case '时间':
type = 'time';
break;
case '销量':
type = 'hot';
break;
default:
type = 'price';
};
//第一次点击 flag是-1 进来乘-1 变成1 {-1*1=-1}
//第二次点击 flag是1 进来还是乘以-1 变成-1 {-1*-1}=1
this.flag *= -1;
data.sort((a, b) => {
//处理time是字符串的特殊情况
a = (a[type] + '').replace(/-/g, '');
b = (b[type] + '').replace(/-/g, '');
return (a - b) * this.flag;
});
//把排好序的数组重新渲染到DOM上
render();
};
});
};
return {
init() {
//init执行就是把这三个函数执行
getData(); //获取数据
render(); //根据数据渲染视图
dealEle();//点击按钮排序
}
}
})();
//用ShopModel拿到init函数并且执行
shopModel.init();
JS 使用单例模式【命令模式】 加自定义属性
设置闭包 ,防止代码冲突 闭包的“保护” 高级单例设计模式 =>可以把当前版块中的一些内容暴露到外面,供其他版块使用
let productModel = (function () {
//操作谁获取谁
let productBox = document.querySelector('#productBox'),
navlinks = productBox.querySelectorAll('.nav-link'), //按钮
content = productBox.querySelector('.content'); //动态获取数据的位置
// 私有上下文当中的公共的
// data数据模型
let data;
//从服务器获取数据
const queryData = function queryData() {
let xhr = new XMLHttpRequest();
xhr.open("GET", './product.json', false); //同步
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {//status网络状态码
data = JSON.parse(xhr.responseText);
}
};
xhr.send();
};
// 根据数据渲染视图
const render = function render() {
let str = '';
data.forEach(item => {
let { title, price, time, hot, img } = item; //解构赋值
str += `<div class="card">
<img src="${img}"
class="card-img-top" alt="${title}" />
<div class="card-body">
<h5 class="card-title">${title}</h5>
<p class="card-text">价格:¥${price}</p>
<p class="card-text">时间:${time}</p>
<p class="card-text">热度:${hot}</p>
</div>
</div>`;
});
//最后一行
let num = 4 - data.length % 4;
for (let i = 0; i < num; i++) {
str += `<div class="card no_bg"></div>`;
}
content.innerHTML = str;
};
const clear=function clear(){
// 点击某个按钮,让除当前按钮之外的其他按钮,他们的排序表示都回归“-1” 这样后续再点击其它按钮的时候,才能从升序开始
navlinks.forEach(navlink=>{
if(this!==navlink){
navlink.flag=-1;
};
});
};
// 点击进行排序
const handle = function handle() {
// 循环
navlinks.forEach(navlink=>{
// 循环给每一个按钮,设置排序标识&事件绑定 会产生闭包
navlink.flag=-1;
navlink.onclick=function(){
clear.call(this);//让clear中的this也是当前点击的按钮
// this->当前点击的按钮
this.flag*=-1;
// pai->存储的是当前点击按钮该按照哪个字段排序
let pai=this.getAttribute('data-pai');
data.sort((a,b)=>{ //箭头函数没有this 箭头函数里的this是上级上下文中的this
a=a[pai];
b=b[pai];
// 时间特殊处理
if(pai==="time"){
a=a.replace(/-/g,'');
b=b.replace(/-/g,'');
}
return (a-b)*this.flag;
});
render();
};
});
};
return {
// 命令模式:模块入口“管控当前版块各方法执行的顺序”
init() {
queryData();
render();
handle();
}
};
})();
productModel.init();