該插件被廣泛應用於各種需要評分的頁面當中,今天作為學習,把源碼拿出來分析一下,順便學習其使用方法。
一、插件使用一覽。
<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>
執行效果:

可以看到,上面的例子中,有10顆星,是參數length的作用。其中,默認總分是20分,就是10顆星都選擇。這里我們着重注意<div>的id16_1,其中16被用來初始化評分插件默認選擇的比例,16/20 * 10。所以我們上面有8顆星是黃色的。
當我們把鼠標放到插件上時,小星星會隨着鼠標移動而增加或減少(紅色會覆蓋黃色或白色),表示評分的從0至20,但點擊鼠標時,評分結束,插件不能再編輯了,同時,通過Ajax向指定的路徑POST數據,用后台數據將評分數據持久化。
在分析源代碼之前,我們先看一下使用該插件時有哪些可選參數:
(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來返回鼠標選擇的比例,這個比例是什么,見下圖用筆刷框起來的部分:

接下來,我們再關注一個函數:
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>不是具有高度的長方形顏色條嗎?下面我們看一下小星星的圖片就明白為什么了!

不用多說,旁邊用不透明的背景,中間小星星是透明的,下面的顏色自然就顯示出來了!!
接下來的語句很簡單,就是設置一下最外層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、標題、文章內容、評分四個字段,截圖如下:

評分字段默認為-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屬性中。
頁面效果如下:

我們可以看到上面前台頁面的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顆紅星嘛):

看看數據庫的變化:

試驗成功!今天學習就到這里,希望此篇學習筆記對大家能有所幫助!
