jQuery插件-jRating评分插件源码分析及使用方法


Posted in Javascript onDecember 28, 2012

该插件被广泛应用于各种需要评分的页面当中,今天作为学习,把源码拿出来分析一下,顺便学习其使用方法。
一、插件使用一览

<div> 
<div>第一个例子</div> 
<div id="16_1" class="myRating"></div> 
</div>

<link href="Script/jRating/jRating.jquery.css" rel="stylesheet" type="text/css" /> 
<script src="Script/jquery-1.7.min.js" type="text/javascript"></script> 
<script src="Script/jRating/jRating.jquery.js" type="text/javascript"></script> 
<script type="text/javascript"> 
$(function () { 
$(".myRating").jRating({ 
length:10 
}); 
}); 
</script>

执行效果
jQuery插件-jRating评分插件源码分析及使用方法
可以看到,上面的例子中,有10颗星,是参数length的作用。其中,默认总分是20分,就是10颗星都选择。这里我们着重注意<div>的id16_1,其中16被用来初始化评分插件默认选择的比例,16/20 * 10。所以我们上面有8颗星是黄色的。

当我们把鼠标放到插件上时,小星星会随着鼠标移动而增加或减少(红色会覆盖黄色或白色),表示评分的从0至20,但点击鼠标时,评分结束,插件不能再编辑了,同时,通过Ajax向指定的路径POST数据,用后台数据将评分数据持久化。

在分析源代码之前,我们先看一下使用该插件时有哪些可选参数:
jQuery插件-jRating评分插件源码分析及使用方法 
二、插件源码分析

按照jQuery插件开发的推荐方法,为了避免快捷符号“$”与其他JavaScript插件产生冲突,源码开头采用了下面技术:

(function($) { 
$.fn.jRating = function(op) { 
//这里为插件代码 
} 
})(jQurery)

接下来,我们分析的所有代码都将出现在上面绿色区域部分,首先设置默认参数。
var defaults = { 
/** String vars **/ 
bigStarsPath : 'icons/stars.png', // 设置大星星(默认显示)的相对路径 
smallStarsPath : 'icons/small.png', // 小星星 
phpPath : 'php/jRating.php', // 点击鼠标,评分确定后,将POST数据的地址,接下来我们会采用ASP.Net技术进行处理 
type : 'big', // 可以看出,默认是使用大星星 
/** Boolean vars **/ 
step:false, // 如果设置为True,则星星要么全变色,要么不全变,当然这也适和选择分数是同步的。 
isDisabled:false, //如果设置为True,则插件不能编辑,当点击鼠标过后,默认是True的状态 
showRateInfo: true, //当鼠标放到星星上时,是否在鼠标下方显示选择比例信息,例如16/20 
/** Integer vars **/ 
length:5, // 星星的个数 
decimalLength : 0, // 选择的数字其后的小数位,最多为3位,如果设置为1,可能出现的情况为16.3/20 
rateMax : 20, // 比例中的分母,整数0-9999 
rateInfosX : -45, // 信息提示框相对于鼠标位置的横坐标位置 
rateInfosY : 5, // 同上,纵坐标位置 
/** Functions **/ 
onSuccess : null, //成功后的回调函数 
onError : null //出错处理函数 
};

通过上面绿色部分的解释,我们可以看到所有参数的默认值,同时,我们可以在插件使用中,根据需求确定适合的配置,插件的使用不就是这些参数的搭配组合吗?
接下来我们再看一个函数作用域:
if(this.length>0) 
return this.each(function() { //接下来出现的代码,都将在此处!!!}

这段代码很简单,我们要在选中的集合上执行jRating()函数,而上面的代码首先判断该集合是否长度大于0,如果为1或者更多,则在该集合上执行each()函数,对集合中的每一个元素(div)进行单独处理。
该插件的核心代码其实都在上面的each()函数中,我们首先看几个函数,这几个函数都定义在each()函数中,并被其他语句调用。
function findRealLeft(obj) { 
if( !obj ) return 0; 
return obj.offsetLeft + findRealLeft( obj.offsetParent ); 
};

首先关注findRealLeft()函数,该函数接收名为obj的对象参数,最后返回该元素对象相对于浏览器左边界的距离。注:offsetParent是指元素最近的定位(relative,absolute)祖先元素,如果没有祖先元素是定位的话,会指向body元素。offsetLeft返回相对于offsetParent的位置。
function getNote(relativeX) { 
var noteBrut = parseFloat((relativeX*100/widthRatingContainer)*opts.rateMax/100); //两个100是否可以去掉,表示选择的比例,如16 或 16.1 
switch(opts.decimalLength) { //根据参数确定要输去比例需要的小数位,例如16.1 16.12 16.123 
case 1 : 
var note = Math.round(noteBrut*10)/10; 
break; 
case 2 : 
var note = Math.round(noteBrut*100)/100; 
break; 
case 3 : 
var note = Math.round(noteBrut*1000)/1000; 
break; 
default : 
var note = Math.round(noteBrut*1)/1; 
} 
return note; 
};

接着关注getNote函数,首先我们看以下relativeX是一个什么东西:
var realOffsetLeft = findRealLeft(this); 
var relativeX = e.pageX - realOffsetLeft;

上面两行代码是调用getNote函数前,定义relativeX变量用的,我们可以分析出relativeX的作用。这里的this是我们应用jRating()函数的某个div,首先获得其相对于浏览器的左边距,因为上面两行代码是出现在鼠标移动处理函数mouseenter中(稍后我们会看到),因此这里的e.pageX表示鼠标相对于浏览器的横向距离。因此,这里的relativeX表示的是鼠标相对于<div>左边界的横向距离。
我们再次关注getNote函数,由widthRatingContainer = starWidth*opts.length可以看出,widthRatingContainer是左右星星图片加起来的宽度。因此,var noteBrut = parseFloat((relativeX*100/widthRatingContainer)*opts.rateMax/100);可以把分母与分子上的两个100去掉,即(relativeX/widthRatingContainer)*opts.rateMax),noteBrut变量最后存储的是鼠标选择的比例,如果rateMax设为20,则noteBrut的范围可以通过鼠标来确定(0—20)。
switch函数,是通过decimalLength参数(用来设定显示比例的小数位),最终确定(比例)显示的位数。读到这里,我们可以发现,getNote函数就是通过relativX来返回鼠标选择的比例,这个比例是什么,见下图用笔刷框起来的部分:
jQuery插件-jRating评分插件源码分析及使用方法 
接下来,我们再关注一个函数
function getStarWidth(){ 
switch(opts.type) { 
case 'small' : 
starWidth = 12; // small.png小星星图片的宽度 
starHeight = 10; // 高度 
bgPath = opts.smallStarsPath; //图片相对地址 
break; 
default : 
starWidth = 23; // 大星星的宽度,可以看到,这是默认值 
starHeight = 20; // 高度 
bgPath = opts.bigStarsPath; //星图片相对地址 
} 
};

这个是一个比较简单的用于初始化变量的函数,根据type属性,初始化三个变量,分别是starWidth、starHeight、bgPath,绿色的注释信息已能够说明一切,不再赘述!
each()中定义的函数看完了,接下来,我们还在each()函数中进行游荡,按照从上到下的顺序,先截取了几行代码如下:
var opts = $.extend(defaults, op), //利用extend()函数将默认参数与输入参数进行合并,最后存储在opts变量中。 
newWidth = 0, //定义变量,该变量用于存储relativeX,但会根据step属性进行相应调整 
starWidth = 0, //定义变量,星星的宽度 
starHeight = 0, //高度 
bgPath = ''; //星星图片地址 
if($(this).hasClass('jDisabled') || opts.isDisabled) //确定jDisabled变量,表示是否能对div进行操作 
var jDisabled = true; 
else 
var jDisabled = false; 
getStarWidth(); //这个函数不赘述,上面分析过 
$(this).height(starHeight); //根据星星的高度,确定此div的高度。

接着往下看
var average = parseFloat($(this).attr('id').split('_')[0]), //通过<div>的id(例如16_2),获取下划线前面的数字,把该数字作为默认的选择比例 
idBox = parseInt($(this).attr('id').split('_')[1]), // 下划线后面的部分,作为辨别评分插件的id 
widthRatingContainer = starWidth*opts.length, // 星星图片宽度总和,并作为外围容器的宽度 
widthColor = average/opts.rateMax*widthRatingContainer, // 颜色块占用的宽度

接下来,我们将看到新建的三个<div>,并插入到主div中
quotient = 
$('<div>', 
{ 
'class' : 'jRatingColor', 
css:{ 
width:widthColor 
} 
}).appendTo($(this)), 
average = 
$('<div>', 
{ 
'class' : 'jRatingAverage', 
css:{ 
width:0, 
top:- starHeight 
} 
}).appendTo($(this)), 
jstar = 
$('<div>', 
{ 
'class' : 'jStar', 
css:{ 
width:widthRatingContainer, 
height:starHeight, 
top:- (starHeight*2), 
background: 'url('+bgPath+') repeat-x' 
} 
}).appendTo($(this));

首先我们分析第一个<div>,它的类名为jRatingColor,它表示默认比例,用黄色表示,它的长度为withColor,这里主要看一下它的样式表:
.jRatingColor { 
background-color:#f4c239; /* bgcolor of the stars*/ 
position:relative; //相对定位 
top:0; 
left:0; 
z-index:2; //这里需注意,该div的祖先即我们each函数中的this 的z-index是1,下面我们将马上看到。 
height:100%; 
}

第二个<div>样式表如下:
.jRatingAverage { 
background-color:#f62929; //红色 
position:relative; 
top:0; 
left:0; 
z-index:2; 
height:100%; 
}

但在上面的程序中,初始化时,把宽度设为0(因为鼠标还没选嘛),同时改变了top值:- 星高度,这样它就和上面添加的div在纵方向上重合了。
接下来看第三个<div>,主要用来放小星星。
/** Div containing the stars **/ 
.jStar { 
position:relative; 
left:0; 
z-index:3; 
}

这个样式表比较简单,我们着重看一下JS中动态添加的几个属性值:
width:widthRatingContainer, //设置宽度 
height:starHeight, //高度 
top:- (starHeight*2), //改变纵方向的值,和上面两个<div>重合 
background: 'url('+bgPath+') repeat-x' //设置背景为小星星

属性的值设置了,但也许有人会问,问什么只看到小星星颜色是彩色的,而上面添加的前两个<div>不是具有高度的长方形颜色条吗?下面我们看一下小星星的图片就明白为什么了!
jQuery插件-jRating评分插件源码分析及使用方法
不用多说,旁边用不透明的背景,中间小星星是透明的,下面的颜色自然就显示出来了!!
接下来的语句很简单,就是设置一下最外层div容器的样式,注意z-Index属性:
$(this).css({width: widthRatingContainer,overflow:'hidden',zIndex:1,position:'relative'});

接下来会进入相对复杂的部分,我们将关注鼠标动作及其响应效果,首先关注一个小逻辑:
if(!jDisabled)
//接下来的代码
可以看出,前面我们设置的jDisable变量在这里用上了,如果jDisabled为true,就表示插件禁用了,那么接下来的鼠标操作将不会被执行。
接下来看鼠标操作是如何添加到插件中的:
$(this).unbind().bind({//鼠标事件处理代码,下面将分别进行讨论。
});
首先看以一下鼠标进入事件处理代码
mouseenter : function(e){ 
var realOffsetLeft = findRealLeft(this); 
var relativeX = e.pageX - realOffsetLeft; //首先计算出relativeX,它表示的是鼠标相对于外层<div>左边界的横向距离 
if (opts.showRateInfo) 
var tooltip = 
$('<p>',{ 
'class' : 'jRatingInfos', 
html : getNote(relativeX)+' <span class="maxRate">/ '+opts.rateMax+'</span>', //注意这里用了getNote方法,前面已讲了它的用途。 
css : { 
top: (e.pageY + opts.rateInfosY), 
left: (e.pageX + opts.rateInfosX) 
} 
}).appendTo('body').show(); 
},

relativeX变量不多解释,这里的注释和前面都有提到,接下来,判断showRateInfo参数是否为true,如果为true,表示要显示比例信息(例如鼠标下面显示16/20),tooltip变量就是这个信息框,最后通过appendTo方法添加到body中。代码逻辑很简单,这个函数主要用来显示提示框<p>,我们在这里可以重点关注一下<p>节点的样式,它是绝对定位的,并利用代码改变了top和Left值,看一下相关的样式表:
p.jRatingInfos { 
position: absolute; 
z-index:9999; 
background: transparent url('http://www.cnblogs.com/icons/bg_jRatingInfos.png') no-repeat; 
color: #FFF; 
display: none; 
width: 91px; 
height: 29px; 
font-size:16px; 
text-align:center; 
padding-top:5px; 
} 
p.jRatingInfos span.maxRate { 
color:#c9c9c9; 
font-size:14px; 
}

接下来我们看一下鼠标进来后的mousemove事件的处理函数:
mousemove : function(e){ 
var realOffsetLeft = findRealLeft(this); 
var relativeX = e.pageX - realOffsetLeft; 
if(opts.step) newWidth = Math.floor(relativeX/starWidth)*starWidth + starWidth; 
else newWidth = relativeX; 
average.width(newWidth); 
if (opts.showRateInfo) 
$("p.jRatingInfos") 
.css({ 
left: (e.pageX + opts.rateInfosX) 
}) 
.html(getNote(newWidth) +' <span class="maxRate">/ '+opts.rateMax+'</span>'); 
},

这个函数主要用来确定鼠标选择的比例,当然这个比例是通过getNote(newWidth)来得到的,那么,确定合适的newWidth值就成了这个函数的核心,如果opts.step为true,即比例只能是整数个星星(不能为15.3等等),那么我们看一下这个逻辑:Math.floor(relativeX/starWidth),starWidth是星星图片的宽度,Math.floor(-0.1)=-1,Math.floor(0.1) = 0,Math.floor(2.6)=2,知道这些,上面加红的代码就很容易理解了。
OK,Let's go on,看一下三个简单的处理函数
mouseover : function(e){ 
$(this).css('cursor','pointer'); 
}, 
mouseout : function(){ 
$(this).css('cursor','default'); 
average.width(0); 
}, 
mouseleave: function () { 
$("p.jRatingInfos").remove(); 
},

mouseover函数确保鼠标进入插件后的显示样式,mouseout也是同样,但它将类名为average的div(红色的)宽度变成0,mouseleave函数让提示信息框消失。
最后一个函数,也是整个源码的结尾,当然也是最重要最复杂的——鼠标点击函数:
click : function(e){ 
//接下来的代码都在此处。 
}

我们分部来,先看第一部分:
$(this).unbind().css('cursor','default').addClass('jDisabled');

为什么这里只列出一条语句,因为它很重要,但也很简单,我们这里一定要关注unbind()函数,它非常非常重要,当点击鼠标后,首先把其他所有绑定到外围<div>的事件都去掉了,这样就鼠标点击的瞬间,该插件的外观就固定显示在浏览器中,不再随着鼠标事件而出现变化。当然,最后给<div>添加jDisabled属性。
我们接着往后走:
if (opts.showRateInfo) $("p.jRatingInfos").fadeOut('fast',function(){$(this).remove();}); 
e.preventDefault(); 
var rate = getNote(newWidth); //关注rate变量,后面要用到。 
average.width(newWidth);

第一句不难理解,删除提示信息框,第二句取消鼠标点击的默认操作,后面两句很简单,不再赘述,要知道newWidth在前面已提到,表示鼠标选择的宽度。
最后一条语句,把选择的比例发送到服务器端进行持久化操作:
$.post( 
opts.phpPath, //利用Ajax技术,向服务端发送数据的地址 
{ //Post过去的数据 
idBox : idBox, 
rate : rate, 
action : 'rating' 
}, 
function(data) { //回调函数,主要向插件自定义函数传递参数并执行。 
if(!data.error) 
{ 
if(opts.onSuccess) opts.onSuccess( element, rate ); 
} 
else 
{ 
if(opts.onError) opts.onError( element, rate ); 
} 
}, 
'json' //确定如何理解返回的数据,它采用json. 
);

利用jQuery做Ajax确实很简单,代码中做了必要注释,这里不再赘述,这个插件的源码就分析完了,比较粗,但整个逻辑也许体现了一些,希望该学习笔记对大家能有帮助。下面我们进入实战阶段。
三、实战jRating插件
为了更加逼近真实应用,我们先利用sql server建立一张数据库表,它是一个文章类型表,有id、标题、文章内容、评分四个字段,截图如下:
jQuery插件-jRating评分插件源码分析及使用方法 
评分字段默认为-1,表示该文章还没有被评分。当然,现在有人会说,这个表设计的很不合理,因为一篇文章不会只评分一次吧,应该每个用户都能进行评论,是的,我们在这里只是为了演示jRating插件利用Ajax进行持久化操作,因为是演示,所以一切从俭。
新建一个Web页面,用来显示第一篇文章(id为1)的标题、内容及评分插件,见前台代码:
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head runat="server"> 
<title></title> 
<link href="Script/jRating/jRating.jquery.css" rel="stylesheet" type="text/css" /> 
<script src="Script/jquery-1.7.min.js" type="text/javascript"></script> 
<script src="Script/jRating/jRating.jquery.js" type="text/javascript"></script> 
<script type="text/javascript"> 
$(function () { 
$(".theRating").jRating({ 
length: 20, 
phpPath: 'tempAjax.aspx/UpdateComment' //地址变成了一个aspx类型的WEB页面下的一个静态函数,稍后我们会看到! 
}); 
}); 
</script> 
</head> 
<body> 
<form id="form1" runat="server"> 
<div> 
<asp:Label ID="Label1" runat="server" Text="标题:"></asp:Label>   
<asp:Label ID="page1_title" runat="server" Text=""></asp:Label><br /> 
<asp:Label ID="page1_body" runat="server" Text=""></asp:Label><br /> 
<div id="16_1" class="theRating"></div> 
</div> 
</form> 
</body> 
</html>

后台CS代码如下:
protected void Page_Load(object sender, EventArgs e) 
{ 
if (!Page.IsPostBack) 
{ 
tempEntities cbx = new tempEntities(); //用了实体框架获取数据表 
var page1 = cbx.jRatingArticles.Where(m => m.id == 1).SingleOrDefault(); 
page1_title.Text = page1.title; 
page1_body.Text = page1.body; 
} 
}

为了减少数据库连接代码,我用了实体框架,只映射了一张表(jRatingArticle),就是上面我们看到的。获取id为1的文章对象,并把相应属性赋值到Label控件的Text属性中。
页面效果如下
jQuery插件-jRating评分插件源码分析及使用方法 
我们可以看到上面前台页面的JS代码中,有这样一条语句:
phpPath: 'tempAjax.aspx/UpdateComment'
它指明了,当鼠标点击插件后,要通过Ajax发送数据的地址,这里我们用.net页面技术来处理这个异步请求。tempAjax.aspx的后台cs代码如下:
[WebMethod()] 
public static void UpdateComment(int idBox, int rate) 
{ 
tempEntities cbx = new tempEntities(); 
var page1 = cbx.jRatingArticles.Where(m => m.id == 1).SingleOrDefault(); 
page1.is_comment = rate; 
cbx.SaveChanges(); 
}

此时,我们还需修改jRating插件的原文件,把鼠标单击(click)处理函数中的$.post函数替换如下:
$.ajax({ 
type: "POST", 
url: opts.phpPath, 
data: '{"idBox":"' + idBox + '","rate":"' + rate + '"}', 
contentType: "application/json; charset=utf-8", 
dataType: "json" 
});

为什么要改变源文件,因为我想改变Ajax请求的contentType属性,利用json格式发送请求数据,默认是application/x-www-form-urlencoded
OK,万事俱备,看一下执行效果(选择比例为16,16颗红星嘛):
jQuery插件-jRating评分插件源码分析及使用方法 
看看数据库的变化
jQuery插件-jRating评分插件源码分析及使用方法 
试验成功!今天学习就到这里,希望此篇学习笔记对大家能有所帮助!
Javascript 相关文章推荐
关于UTF-8的客户端用AJAX方式获取GB2312的服务器端乱码问题的解决办法
Nov 30 Javascript
利用JS判断用户是否上网(连接网络)
Dec 23 Javascript
JavaScript 获取任一float型小数点后两位的小数
Jun 30 Javascript
JavaScript 闭包详细介绍
Sep 28 Javascript
JavaScript实现鼠标点击导航栏变色特效
Feb 08 Javascript
详解vue2.0脚手架的webpack 配置文件分析
May 27 Javascript
Angular2 父子组件通信方式的示例
Jan 29 Javascript
js中自定义react数据验证组件实例详解
Oct 19 Javascript
详解小程序不同页面之间通讯的解决方案
Nov 23 Javascript
js获取form表单中name属性的值
Feb 27 Javascript
简单两步使用node发送qq邮件的方法
Mar 01 Javascript
layui table 表格模板按钮的实例代码
Sep 21 Javascript
JS声明变量背后的编译原理剖析
Dec 28 #Javascript
动态的改变IFrame的高度实现IFrame自动伸展适应高度
Dec 28 #Javascript
2012年开发人员的16款新鲜的jquery插件体验分享
Dec 28 #Javascript
web性能优化之javascript性能调优
Dec 28 #Javascript
javascript的字符串按引用复制和传递,按值来比较介绍与应用
Dec 28 #Javascript
javascript 利用Image对象实现的埋点(某处的点击数)统计
Dec 28 #Javascript
Javascript 加载和执行-性能提高篇
Dec 28 #Javascript
You might like
理解和运用PHP中的多态性[译]
2011/08/02 PHP
php获取本周开始日期和结束日期的方法
2015/03/09 PHP
php根据一个给定范围和步进生成数组的方法
2015/06/19 PHP
Laravel中Trait的用法实例详解
2016/03/16 PHP
Zend Framework实现多文件上传功能实例
2016/03/21 PHP
php中mkdir()函数的权限问题分析
2016/09/24 PHP
PHP类相关知识点实例总结
2016/09/28 PHP
再论Javascript下字符串连接的性能
2011/03/05 Javascript
JavaScript中获取未知对象属性的代码
2011/04/27 Javascript
基于JQuery的模拟苹果桌面Dock效果(稳定版)
2012/10/15 Javascript
Javascript+CSS3实现进度条效果
2016/10/28 Javascript
vue自定义指令实现v-tap插件
2016/11/03 Javascript
解析AngularJS中get请求URL出现的跨域问题
2016/12/01 Javascript
Vue.directive自定义指令的使用详解
2017/03/10 Javascript
jQuery中用on绑定事件时需注意的事项
2017/03/19 Javascript
vue watch监听对象及对应值的变化详解
2018/02/24 Javascript
JS实现动态添加外部js、css到head标签的方法
2019/06/05 Javascript
判断“命令按钮”是否被鼠标单击详解
2019/07/31 Javascript
Vue2.0 实现页面缓存和不缓存的方式
2019/11/12 Javascript
微信小程序获取当前时间及星期几的实例代码
2020/09/20 Javascript
[05:46]DOTA2英雄梦之声_第18期_陈
2014/06/20 DOTA
[00:57]林俊杰助阵DOTA2亚洲邀请赛
2015/01/28 DOTA
python中使用zip函数出现错误的原因
2018/09/28 Python
对python中if语句的真假判断实例详解
2019/02/18 Python
pytest中文文档之编写断言
2019/09/12 Python
python GUI库图形界面开发之PyQt5输入对话框QInputDialog详细使用方法与实例
2020/02/27 Python
python是怎么被发明的
2020/06/15 Python
Python SMTP发送电子邮件的示例
2020/09/23 Python
CSS3 制作旋转的大风车(充满童年回忆)
2013/01/30 HTML / CSS
HTML5 Canvas 实现圆形进度条并显示数字百分比效果示例
2017/08/18 HTML / CSS
瑜伽服装品牌:露露柠檬(lululemon athletica)
2017/06/04 全球购物
中专毕业生自荐信范文
2013/11/28 职场文书
派出所所长先进事迹
2014/05/19 职场文书
学习十八届四中全会精神思想汇报
2014/10/23 职场文书
高效课堂教学反思
2016/02/24 职场文书
Golang Elasticsearches 批量修改查询及发送MQ
2022/04/19 Golang