原生JS结合cookie实现商品评分组件
组件样式效果:
再次访问记录用户上次操作:
开发思路如下:
1.利用JS直接操作DOM的方式开发商品评分组件,主要实现功能有:显示评价评分的样式以及将用户操作后对应的数据返回到主页面
2.主页面引入商品评分组件的js文件并根据规定格式的数据,生成对应的组件元素并插入页面
3.主页面利用侦听事件接收组件抛发的数据并保存,每次有组件抛发事件都会覆盖上次抛发的数据,直到页面关闭,将最后一次抛发的数据保存到cookie中。
4.当再次打开主页面时,先判断cookie中是否有对应的数据,如果有提取cookie其中的数据,先根据页面数据生成对应的组件元素,再将cookie中数据传入对应组件元素中重新渲染,最后将元素插入主页面
注意点:
因为当再次打开页面,用户可能不一定会修改所有的组件元素,这样组件抛出的数据就不完整,不会是所有元素的数据,当我们在保存数据的时候需要和上一次保存的数据进行比对,只覆盖和上一次保存数据不同的属性,保证数据的完整性。
主页面JS部分代码如下:
import Star from "./js/Star.js";
var data=["商品符合度","店家服务态度","快递配送速度","快递员服务","快递包装"];
var cookieStorage={};
// 初始化加载先拿到cookie,切割好数据之后,在初始化创建渲染完评分数据对象之后,再通过setData的方法修改数据
var hository=JSON.parse(document.cookie.split("=")[1]);
data.forEach((item)=>{
var star=new Star(item);
star.addEventListener("change",changeHandler);
if(JSON.stringify(hository)!=="{}"){//判断cookie中是否有对应的数据
star.setData(hository[item])
}
star.appendTo(".ShowStarList");
})
cookieStorage=hository;
function changeHandler(e){
for(var prop in e.StarList){//因为当再次打开页面,用户可能不一定会修改所有的组件元素,这样抛出的数据就不完整不会是所有元素的数据
cookieStorage[prop]=e.StarList[prop];
}
}
//页面点击操作之后,将数据存放到cookieStorage中,当下一次再点击时将cookieStorage中的数据覆盖
// 直到页面被关闭时,触发下述事件,将cookieStorage+时间段的方式存储到cookie中
window.onbeforeunload=closeHandler;
function closeHandler(){
var date=new Date();
date.setFullYear(2021);
document.cookie="cookieStorage="+JSON.stringify(cookieStorage)+";expires="+date.toUTCString();
}
商品评分组件JS部分代码如下:
import Base from "./Base.js";
export default class Star extends Base{
static StarList = {};//用来存储数据选中的评分数据到时候返回页面
label;
starCon = [];
pos=-1
labelWrap;
starWrap;
face;
scoreWrap;
constructor(_label) {
// 第一次创建是用label生成的对象,后面都是用StarList去渲染对象
super();
this.label = _label;
this.elem.setAttribute("class", "StarCon");
this.createStarCon();
this.setStyle();
}
//创建评分组件的HTML结构
createStarCon() {
this.labelWrap = document.createElement("span");
this.labelWrap.textContent = this.label;
this.starWrap = document.createElement("div");
for (let i = 0; i < 5; i++){
let singleStar = document.createElement("div");
singleStar.setAttribute("class", "singleStar");
this.starWrap.appendChild(singleStar);
this.starCon.push(singleStar);
}
this.face = document.createElement("div");
this.starWrap.appendChild(this.face);
this.scoreWrap = document.createElement("span");
this.scoreWrap.textContent = "0分";
this.labelWrap.setAttribute("class", "label");
this.starWrap.setAttribute("class", "star");
this.face.setAttribute("class", "face");
this.scoreWrap.setAttribute("class", "score");
this.elem.appendChild(this.labelWrap);
this.elem.appendChild(this.starWrap);
this.elem.appendChild(this.scoreWrap);
this.starWrap.addEventListener("mouseover",e=>this.mouseHandler(e));
this.starWrap.addEventListener("click",e=>this.mouseHandler(e));
this.starWrap.addEventListener("mouseleave",e=>this.mouseHandler(e));
}
mouseHandler(e) {
// click和mouseover都要不停渲染星星样式,所以都要通过遍历识别鼠标当前指向的是哪个e.target,从而通过for循环渲染星星
// 其中点击事件和鼠标移动事件不同的地方在于
// 点击事件会记录下一个全局变量pos表示选中该数据,且会抛出事件传递包括同页面其他实例对象的数据给页面
// 鼠标移动事件则是利用相同原理除了渲染星星外,还要渲染分数和笑脸
switch (e.type) {
case "mouseover":
let index = this.starCon.indexOf(e.target);
if (index < 0) return;
this.changeScore(index + 1);
this.changeFace(index);
this.changeStar(index);
break;
case "click":
let index1 = this.starCon.indexOf(e.target);
if (index1 < 0) return;
this.pos = index1;
this.dispatch();
this.changeStar(index1);
break;
case "mouseleave":
this.changeStar(this.pos);
this.changeScore(this.pos + 1);
this.changeFace(-1);
break;
}
}
//根据分数改变星星样式
changeStar(n){
for(var i=0;i<this.starCon.length;i++){
if(i<=n || i<=this.pos){
this.starCon[i].style.backgroundPositionY="-16px";
}else{
this.starCon[i].style.backgroundPositionY="0px";
}
}
}
//根据分数改变分数样式
changeScore(n){
this.scoreWrap.textContent=n+"分";
if(n===0){
this.scoreWrap.style.color="#999";
}else{
this.scoreWrap.style.color="#e4393c";
}
}
//根据分数改变笑脸样式
changeFace(n){
if(n<0){
this.face.style.display="none";
return
}
var index=5-n-1
// 鼠标移动时,笑脸的div位置和其背景图的位置都要同时移动
// 鼠标离开或者点击之后,笑脸消失
this.face.style.display="block";
this.face.style.backgroundPositionX=-index*20+"px";
this.face.style.left=this.starCon[n].offsetLeft+"px";
}
//每次点击后抛发改变后的实例化组件元素群组的数据
dispatch() {
Star.StarList[this.label]=this.pos+1;
var evt=new Event("change");
evt.score=this.pos+1;
evt.label=this.label;
evt.StarList=Star.StarList;
this.dispatchEvent(evt);
}
//根据cookie传入的数据重新渲染组件元素
setData(historyScore) {
if (historyScore < 1) return;
this.pos = historyScore-1;
this.changeStar(historyScore-1);
this.changeScore(historyScore);
}
//设置实例化组件的样式
setStyle() {
Utils.addCSS(".label",{
float: "left",
height: "16px",
font: '12px / 150% tahoma, arial, "Microsoft YaHei", "Hiragino Sans GB", 宋体, sans-serif',
marginRight: "10px",
overflow: "hidden",
whiteSpace: "nowrap",
textOverflow: "ellipsis",
color: "rgb(102, 102, 102)",
});
Utils.addCSS(".star",{
float: "left",
height: "16px",
position: "relative",
marginTop: "1px",
});
Utils.addCSS(".singleStar",{
float: "left",
height: "16px",
width: "16px",
backgroundImage: "url(../img/commstar.png)",
backgroundPositionY: "0px",
});
Utils.addCSS(".face",{
height: "16px",
width: "16px",
backgroundImage: "url(../img/face-red.png)",
position:"absolute",
top:"-16px",
display: "none",
backgroundPositionX:"0px",
left:"0px",
});
Utils.addCSS(".score",{
position:"relative",
width:"30px",
height:"16px",
top:"-2px",
marginLeft: "10px",
font: '12px / 150% tahoma, arial, "Microsoft YaHei", "Hiragino Sans GB", 宋体, sans-serif',
textAlign:"right",
color:"#999",
});
}
}