How can I get jqGrid frozen columns to work with word wrap on
Asked Answered
G

6

10

I am using the latest jqgrid 4.3.1 and I am trying to use frozen columns.

The issue is that I have overridden the default CSS to support word wrap (CSS solution can be seen in this question) in jqGrid and I think that is the reason that the frozen columns don't line up correctly with the regular columns. The heights of the frozen rows are not the same as the heights of the rest of the grid. Here is a screenshot.. the frozen columns are highlighted in the red box (I crossed out the content given it's not a public site:

enter image description here

Is there any way to have frozen columns line up with word wrapped cells in jqGrid?

Update

After trying Oleg's solution below, it works in Firefox but in IE8 I don't see the horizontal scroll bar (see pic).

Firefox:

enter image description here

IE: (notice no horizontal scroll bar)

enter image description here

Note:

To help answer Oleg's question, here is a dump of my jqGrid Setup:

jQuery(gridSelector).jqGrid({
    mtype: 'POST',
    toppager: true,
    url: siteRoot + controller + "/" + gridDataName + "?" + querystring,
    datatype: "json",
    colNames: names,
    colModel: model,
  shrinkToFit: false,

    imgpath: siteRoot + "Scripts/jqGrid431/themes/steel/images",
    rowNum: 20,
    rowList: [10, 20, 50, 999],
    altRows: true,
    altclass: "altRow",
    jsonReader: {
        root: "Rows",
        page: "Page",
        total: "Total",
        records: "Records",
        repeatitems: false,
        id: "Id"
    },
    search: true,
    postData: (myfilter) ? { filters: JSON.stringify(myfilter)} : {},
    //postData:  { filters: JSON.stringify(myfilter) },
    pager: pagerSelector,
    height: "auto",
    sortname: sortCol,
    viewrecords: true,
    sortorder: sortDirection,
    beforeRequest: function () {

        var grid = jQuery(gridSelector);
        if (gridprefs && gridprefs.filter) {
            grid.setPostDataItem('_search', true);
            for (var prop in gridprefs.filter) {
                var value = eval('gridprefs.filter.' + prop);
                if ('' + value != '') {
                    grid.setPostDataItem(prop, value);
                }
            }

            grid.setPostDataItem('sidx', gridprefs.scol);
            grid.setPostDataItem('sord', gridprefs.sord);
            grid.setPostDataItem('page', gridprefs.page);
            grid.setPostDataItem('rows', gridprefs.rows);
            grid.jqGrid('setGridParam', {
                sortname: gridprefs.scol,
                sortorder: gridprefs.sord,
                page: gridprefs.page,
                rowNum: gridprefs.rows
            });
        }
    },
    loadComplete: function () {

        var newCapture = "", filters, rules, rule, op, i, iOp,
                    postData = jQuery(gridSelector).jqGrid("getGridParam", "postData"),
                    isFiltering = jQuery(gridSelector).jqGrid("getGridParam", "search");

        if (isFiltering === true && typeof postData.filters !== "undefined") {
            filters = $.parseJSON(postData.filters);
            newCapture = "Filter: [";
            rules = filters.rules;
            for (i = 0; i < rules.length; i++) {
                rule = rules[i];
                op = rule.op;  // the code name of the operation
                iOp = $.inArray(op, arOps);
                if (iOp >= 0 && typeof $.jgrid.search.odata[iOp] !== "undefined") {
                    op = $.jgrid.search.odata[iOp];
                }
                newCapture += rule.field + " " + op + " '" + rule.data + "'";
                if (i + 1 !== rules.length) {
                    newCapture += ", ";
                }
            }
            newCapture += "]";
        }
        jQuery(gridSelector).jqGrid("setCaption", newCapture);
        fixPositionsOfFrozenDivs.call(this);

        $(gridSelector).supersleight({ shim: siteRoot + 'Content/Images/shim.gif' });
        if (gridprefs && gridprefs.filter) {
            for (var prop in gridprefs.filter) {
                $('#gs_' + prop).val(eval('gridprefs.filter.' + prop));
            }
            $(".ui-pg-selbox").val(gridprefs.rows);
            $(".ui-pg-input").val(gridprefs.page);
        }
        gridprefs = {};
    },
    editurl: siteRoot + controller + "/Update" + appendRoute,
    ondblClickRow: editable ?
        function (rowid) {
            jQuery(gridSelector).editGridRow(rowid, { width: 600 });
        } :
        function (rowid) { }
});

//$(gridSelector).jqGrid('navGrid', '#pager', { search: true, cloneToTop: true });
$(gridSelector).jqGrid('filterToolbar', { stringResult: true, searchOnEnter: true });

jQuery(gridSelector).jqGrid('bindKeys', {});

if (editable) {
    jQuery(gridSelector).navGrid(pagerSelector,
        { cloneToTop: true, refresh: false
        },
        { height: 380, width: 500, reloadAfterSubmit: true, closeAfterEdit: true, url: siteRoot + controller + "/Update", zIndex: 1100 },
        { height: 380, width: 500, reloadAfterSubmit: true, closeAfterAdd: true, url: siteRoot + controller + "/Add", zIndex: 1100 },
        { reloadAfterSubmit: true, url: siteRoot + controller + "/Delete" },
        { multipleSearch: true,
            beforeShowSearch: function($form) {
                $('#searchmodfbox_' + $(gridSelector)[0].id).width(560);
            }
        });

} else {
    jQuery(gridSelector).navGrid(pagerSelector,
        { cloneToTop: true, refresh: false, add: false, edit: false, del: false },
        { }, { }, { }, { multipleSearch: true,
            beforeShowSearch: function($form) {
                $('#searchmodfbox_' + $(gridSelector)[0].id).width(560);
            }
        });
    }

    myAddButton(gridSelector, {
        caption: "",
        title: "Reload Grid",
        buttonicon: 'ui-icon-refresh',
        onClickButton: function () {
            $(gridSelector).trigger("reloadGrid");
        }
    });

}
Gosselin answered 31/12, 2011 at 5:58 Comment(4)
Is the issue the scrollbar in the header? Or are the cells below the header "Workstream" supposed to be taller and match those under "Team"?Serin
@Serin - both :)Gosselin
Do you want word-wrap on in the body, or just the header?Serin
@Serin - both :)Gosselin
S
14

The implementation of frozen columns in jqGrid are based on creating of two additional divs with absolute position over the standard grid. If the height of all column headers and all rows of the grid's body are the same the frozen columns works good, but in case of variable height (usage of height: auto CSS) one have the following results (see the first demo):

enter image description here

The first div, so named fhDiv, which I marked with yellow color contains the copy of the column header (the hDiv) where the last non-frozen columns are removed. In the same way the second div, so named fbDiv, which I marked with red color contains the copy of the grid body (the bDiv) where the last non-frozen columns are removed. You can read here more about the standard grid elements.

In the demo I used character wrapping in the column headers which I described in the answer and the word wrapping described for example here.

The height of every row of the fhDiv or fbDiv will be calculated independent from the height of non-frozen columns. So the height of the rows can be less as required.

It is difficult to suggest perfect solution of the problem, but it seems that I found good enough pragmatical way. The idea is to set the height of every row from the fhDiv and fbDiv explicitly based on the size of the corresponding row in the main dives hDiv and bDiv. So I extended the code of fixPositionsOfFrozenDivs function described in the answer to the following:

var fixPositionsOfFrozenDivs = function () {
        var $rows;
        if (typeof this.grid.fbDiv !== "undefined") {
            $rows = $('>div>table.ui-jqgrid-btable>tbody>tr', this.grid.bDiv);
            $('>table.ui-jqgrid-btable>tbody>tr', this.grid.fbDiv).each(function (i) {
                var rowHight = $($rows[i]).height(), rowHightFrozen = $(this).height();
                if ($(this).hasClass("jqgrow")) {
                    $(this).height(rowHight);
                    rowHightFrozen = $(this).height();
                    if (rowHight !== rowHightFrozen) {
                        $(this).height(rowHight + (rowHight - rowHightFrozen));
                    }
                }
            });
            $(this.grid.fbDiv).height(this.grid.bDiv.clientHeight);
            $(this.grid.fbDiv).css($(this.grid.bDiv).position());
        }
        if (typeof this.grid.fhDiv !== "undefined") {
            $rows = $('>div>table.ui-jqgrid-htable>thead>tr', this.grid.hDiv);
            $('>table.ui-jqgrid-htable>thead>tr', this.grid.fhDiv).each(function (i) {
                var rowHight = $($rows[i]).height(), rowHightFrozen = $(this).height();
                $(this).height(rowHight);
                rowHightFrozen = $(this).height();
                if (rowHight !== rowHightFrozen) {
                    $(this).height(rowHight + (rowHight - rowHightFrozen));
                }
            });
            $(this.grid.fhDiv).height(this.grid.hDiv.clientHeight);
            $(this.grid.fhDiv).css($(this.grid.hDiv).position());
        }
    };

I called the method inside of resizeStop and loadComplete callbacks. In case of usage of gridResize method one need include additional fixes inside of stop handler.

The full my suggestions you can see on the demo which fixs the results from the first demo to the following:

enter image description here

UPDATED: The answer contains updated version of the demo: this one.

Segregate answered 7/1, 2012 at 17:4 Comment(14)
thanks Oleg . . a few things i noticed. I had to add the: resizeColumnHeader.call($grid[0]); in the stop: function. Also, this works great in firefox but i just tested in IE and i don't see any horizontal scroll bar at the bottom. i added a pic in the question to showGosselin
@leora: I tested my demo on IE8 and can't see the problem which you describes. Can you provide an online test demo which has the problem? Probably you have some other problems with the CSS which you used. Look at the CSS which I use in my demo and compare there with youth.Segregate
good idea . . i will revert back to the original out of the box css and only make the changes that you have added . .Gosselin
so i know now that it has nothing to do with any of this frozen column stuff. Even when i create a basic jqgrid and set an explicit width (that is smaller than the sum of the col widths) firefox and chrome both show a horizontal scroll bar but IE doesn'tGosselin
also, to help clarify my comment directly above, i have dumped all of the jqgrid settings at the bottom of the questionGosselin
@leora: The information which you posted don't contain CSS settings which probably are the origin of the problem. Without debugging the problem I could suggest you only the following workaround. You can include in the CSS settings the following: .ui-jqgrid .ui-jqgrid-bdiv { overflow: auto !important; } and .ui-jqgrid .frozen-bdiv { overflow: hidden !important; }. It should fix the problem. Nevertheless I would recommend you to use IE Developer tools (press F12), choose the bdiv which is under div.ui-jqgrid > div.ui-jqgrid-view and then verify which your CSS overwrites the overflow.Segregate
@leora: Do you have any success with the CSS which I suggested? Do you examined the CSS applied on div.ui-jqgrid > div.ui-jqgrid-view which exist over the grid?Segregate
I am still struggling to get a "clean" solution to frozen columns with word wrap on . . do you know if this is going to be supported as a core feature in any upcoming version . . thanks for all of your suggestionsGosselin
@leora: You are welcome! I have posted my suggestions to trirand (starting with this post, see also this post), but Tony till now don't made any changes. I posted many other clear bugfixes (like this one) which are still uncommented and not fixed. I don't know the reason. Sorry.Segregate
@leora: I have idea how to make pure implementation of frozen columns. One need to find good way to display transparent cells in the <table>. The frozen div consist now from the copy of the main table where last columns are removed. The frozen div has absolute position. So the idea is to make the non-frozen columns on the frozen div transparent instead of removing there. In the case the height of rows will be calculated in the frozen div exactly as in the main div and one will have perfect results even with wrapped texts. I don't have enough time to implement what I described above.Segregate
i accepted the answer as its the best answer but my opinion is still that jqGrid should support this out of the box as this seems like a lot of hacking to get this to work and still seems a bit fragile. I appreciate your thoughts and insights hereGosselin
@leora: I am not developer of jqGrid. I have been posted feature requests and bug reports to trirand (to Tony), but sometime Tony have another opinion about the problem as me. :-) I posted some bug reports about frozen columns too, but the most from there are stay just unanswerd. I agree with you that fixing jqGrid code is the best way, but I can't suggest you more as I can.Segregate
Oleg, thanks for sharing this code - I ran into the same problem, and your function worked to fix it. I did have to make one slight tweak though. I tried setting resizeStop: fixPositionsOfFrozenDivs and loadComplete: fixPositionsOfFrozenDivs, but the two events have different 'this' when they fire. for loadComplete, 'this' is the grid table but for resizeStop it's the grid itself. So, I inserted a line at the top of your method: var grid = this.grid || this. Then, the I replaced the references in your method to "this.grid" with "grid" (the local var). Now it works perfectly for me.Childbearing
@MarkShapiro: you are welcome! Every new version of jqGrid have some changes so it's usual that One have to make some small adaptation of the code. Happy New Year!Segregate
I
2

So this is the function that will resize a columns.

function updateSize(){

    //getting all lines in two tables by they id
    var lines = $("tr", this),
        flines = $("tr", "#"+$(this).attr("id")+"_frozen" );

    //setting in all frozen lines height equel to grid
    flines.each(function(i, item){

        //i%2 check because of border collapse
        $(item).height( $(lines[i]).innerHeight() - (i%2?1:0) );
    });
}

CSS rule

.ui-jqgrid tr.jqgrow td{
    height: auto;
    white-space: normal;
}

And the init

jQuery("#gfrc1").jqGrid({ 
    //options
    'loadComplete': updateSize,
    'resizeStop': updateSize
});
//Frozen Columns init
jQuery("#gfrc1").jqGrid('setFrozenColumns');

Not good but it works. There a bit buggy thing when i tried to set it with setParams method, context is changing so better to do it on init of jqGrid. If you will need to set you functionality just use apply to updateSize method in your function and save this context.

The other thing is in height and border-collapse, i don't know really how to solve that problem better :)

And example with static data.

Infusion answered 4/1, 2012 at 2:48 Comment(0)
S
1

Assuming this is the CSS you used (from your link):

.ui-jqgrid tr.jqgrow td {
    white-space: normal !important;
    height:auto;
    vertical-align:text-top;
    padding-top:2px;
}

You should be able to get rid of the scrollbar with (note the selector is .ui-jqgrid tr.jqgrow not .ui-jqgrid tr.jqgrow td):

.ui-jqgrid tr.jqgrow {
    overflow: hidden;
}

The height: auto; is what's causing the rows to be shorter. Try removing that from your CSS entirely. If that doesn't work you can also either set all <td> elements to the same height or all to auto (both untested, since you didn't post code).

Serin answered 3/1, 2012 at 3:24 Comment(0)
S
1

try this:

qgrid tr.jqgrow td {

white-space: nowrap !important;

}
Slash answered 7/1, 2012 at 18:48 Comment(0)
H
1

I wasn't able to get Oleg's solution to work, but I made something based on his work that is working for me. Not sure if I missed something, but since this is working for me I thought I'd share:

I also had to add a line to my CSS file

.frozen-div{overflow:hidden;}

 var fixPositionsOfFrozenDivs = function () {
     var originalRowHeightArray = new Array();
     var gridId = $(this).attr("id");

     $("#" + gridId).find("tr").each(function () {
         originalRowHeightArray.push($(this).find("td").first().height() + 1);
     });

     $rows = $('>div>table.ui-jqgrid-btable>tbody>tr', this.grid.bDiv);

     $rows.each(function (i) {
         var rowHight = $($rows[i]).height(), rowHightFrozen = $(this).height();
         $(this).height(originalRowHeightArray[i] + "px");
     });
 };
Helgoland answered 31/1, 2013 at 21:43 Comment(1)
your solution worked perfect for me. Had the same issue and called your function after grid had been loaded. The frozen and non-frozen rows are MUCH more aligned now. Thanks!Engen
I
0

I`ve looked at it and get why is this problem appeared.

When frozen columns is showing actually there is some duplicating of tables, but only the frozen cols and if you setting white-space to normal, and somewhere in grid appears not equal height column in not frozen area it's starting to missmatch in height.

The duplicating of tables done to do not recalculate params of tables in js, so there is some problem that if you need this css property than you should set your gridComplete and resizeStop events (or anyone else that will change cols width) to function like this:

function onChangeGrid(){
   var frozen = $("#grid_frozen tr", this),
       rows = $("#grid tr", this);

   frozen.each(function(i, item){
     var fEl = $(item),
         h = $(rows[i]).height();

     if( fEl.height() < h ){
       fEl.height(h); 
     } else {
       fEl.css("height", "auto");
     }
   });
}


$("#my_grid").jqGrid({
  gridComplete: onChangeGrid,
  resizeStop: onChangeGrid
});

It's not testetd i wrote it here but some fixes and should work fine, i havent jqGrid setup now.

Have fun :)

By the way, it's terrible fix if you need to see a lot of rows, and it would be nice to set issue task on github

Infusion answered 3/1, 2012 at 3:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.