jQuery插件——jRating評分插件源碼分析


  該插件被廣泛應用於各種需要評分的頁面當中,今天作為學習,把源碼拿出來分析一下,順便學習其使用方法。

  一、插件使用一覽。

    <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數據,用后台數據將評分數據持久化。

  在分析源代碼之前,我們先看一下使用該插件時有哪些可選參數:

  
  二、插件源碼分析
  
  按照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來返回鼠標選擇的比例,這個比例是什么,見下圖用筆刷框起來的部分:

  接下來,我們再關注一個函數:

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>&nbsp;&nbsp;
        <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顆紅星嘛):

  看看數據庫的變化:

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

  

 

 

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM