jqGrid Column Group

Lorenzo picture Lorenzo · Sep 8, 2011 · Viewed 15.1k times · Source

can I implement a column group in jqGrid like the jQuery EasyUI library does?

You can figure out what I mean by going on the jQuery EasyUI demo web site, and choose Datagrid then Column Group from the left menu.

Thanks for helping

Answer

Oleg picture Oleg · Sep 8, 2011

Your question is not new. Many times the corresponding feature request was asked in trirand forum or on the stackoverflow. I give another answer on the close question some time before.

Now after reading of your question I do decided don't make a perfect solution supporting all jqGrid features (which is too difficult at once). Instead of that I decide to create a solution which can be already used in many cases, but which has some restrictions.

The demo shows my first results:

enter image description here

The restrictions:

  • the columns of the grid can not be resized. I use in the demo cmTemplate: {resizable: false} parameter to set resizable: false in all grid columns.
  • sortable: true is not supported
  • showCol, hideCol or columnChooser are not currently supported, but I am sure that one can quickly fix the problems.
  • columns over which one will place an additional column header should has the same width. If the columns have different width values the total width of the column will be divided between the columns automatically.

On the other side everything work without any problem in all my tests with other popular options like shrinkToFit: false, autowidth: true or changing of the grid width with respect of setGridWidth method (with or without shrinking).

Now first about the usage of the feature. I wrote function insertColumnGroupHeader which I use in the above example as

insertColumnGroupHeader(grid, 'amount', 3, '<em>Information about the Price</em>');

It inserts an additional column header with the HTML fragment 'Information about the Price' over 3 columns starting with the column 'amount'. So the usage is pretty simple. You can of course use just any text like 'Information about the Price' as the additional column header.

The function insertColumnGroupHeader has the following code:

var denySelectionOnDoubleClick = function ($el) {
        // see https://stackoverflow.com/questions/2132172/disable-text-highlighting-on-double-click-in-jquery/2132230#2132230
        if ($.browser.mozilla) {//Firefox
            $el.css('MozUserSelect', 'none');
        } else if ($.browser.msie) {//IE
            $el.bind('selectstart', function () {
                return false;
            });
        } else {//Opera, etc.
            $el.mousedown(function () {
                return false;
            });
        }
    },
    insertColumnGroupHeader = function (mygrid, startColumnName, numberOfColumns, titleText) {
        var i, cmi, skip = 0, $tr, colHeader, iCol, $th,
            colModel = mygrid[0].p.colModel,
            ths = mygrid[0].grid.headers,
            gview = mygrid.closest("div.ui-jqgrid-view"),
            thead = gview.find("table.ui-jqgrid-htable>thead");

        mygrid.prepend(thead);
        $tr = $("<tr>");
        for (i = 0; i < colModel.length; i++) {
            $th = $(ths[i].el);
            cmi = colModel[i];
            if (cmi.name !== startColumnName) {
                if (skip === 0) {
                    $th.attr("rowspan", "2");
                } else {
                    denySelectionOnDoubleClick($th);
                    $th.css({"padding-top": "2px", height: "19px"});
                    $tr.append(ths[i].el);
                    skip--;
                }
            } else {
                colHeader = $('<th class="ui-state-default ui-th-ltr" colspan="' + numberOfColumns +
                    '" style="height:19px;padding-top:1px;text-align:center" role="columnheader">' + titleText + '</th>');
                denySelectionOnDoubleClick($th);
                $th.before(colHeader);
                $tr.append(ths[i].el);
                skip = numberOfColumns - 1;
            }
        }
        mygrid.children("thead").append($tr[0]);
    };

Additionally it was required to make some changes in the jqGrid code. You can download the modified version (the modification of the 4.1.2 version) of jquery.jqGrid.src.js from here. The changes consist from two blocks. First I changed the code of sortData function, the lines 1874-1884

var thd= $("thead:first",ts.grid.hDiv).get(0);
$("tr th:eq("+ts.p.lastsort+") span.ui-grid-ico-sort",thd).addClass('ui-state-disabled');
$("tr th:eq("+ts.p.lastsort+")",thd).attr("aria-selected","false");
$("tr th:eq("+idxcol+") span.ui-icon-"+ts.p.sortorder,thd).removeClass('ui-state-disabled');
$("tr th:eq("+idxcol+")",thd).attr("aria-selected","true");
if(!ts.p.viewsortcols[0]) {
    if(ts.p.lastsort != idxcol) {
        $("tr th:eq("+ts.p.lastsort+") span.s-ico",thd).hide();
        $("tr th:eq("+idxcol+") span.s-ico",thd).show();
    }
}

to the following:

var previousSelectedTh = ts.grid.headers[ts.p.lastsort].el,
    newSelectedTh = ts.grid.headers[idxcol].el;
$("span.ui-grid-ico-sort",previousSelectedTh).addClass('ui-state-disabled');
$(previousSelectedTh).attr("aria-selected","false");
$("span.ui-icon-"+ts.p.sortorder,newSelectedTh).removeClass('ui-state-disabled');
$(newSelectedTh).attr("aria-selected","true");
if(!ts.p.viewsortcols[0]) {
    if(ts.p.lastsort != idxcol) {
        $("span.s-ico",previousSelectedTh).hide();
        $("span.s-ico",newSelectedTh).show();
    }
}

Next I defined getColumnHeaderIndex function as the following

var getColumnHeaderIndex = function (th) {
    var i, headers = ts.grid.headers, ci = $.jgrid.getCellIndex(th);
    for (i = 0; i < headers.length; i++) {
        if (th === headers[i].el) {
            ci = i;
            break;
        }
    }
    return ci;
};

and changed the lines 2172 and 2185 of grid.base.js from

var ci = $.jgrid.getCellIndex(this);

to

var ci = getColumnHeaderIndex(this);

It's all. The above described changes should have no negative influence on the original jqGrid code and can be used as usual. I will publish my suggestion in the next time on trirand forum.

UPDATED: Another version of the demo allows resizing of all columns excepting the columns having the headers. In the version all the columns over which one will place an additional column header have to have the same width. The width of the columns not divided between the columns automatically. You have to set the same column width manually.

UDPATED 2: I want to inform about some progress in creating more advanced version of multiheader jqGrid. First wildraid posted very interesting solution. See his demo here. By the way if one use with the method jqGrid with the fixes which I suggested (see above) the problem with sorting icons in the demo will be solved. See here the demo as the conformation.

After that I works a little more abut reducing restrictions in my multicolumn approach which use rowSpan to increase the height of the columns. Here is my current intermediate result: the new demo. The new demo work already very good in Internet Explorer 9/8, Firefox and Opera. In Webkit-based browsers (Google Chrome and Safari) it has still the above listed restriction (column headers which has multiheaders have to have the same size and be not resizable). The demo looks good has the restrictions and it looks good in Webkit-based web browsers. Nevertheless you can see progress in the sort time.

I plan to increase the height of the resizable area used to resize the columns based on the demo from the answer. Of cause the usage of many headers over column headers will be also supported. ColumnChooser or showCol/hideCol will be supported too. The most interesting for me now is to finding a clear way how to implement multirows column headers using rowSpan in Webkit-based browsers (Google Chrome and Safari). Probably somebody else will find a solution way? It is the main reason why I decide to share not completed results here.

UPDATE 3: The changes in the code of jqGrid are included (see here) in the main code of jqGrid. I improved the solution which I described here to this and this demos. The second demo increase the grid width if the column width will be increased. I personally like the behavior.

UPDATE 4: The next version of the demo you can see here. It has an boolean option (the parameter useColSpanStyle) which which defines whether colspan should be used or not. With false value of the parameter the results will be the following.