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 相关文章推荐
jQuery.extend 函数的详细用法
Jun 27 Javascript
jquery ajax方式直接提交整个表单核心代码
Aug 15 Javascript
js中的push和join方法使用介绍
Oct 08 Javascript
jQuery获取样式中颜色值的方法
Jan 29 Javascript
10个JavaScript中易犯小错误
Feb 14 Javascript
jQuery 如何给Carousel插件添加新的功能
Apr 18 Javascript
JavaScript比较两个数组的内容是否相同(推荐)
May 02 Javascript
解决React Native端口号修改的方法
Jul 28 Javascript
JavaScript实现树的遍历算法示例【广度优先与深度优先】
Oct 26 Javascript
iview tabs 顶部导航栏和模块切换栏的示例代码
Mar 04 Javascript
ES5新增数组的实现方法
May 12 Javascript
js代码编写无缝轮播图
Sep 13 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中file_get_content 和curl以及fopen 效率分析
2014/09/19 PHP
基于PHP实现商品成交时发送短信功能
2016/05/11 PHP
laravel5.6 框架操作数据 Eloquent ORM用法示例
2020/01/26 PHP
Yii使用EasyWechat实现小程序获取用户的openID的方法
2020/04/29 PHP
javascript 函数使用说明
2010/04/07 Javascript
jQuery中ready事件用法实例
2015/01/19 Javascript
JavaScript判断前缀、后缀是否是空格的方法
2015/04/15 Javascript
javascript弹出拖动窗口
2015/08/11 Javascript
JavaScript提高性能知识点汇总
2016/01/15 Javascript
jQuery实现图片局部放大镜效果
2016/03/17 Javascript
关于javascript的一些知识以及循环详解
2016/09/12 Javascript
实例讲解Vue.js中router传参
2018/04/22 Javascript
对mac下nodejs 更新到最新版本的最新方法(推荐)
2018/05/17 NodeJs
vue项目前端知识点整理【收藏】
2019/05/13 Javascript
vue+elementUI实现图片上传功能
2019/08/20 Javascript
Vue+abp微信扫码登录的实现代码示例
2020/01/06 Javascript
[53:15]2018DOTA2亚洲邀请赛3月29日 小组赛A组 LGD VS TNC
2018/03/30 DOTA
Python中super()函数简介及用法分享
2016/07/11 Python
scrapy spider的几种爬取方式实例代码
2018/01/25 Python
使用python的pexpect模块,实现远程免密登录的示例
2019/02/14 Python
python GUI库图形界面开发之PyQt5计数器控件QSpinBox详细使用方法与实例
2020/02/28 Python
python GUI库图形界面开发之PyQt5状态栏控件QStatusBar详细使用方法实例
2020/02/28 Python
深入浅析Python 命令行模块 Click
2020/03/11 Python
Django-migrate报错问题解决方案
2020/04/21 Python
keras 多gpu并行运行案例
2020/06/10 Python
Python爬取数据并实现可视化代码解析
2020/08/12 Python
Html5 FileReader实现即时上传图片功能实例代码
2014/09/01 HTML / CSS
HTML5响应式(自适应)网页设计的实现
2017/11/17 HTML / CSS
洲际酒店集团大中华区:IHG中国
2016/08/17 全球购物
环境工程与管理大学毕业生求职信
2013/10/02 职场文书
高中生学习的自我评价
2013/12/14 职场文书
2014年秋季开学演讲稿
2014/05/24 职场文书
大型公益活动策划方案
2014/08/20 职场文书
党员教师个人对照检查材料(群众路线)
2014/09/26 职场文书
股权转让协议范本
2014/12/07 职场文书
详解Js模块化的作用原理和方案
2021/04/29 Javascript