使用的是雪碧图,用的软件是CSS Sprite Tools

第一次实现与分析:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>实现方式1</title>
</head>
<style>
body,ul,li{
padding: 0;
margin: 0;
}
li{
list-style-type: none;
/* 1 */
}
.rating{
width: 525px;
height: 26px;
margin: 100px auto;
}
.rating-item{
float: left;
height: 99px;
width: 102px;
background: url(imgs/img2.png) no-repeat;
}
</style>
<body>
<ul id="rating" class="rating">
<li class="rating-item" title="很不好"></li>
<li class="rating-item" title="不好"></li>
<li class="rating-item" title="一般"></li>
<li class="rating-item" title="好"></li>
<li class="rating-item" title="很好"></li>
</ul> <script src="http://libs.baidu.com/jquery/1.11.3/jquery.min.js"></script>
<!-- 使用cdn的好处:与其他的网站共享 -->
<script>
// console.log($);
var num = 2,
$rating = $("#rating"),
$item = $rating.find(".rating-item");//默认点亮
  //不足1:直接在全局作用域中命名污染了命名环境 var lightOn = function(num){
$item.each(function(index){//在each方法的函数中index是可以直接得到的
console.log(index);
if(index < num){
$(this).css('background-position','0 -94px');//通过使用背景位置来调整,注意'0 0'与'0,0'是完全不一样的,今天会再开一篇写
// console.log($(this).index(),num);
}else{
$(this).css('background-position','0 0');
}
});
};
//初始化
lightOn(num);
  //不足:在每个图片上都绑定了监听事件,占用了过多资源,事件委托今天也会专门整理一篇
// console.log(num);
//事件绑定
$item.on('mouseover',function(){
lightOn($(this).index() + 1);
}).on('click',function(){
num = $(this).index() + 1;
});
$rating.on('mouseout',function(){
lightOn(num);//记住了选中之后的效果
})
</script>
</body>
</html>

第二次重写:如果现在要多加一组星星评分在下面呢??(比如美团外卖)难道又要复制然后修改一下吗,我觉得不行

使用了jQuery自制插件以及IIFE的一些知识

<!--html部分只比上面多了一个id为rating2的父容器和五个星星-->
<script src="http://libs.baidu.com/jquery/1.11.3/jquery.min.js"></script>
<script>
// console.log($); var rating = (function(){//将这些变量和函数包裹在这个立即执行函数的块级作用域中,将外部会用到的返回出去
//为了避免多次调用init多次声明lightOn,将其移出放到这,但又会有新的问题
//$item访问不到了 要为其添加一个参数,使得在后面调用的时候$item是能被获取到的
var lightOn = function($item,num){
$item.each(function(index){
console.log(index);
if(index < num){
$(this).css('background-position','0 -94px');
// console.log($(this).index(),num);
}else{
$(this).css('background-position','0 0');
}
});
};
var init = function(el,num){//用函数字面量创建一个初始化函数init,包括了获取元素、点亮星星颗数的函数 //这里需要注意了,用函数字面量定义的函数是不存在“提升”(hoisting)的,只有用函数声明定义的才具有这个特性
var $rating = $(el),
$item = $rating.find(".rating-item"); //
//初始化,根据传入的元素和数值点亮默认的星星
lightOn($item,num);
// console.log(num);
//事件绑定
$rating.on('mouseover','.rating-item',function(){//在这里使用了事件代理写法,意指将.rating-item上的事件委托给$rating来处理
lightOn($item,$(this).index() + 1);
}).on('click','.rating-item',function(){
num = $(this).index() + 1;
}).on('mouseout',function(){
lightOn($item,num);
} // jQuery插件,方便后面有多个类似需求时直接调用(这个算造轮子吗)
$.fn.extend({
rating:function(num){//el就是下面的this
return this.each(function(){
init(this,num);//this直接指向调用这个自定义方法的元素
});
}
}); return{
init:init//将init方法返回出去
} })(); rating.init("#rating",2);
// rating.init("#rating2",3);
$("#rating2").rating(4);//调用自定义的插件

第三种:这种开始我就有点脑壳疼了,使用了设计模式中的模板方法模式,虽然老师讲的很好,但是我总是半懂不懂...

    var rating = (function(){
//点亮整颗的实现函数,后面还有实现半颗的
var lightEntireOne = function(el,options){
this.$el = $(el);
this.$item = this.$el.find('.rating-item');
this.opts = options;//注意这里options是对象
       //将传入的参数传递为调用该函数的元素(?)的属性   
};
    //在这个函数的原型上添加方法,使所有实例都能共享  
lightEntireOne.prototype.init = function(){
this.lightOn(this.opts.num);
if(!this.opts.readOnly){//见下面的default属性,在这一次的重写中传入给初始化对象的不再是元素和一个数值
       //而是元素和一个对象,我们每次有需求变更,就在这个对象中进行调整,调整后的对象为options
       //这里的意思即是只有在options对象不是只读的,即可以修改,才可以对其绑定点击事件
       //比如豆瓣评分,平均评分是不可点击的,但个人评分可以~这样就完成了代码块的复用(?)
this.bindEvent();}
};
lightEntireOne.prototype.lightOn = function(num){
num = parseInt(num);//转为整型数
this.$item.each(function(index){
if(index < num){
$(this).css('background-position','0 -94px');
}else{
$(this).css('background-position','0 0');
}
});
};
lightEntireOne.prototype.bindEvent = function(){
var current = this,//保存当前の图片
itemLength = current.$item.length;
        //current.$el:当前元素的父元素(就是外面那层),this.$el = $(el)
current.$el.on('mouseover','.rating-item',function(){
var currentNum = $(this).index() + 1;当前鼠标所在的序列值
console.log(currentNum);
current.lightOn(currentNum);
          //(a)&&b,如果a真,则执行b
          //这里是避免后续添加新需求时又添加了其他名为select的...“东西”
(typeof current.opts.select === 'function') && current.opts.select.call(this,currentNum
,itemLength);
          //注意这里的call方法,a.call(b,num1,...),把b用a的方法走一遍,但不做保存,这里的this即为五个子元素图片
          //而currentNum和itemLength即为后面传入select函数的参数
current.$el.trigger('select',[currentNum,itemLength]);//!!!!!!
  //在父元素上触发select事件,这个事件是自定义的
}).on('click','.rating-item',function(){
currentNum = $(this).index() + 1;//将要点亮的星星数值进行变化并保存在这个变量中
(typeof current.opts.chosen === 'function') && current.opts.chosen.call(this,currentNum
,itemLength);
current.$el.trigger('chosen',[currentNum,itemLength]);
}).on('mouseout',function(){、
          currentNum = $(this).index()+1;
current.lightOn(currentNum);
});
}; //默认参数对象
var defaults = {
num:0,
readOnly:false,
select:function(){},//默认情况下select和chosen不会触发任何事件
chosen:function(){}
}; //初始化
var init = function(el,options){
//当用户没有传递参数时,使用默认的
options = $.extend({},defaults,options);
//用options替代defaults,将生成的内容放到前面的空对象中,并将这个空对象返回赋值给options对象
new lightEntireOne(el,options).init();
}; return {
init:init
}
})();
//下面主要是为了在鼠标移入和点击时在控制台打印出x/5的字样,在有需要时也可以通过ajax发送出去
rating.init("#rating",{
num:2,
select:function(currentNum,total){
console.log(this);//lightEntireOne对象,要让其指向每颗星星,就要使用call
console.log(currentNum + "/" + total);
}
});     //上面与下面两种方法都是可以的~
rating.init("#rating2",{
num:1,
});
$("#rating").on('select',function(e,num,total){
console.log(num + "/" + total);
}).on('chosen',function(e,num,total){
console.log(num + "/" + total);
})
   //注意在下面的预览中,在点击后this指向的是对应的子元素,如果不使用call方法,this指向的就是select()这个函数啦~

打印的效果预览:

----2019-4-8 一更----

----4-10 二更----

第四次重写,添加了半颗星的功能

<script>
var rating = (function(){
//点亮整颗
var lightEntireOne = function(el,options){
this.$el = $(el);
this.$item = this.$el.find('.rating-item');
this.opts = options;
};
lightEntireOne.prototype.init = function(){
this.lightOn(this.opts.num);
if(!this.opts.readOnly){
this.bindEvent();}
};
lightEntireOne.prototype.lightOn = function(num){
num = parseInt(num);
this.$item.each(function(index){
if(index < num){
$(this).css('background-position','0 -94px');
}else{
$(this).css('background-position','0 0');
}
});
};
lightEntireOne.prototype.bindEvent = function(){
var current = this,
itemLength = current.$item.length; current.$el.on('mouseover','.rating-item',function(){
var currentNum = $(this).index() + 1;
current.lightOn(currentNum);
(typeof current.opts.select === 'function') && current.opts.select.call(this,currentNum
,itemLength);
current.$el.trigger('select',[current.opts.num,itemLength]);//!!!!!! }).on('click','.rating-item',function(){
current.opts.num = $(this).index() + 1;
(typeof current.opts.chosen === 'function') && current.opts.chosen.call(this,current.opts.num
,itemLength);
current.$el.trigger('chosen',[current.opts.num,itemLength]);
}).on('mouseout',function(){
currentNum = $(this).index() + 1;
current.lightOn(current.opts.num); });
}; //
var lightHalfOne = function(el,options){
this.$el = $(el);
this.$item = this.$el.find('.rating-item');
this.opts = options;
this.add = 0;
};
lightHalfOne.prototype.init = function(){
this.lightOn(this.opts.num);
if(!this.opts.readOnly){
this.bindEvent();}
};
lightHalfOne.prototype.lightOn = function(num){
var count = parseInt(num),//向下舍入!只要整数部分
isHalf = count !== num;
console.log(count);
this.$item.each(function(index){
if(index < count){
$(this).css('background-position','0 -94px');
}else{
$(this).css('background-position','0 0');
}
}); if(isHalf){
this.$item.eq(count).css('background-position','0 -186px');
}
};
lightHalfOne.prototype.bindEvent = function(){
var current = this,
itemLength = current.$item.length;
console.log(typeof(this),this);//lightHalfOne对象 current.$el.on('mousemove','.rating-item',function(e){
var $this = $(this),
currentNum = 0;
console.log($this);
if(e.pageX - $this.offset().left < $this.width()/2){
//此时的this指向current.$el
current.add = 0.5;
// console.log("half");
}else{
current.add = 1;
}
currentNum = $this.index() + current.add;
current.lightOn(currentNum);
// console.log(currentNum);
(typeof current.opts.select === 'function') && current.opts.select.call(this,current.opts.num
,itemLength);
console.log(currentNum);
console.log(current.opts.num);
current.$el.trigger('select',[current.opts.num,itemLength]);//!!!!!! }).on('click','.rating-item',function(){
current.opts.num = $(this).index() + current.add;;
(typeof current.opts.chosen === 'function') && current.opts.chosen.call(this,current.opts.num
,itemLength);
current.$el.trigger('chosen',[current.opts.num,itemLength]);
}).on('mouseout',function(){
current.lightOn(current.opts.num); });
}; //默认参数
var defaults = {
mode:"lightEntire",
num:0,
readOnly:false,
select:function(){},
chosen:function(){}
};
//此mode非彼mode
var mode = {
'entire':lightEntireOne,
'half':lightHalfOne
} //初始化
var init = function(el,options){
//当用户没有传递参数时,使用默认的
options = $.extend({},defaults,options);
if(!mode[options.mode]){
options.mode = 'entire';
}
//用options替代defaults,将生成的内容放到前面的空对象中,并将这个空对象返回
// new lightRntire(el,options).init();
// new lightHalfOne(el,options).init();
new mode[options.mode](el,options).init();
}; return {
init:init
}
})(); rating.init("#rating",{
mode:'entire',
num:2.5,
select:function(currentNum,total){
// console.log(this);//lightEntireOne对象,要让其指向每颗星星,就要使用call
console.log(currentNum + "/" + total);
},
chosen:function(currentNum,total){
// console.log(this);//lightEntireOne对象,要让其指向每颗星星,就要使用call
console.log(currentNum + "/" + total);
}
}); rating.init("#rating2",{
mode:'half',
num:3.5,
});
$("#rating2").on('select',function(e,num,total){
console.log(num + "/" + total);
}).on('chosen',function(e,num,total){
console.log(num + "/" + total);
}) </script>

功能粗略分析

1.当初始化没有传入mode或是不存在的mode属性时,mode值默认为entire,即不支持半星评分。

2.以mode值为half为例,

由于ishalf判断为true,所以会将第3颗星星(传入为2.5)变化到半颗的位置

3.

.

这里的($)this是当前停留的那颗星星

4.然后就是这个写法,相当于new entire(el,options).init();,中括号的写法会读取其值

明天来更提取抽象父类的!

星级评分原理 N次重写的分析的更多相关文章

  1. SpringMVC关于json、xml自动转换的原理研究[附带源码分析 --转

    SpringMVC关于json.xml自动转换的原理研究[附带源码分析] 原文地址:http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-c ...

  2. ThreadLocal的原理,源码深度分析及使用

    文章简介 ThreadLocal应该都比较熟悉,这篇文章会基于ThreadLocal的应用以及实现原理做一个全面的分析 内容导航 什么是ThreadLocal ThreadLocal的使用 分析Thr ...

  3. 用css制作星级评分

    Step 1: XHTML <ul class="star-rating">       <li><a href="#" titl ...

  4. 原生JS实现-星级评分系统

    今天我又写了个很酷的实例:星级评分系统(可自定义星星个数.显示信息) sufuStar.star();使用默认值5个星星,默认信息 var msg = [........]; sufuStar.sta ...

  5. javascript星级评分(多个)

    JS打多个类型星级评分: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http: ...

  6. 干货之运用CALayer创建星级评分组件(五角星)

    本篇记录星级评分组件的创建过程以及CALayer的运用. 为了实现一个星级评分的组件,使用了CALayer,涉及到mask.CGPathRef.UIBezierPath.动画和一个计算多角星关键节点的 ...

  7. js星级评分点击星级评论打分效果

    html代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www ...

  8. MapReduce原理及其主要实现平台分析

    原文:http://www.infotech.ac.cn/article/2012/1003-3513-28-2-60.html MapReduce原理及其主要实现平台分析 亢丽芸, 王效岳, 白如江 ...

  9. js星级评分点击星级评论打分效果--收藏--转载

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

随机推荐

  1. UDP协议学习(转)

    reference: https://blog.csdn.net/s_lisheng/article/details/73538229                  https://blog.cs ...

  2. springcloud学习计划

    后续参考学习spring cloud https://blog.csdn.net/forezp/article/details/70148833 https://github.com/forezp/S ...

  3. keil的可烧写hex文件生成

    右键Target1 Options Target for ‘Target1’ ...->Output->Create Executable:->Create HEX File Bui ...

  4. FCC JS基础算法题(11):Seek and Destroy (摧毁数组)

    题目描述: 实现一个摧毁(destroyer)函数,第一个参数是待摧毁的数组,其余的参数是待摧毁的值. 我们可以使用arguments来进行参数的遍历. function destroyer(arr) ...

  5. 剑指Offer 21. 栈的压入、弹出序列 (栈)

    题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压 ...

  6. CDMA码片序列问题

    要想知道到底是怎么算的 建议看见这篇博客的任何一位去先看一下这篇博客:https://blog.csdn.net/dog250/article/details/6420427 在CDMA中.每一个比特 ...

  7. N!分解质因子p的个数_快速求组合数C(n,m)

    int f(int n,int p) { ) ; return f(n/p,p) + n/p; } https://www.xuebuyuan.com/2867209.html 求组合数C(n,m)( ...

  8. ID3-C45-CART

    区别:使用不同的属性选择度量. 信息增益偏向多值属性 信息增益率倾向产生不平衡的划分 基尼指数偏向多值属性,并且当类的数量很大时会有困难,还倾向于导致相等大小的分区和纯度 C4.5: 优点:产生的分类 ...

  9. bootstrap table表格前台分页,点击tab选项,重新刷新表格

    近期做项目的时候使用bootstrap表格前台分页,并且有一个tab切换选项,共用一个table,效果如下图,上方是tab选项,下方是table: 在实际实现的时候,在默认状态下,表格翻到了第5页,此 ...

  10. 关于IE无法访问本机网络的问题

    多次遇到IE无法访问本机站点的情况,比如架设了一个花生壳,所有人都可以访问,唯独本机不行(服务器),还需要把这个域名加入信任站点,这TMD什么情况.今天又遇到访问本地restful service,用 ...