How to Add Rating Bar in dhtmlxGrid

| Leave a comment

This quick tutorial leads you through the steps of adding a simple star rating bar in a cell of dhtmlxGrid. You’ll learn how to create a custom exCell (“extended cell”) that allows the end users to view/set the rating of an item in our JavaScript datagrid.

DHTMLX Grid with Rating Bar

Download the demo with a star rating bar in a grid or read the tutorial and see how it can be done step by step.

 
Step 1 – Creating a Custom ExCell Type

Let’s start the creation of a custom exCell. ExCell is a unique mechanism of dhtmlxGrid that defines the data format and the way of editing data for each column (cell) in a grid. We’ll take a standard read-only “ro” exCell as a basis of our custom exCell:

  function eXcell_rating(cell){           //excell name is defined here
        eXcell_ro.call(this, cell);
  }
  eXcell_rating.prototype = new eXcell;    // nest all other methods from base class

 
Step 2 – Setting Style for Custom ExCell

The content of a cell with a star rating bar will be the following:

<div class=”stars”>
<a rating="1" class="gridRatingMarker"></a> //single star icon
<a rating="2" class="gridRatingMarker"></a>
<a rating="3" class="gridRatingMarker"></a>
<a rating="4" class="gridRatingMarker"></a>
<a rating="5" class="gridRatingMarker"></a>
</div>

We also add CSS for our rating star icons:

    <style>
        .stars {float:left; height:20px;}
        .stars li{float:left;}
        .stars li a:hover,.stars .rated{background:url(codebase/imgs/rating/star_1.png) no-repeat; width:20px; height:20px;}
        .stars a{float:left;background:url(codebase/imgs/rating/star_2.png) no-repeat;width:20px; height:20px;}
    </style>

We set path to the images of the stars in the parameter background. In our case:

  • star_1.png – for rated star icon
  • star_2.png – for unrated (empty) star icon

 
 
Step 3 – Rendering Star Rating Bar in a Grid Cell

The next step is to set up the function setValue that is responsible for rendering of a cell content. We modify this function like this:

        this.setValue=function(val){
            var r_val="<div class='stars'>";
            for (var i=1; i<=5; i++){
                if (val>=i){
                    r_val+="<a rating='"+i+"' class='rated gridRatingMarker'></a>" //set a rated star
                }
                else{
                    r_val+="<a rating='"+i+"' class='gridRatingMarker'></a>"; // set an unrated star
                };
            };
            r_val+="</div>";
            this.setCValue(r_val,val);
        };

When referring to a cell value, we need to get an original rating value and not the HTML content that was placed in the cell. Therefore, before forming the HTML content of the cell, we need to save its original value and place it into the new function getValue:

        this.setValue=function(val){
    this.cell.orig_val = val; // saving the original value of the cell

        }
        this.getValue=function(){ // adding the getValue function which returns the original value of the cell
            return this.cell.orig_val;
        }

 
Step 4 – Adding the Ability to Set Rating to a Selected Item

Now we need to add event listeners to our star elements to make them react on the user’s actions. We will use the dhtmlxEvent functionality to add the event handlers to the dhtmlxGrid body.

  • onmouseover event
  • Hovering over any rating star will turn on the mode, when the user can set a new rating to an item. So instead of showing the current average rating of the item we should highlight the stars according to the hovering star.
    If the user hovers the mouse on the star which defines mark “4”, stars with mark “1”, “2” and “3” should also be highlighted.

                    dhtmlxEvent(_grid.entBox, "mouseover", function(e){
                        var el = e.target || e.scrElement;
                        if ((el.className || "").indexOf("gridRatingMarker") !="-1"){ // check if the observable object has the class "gridRatingMarker"
                            for( var i=1; i<=5; i++){ //iterating through the star icons
                                if(i<=el.getAttribute("rating")){
                                    el.parentNode.childNodes[i-1].className = "rated gridRatingMarker" // highlight the star
                                }else{
                                    el.parentNode.childNodes[i-1].className = "gridRatingMarker" // set the star as unmarked
                                };
                            };
                        }
                    });
  • onmouseout event
  • If the user leaves a star icon without clicking on it, we should put back the average rating of the item.

                    dhtmlxEvent(_grid.entBox,"mouseout", function(e){
                        var el = e.target || e.scrElement;
                        if ((el.className || "").indexOf("gridRatingMarker") !="-1"){
                            _grid.cells4(el.parentNode.parentNode).setValue(_grid.cells4(el.parentNode.parentNode).getValue()); //set the original saved value of the cell
                        }
                    });
  • onmousedown event
  • If the user clicks on a star icon, then a new rating value should be applied.

                    dhtmlxEvent(_grid.entBox,"mousedown", function(e){
                        var el = e.target || e.scrElement;
                        if ((el.className || "").indexOf("gridRatingMarker") !="-1"){
                            _grid.cells4(el.parentNode.parentNode).setValue(el.getAttribute("rating")); // setting a new value to a cell
                        }
                    });

    We need to place these events in our exCell function.

    NOTE that the events should be attached only once but not to each row in your grid. So to avoid the events cloning, make sure that the events haven’t been already attached.
    For example:

            if (!_grid._run_rating_once){
             // attaching your events
             _grid._run_rating_once = true;
            };

    Now our exCell is ready and the final code should look the following way:

        function eXcell_rating(cell){
            eXcell_ro.call(this, cell);
            if (cell){
                var _grid = this.grid;
                if (!_grid._run_rating_once){
                    dhtmlxEvent(_grid.entBox, "mouseover", function(e){
                        var el = e.target || e.scrElement;
                        if ((el.className || "").indexOf("gridRatingMarker") !="-1"){
                            for( var i=1; i<=5; i++){
                                if(i<=el.getAttribute("rating")){
                                    el.parentNode.childNodes[i-1].className = "rated gridRatingMarker"
                                }else{
                                    el.parentNode.childNodes[i-1].className = "gridRatingMarker"
                                };
                            };
                        }
                    });
                    dhtmlxEvent(_grid.entBox,"mouseout", function(e){
                        var el = e.target || e.scrElement;
                        if ((el.className || "").indexOf("gridRatingMarker") !="-1"){
                            _grid.cells4(el.parentNode.parentNode).setValue(_grid.cells4(el.parentNode.parentNode).getValue());
                        }
                    });
                    dhtmlxEvent(_grid.entBox,"mousedown", function(e){
                        var el = e.target || e.scrElement;
                        if ((el.className || "").indexOf("gridRatingMarker") !="-1"){
                            _grid.cells4(el.parentNode.parentNode).setValue(el.getAttribute("rating"));
                        }
                    });
                _grid._run_rating_once = true;
                }
            }
            this.setValue=function(val){
                this.cell.orig_val = val;
                var r_val="<div class='stars'>";
                for (var i=1; i<=5; i++){
                    if (val>=i){
                        r_val+="<a rating='"+i+"' class='rated gridRatingMarker'></a>"
                    }
                    else{
                        r_val+="<a rating='"+i+"' class='gridRatingMarker'></a>";
                    };
                };
                r_val+="</div>";
                this.setCValue(r_val,val);
            };
            this.getValue=function(){
                return this.cell.orig_val;
            }
        }
        eXcell_rating.prototype = new eXcell;

     
    That’s all. Now we’ve got a rating control inside the grid column. You can download the demo and customize the code of this custom exCell for your needs and add necessary interactivity to the rating bar.

    Leave a Reply