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 相关文章推荐
web页面数据展示新想法(json)
Jun 08 Javascript
JavaScript判断窗口是否最小化的代码(跨浏览器)
Aug 01 Javascript
鼠标悬浮显示二级菜单效果的jquery实现
Oct 29 Javascript
javascript多物体运动实现方法分析
Jan 08 Javascript
BootStrap智能表单实战系列(九)表单图片上传的支持
Jun 13 Javascript
微信小程序实现的涂鸦功能示例【附源码下载】
Jan 12 Javascript
vue-cli2.0转3.0之项目搭建的详细步骤
Dec 11 Javascript
微信小程序开发实现消息推送
Nov 18 Javascript
开源一个微信小程序仪表盘组件过程解析
Jul 30 Javascript
JavaScript多种图形实现代码实例
Jun 28 Javascript
在js文件中引入(调用)另一个js文件的三种方法
Sep 11 Javascript
js实现简易点击切换显示或隐藏
Nov 29 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
win7 64位系统 配置php最新版开发环境(php+Apache+mysql)
2014/08/15 PHP
ThinkPHP安装和设置
2015/07/27 PHP
PHP格式化MYSQL返回float类型的方法
2016/03/30 PHP
php使用number_format函数截取小数的方法分析
2016/05/27 PHP
Android AsyncTack 异步任务实例详解
2016/11/02 PHP
数组任意位置插入元素,删除特定元素的实例
2017/03/02 PHP
PHP生成短网址的思路以及实现方法的详解
2019/03/25 PHP
Laravel框架查询构造器简单示例
2019/05/08 PHP
可缩放Reloaded-一个针对可缩放元素的复用组件
2007/03/10 Javascript
javascript Array.prototype.slice使用说明
2010/10/11 Javascript
详解Javascript模板引擎mustache.js
2016/01/20 Javascript
jQuery实现可拖拽的许愿墙效果【附demo源码下载】
2016/09/14 Javascript
Vue2.0组件间数据传递示例
2017/03/07 Javascript
详解vue 计算属性与方法跟侦听器区别(面试考点)
2018/04/23 Javascript
浅析Vue项目中使用keep-Alive步骤
2018/07/27 Javascript
vue主动刷新页面及列表数据删除后的刷新实例
2018/09/16 Javascript
vue过滤器用法实例分析
2019/03/15 Javascript
通过JavaScript下载文件到本地的方法(单文件)
2019/03/17 Javascript
详解jquery和vue对比
2019/04/16 jQuery
Node快速切换版本、版本回退(降级)、版本更新(升级)
2021/01/07 Javascript
[42:20]2014 DOTA2华西杯精英邀请赛5 24 DK VS NewBee
2014/05/25 DOTA
python 中的divmod数字处理函数浅析
2017/10/17 Python
Python基于OpenCV库Adaboost实现人脸识别功能详解
2018/08/25 Python
解决Python3 抓取微信账单信息问题
2019/07/19 Python
使用python-opencv读取视频,计算视频总帧数及FPS的实现
2019/12/10 Python
python实点云分割k-means(sklearn)详解
2020/05/28 Python
解决TensorFlow调用Keras库函数存在的问题
2020/07/06 Python
网页切图的CSS和布局经验与要点
2015/04/09 HTML / CSS
阿迪达斯芬兰官方网站:adidas芬兰
2017/01/30 全球购物
电话销售经理岗位职责
2013/12/07 职场文书
自我鉴定三原则
2014/01/13 职场文书
组织关系转移介绍信
2014/01/16 职场文书
普通大学毕业生自荐信范文
2014/02/23 职场文书
清洁员岗位职责
2015/02/15 职场文书
OpenCV-Python使用cv2实现傅里叶变换
2021/06/09 Python
详解在SQLPlus中实现上下键翻查历史命令的功能
2022/03/18 SQL Server