Jsgrid: Support resizable columns

Created on 23 Mar 2015  路  13Comments  路  Source: tabalinas/jsgrid

Is it possible to support resizable columns in the future?
That you can edit the column width by dragging a handle.

enhancement

Most helpful comment

I was able to accommodate column resizing using the colResizable library and adding the following code to my grid:

    onRefreshed: function (args) {
        //sync column width on page load
        $.each(args.grid._headerGrid[0].rows[0].cells, function (i, obj) {
            $(args.grid._bodyGrid[0].rows[0].cells[i]).css("width", $(obj).css("width"));
        });

        //sync column width on column resize
        $("table").colResizable({
            onResize: function () {
                $.each(args.grid._headerGrid[0].rows[0].cells, function (i, obj) {
                    $(args.grid._bodyGrid[0].rows[0].cells[i]).css("width", $(obj).css("width"));
                });
            }
        });
    },

All 13 comments

Yes, It's a nice grid feature. It's planned. Thank you for request!

Hi @tabalinas

Any news on the column resize? :)

Thank you!

Hi, no progress on this yet. Next upcoming features are adapter for angular and improvement of keyboard support.
Thanks for your interest!

We are also eagerly awaiting this functionality (just an FYI).

Hello all, I am using also this plugin and need this functionality.
I had a look in the code to add in _createHeaderRow
$th.resizable({handles: 'e'});

but it seems more complicate because the header and the body are into 2 different

. any idea ?

Have there been any updates in regards to this or has anyone come up with a workaround in the meantime?

Thanks!

I'm also keen on this functionality.

+1

I did make some progress on this early last year, but the company I worked for at the time blocked me from contributing to this project.

If I get the chance I will see if I can implement this functionality as it seems to be popular.

I've implemented a column resize by combining an external table resize script with jsgrid.
Use this script for the column resize in a separate file:

;(function() {
    function preventEvent(e) {
        var ev = e || window.event;
        if (ev.preventDefault) ev.preventDefault();
        else ev.returnValue = false;
        if (ev.stopPropagation)
            ev.stopPropagation();
        return false;
    }

    function getWidth(x) {
        if (x.currentStyle)
        // in IE
            var y = x.clientWidth - parseInt(x.currentStyle["paddingLeft"]) - parseInt(x.currentStyle["paddingRight"]);
        // for IE5: var y = x.offsetWidth;
        else if (window.getComputedStyle)
        // in Gecko
            var y = document.defaultView.getComputedStyle(x, null).getPropertyValue("width");
        return y || 0;
    }

    window.ColumnResize = function (aTable, aConnectedTable) {
        var table = aTable;
        var connectedTable = aConnectedTable;

        if (table.tagName != 'TABLE') return;

        this.id = table.id;

        // ============================================================
        // private data
        var self = this;

        var dragColumns = table.rows[0].cells; // first row columns, used for changing of width

        var dragConnectedColumns = dragColumns;
        if (connectedTable) dragConnectedColumns = connectedTable.rows[0].cells;

        if (!dragColumns) return; // return if no table exists or no one row exists

        var dragColumnNo; // current dragging column
        var dragX; // last event X mouse coordinate

        var saveOnmouseup; // save document onmouseup event handler
        var saveOnmousemove; // save document onmousemove event handler
        var saveBodyCursor; // save body cursor property

        // ============================================================
        // methods

        // ============================================================
        // do changes columns widths
        // returns true if success and false otherwise
        this.changeColumnWidth = function(no, w) {
            if (!dragColumns) return false;

            if (no < 0) return false;
            if (dragColumns.length < no) return false;

            if (parseInt(dragColumns[no].style.width) <= -w) return false;
            if (dragColumns[no + 1] && parseInt(dragColumns[no + 1].style.width) <= w) return false;

            dragColumns[no].style.width = parseInt(dragColumns[no].style.width) + w + 'px';
            dragConnectedColumns[no].style.width = parseInt(dragConnectedColumns[no].style.width) + w + 'px';

            //if (dragColumns[no + 1]) {
            //    dragColumns[no + 1].style.width = parseInt(dragColumns[no + 1].style.width) - w + 'px';
            //    dragConnectedColumns[no + 1].style.width = parseInt(dragConnectedColumns[no + 1].style.width) - w + 'px';
            //}

            return true;
        }

        // ============================================================
        // do drag column width
        this.columnDrag = function(e) {
            var e = e || window.event;
            var X = e.clientX || e.pageX;
            if (!self.changeColumnWidth(dragColumnNo, X - dragX)) {
                // stop drag!
                self.stopColumnDrag(e);
            }

            dragX = X;
            // prevent other event handling
            preventEvent(e);
            return false;
        }

        // ============================================================
        // stops column dragging
        this.stopColumnDrag = function(e) {
            var e = e || window.event;
            if (!dragColumns) return;

            // restore handlers & cursor
            document.onmouseup = saveOnmouseup;
            document.onmousemove = saveOnmousemove;
            document.body.style.cursor = saveBodyCursor;

            // remember columns widths in cookies for server side
            var colWidth = '';
            var separator = '';
            for (var i = 0; i < dragColumns.length; i++) {
                colWidth += separator + parseInt(getWidth(dragColumns[i]));
                separator = '+';
            }

            preventEvent(e);
        }

        // ============================================================
        // init data and start dragging
        this.startColumnDrag = function(e) {
            var e = e || window.event;

            // if not first button was clicked
            //if (e.button != 0) return;

            // remember dragging object
            dragColumnNo = (e.target || e.srcElement).parentNode.cellIndex;
            dragX = e.clientX || e.pageX;

            // set up current columns widths in their particular attributes
            // do it in two steps to avoid jumps on page!
            var colWidth = new Array();
            for (var i = 0; i < dragColumns.length; i++) {
                colWidth[i] = parseInt(getWidth(dragColumns[i]));
            }
            for (var i = 0; i < dragColumns.length; i++) {
                dragColumns[i].width = ""; // for sure
                dragColumns[i].style.width = colWidth[i] + "px";
                dragConnectedColumns[i].width = ""; // for sure
                dragConnectedColumns[i].style.width = colWidth[i] + "px";
            }

            saveOnmouseup = document.onmouseup;
            document.onmouseup = self.stopColumnDrag;

            saveBodyCursor = document.body.style.cursor;
            document.body.style.cursor = 'w-resize';

            // fire!
            saveOnmousemove = document.onmousemove;
            document.onmousemove = self.columnDrag;

            preventEvent(e);
        }

        // prepare table header to be draggable
        // it runs during class creation

        for (var i = 0; i < dragColumns.length; i++) {
            dragColumns[i].innerHTML = "<div class='jsgrid-header-resizable' style='" + "position:absolute;height:100%;color:transparent;width:5px;right:2px;top:0;cursor:w-resize;z-index:10;'>" + "||||</div>" +
                                       "<div style='position:relative;height:100%;width:100%'>" + dragColumns[i].innerHTML + "</div>";
            dragColumns[i].firstChild.onmousedown = this.startColumnDrag;
        }
    }
})();

Patch jsgrid script:

// append this function in the jsgrid script
_createResizableHeader: function () {
    ColumnResize(this._headerGrid[0], this._bodyGrid[0]);
},

// update _renderGrid function
_renderGrid: function() {
    this._clear();

    this._container.addClass(this.containerClass)
        .css("position", "relative")
        .append(this._createHeader())
        .append(this._createBody());

    this._createResizableHeader();

    this._pagerContainer = this._createPagerContainer();
    this._loadIndicator = this._createLoadIndicator();

    this.refresh();
},

Voila. Columns are now resizable.

I was able to accommodate column resizing using the colResizable library and adding the following code to my grid:

    onRefreshed: function (args) {
        //sync column width on page load
        $.each(args.grid._headerGrid[0].rows[0].cells, function (i, obj) {
            $(args.grid._bodyGrid[0].rows[0].cells[i]).css("width", $(obj).css("width"));
        });

        //sync column width on column resize
        $("table").colResizable({
            onResize: function () {
                $.each(args.grid._headerGrid[0].rows[0].cells, function (i, obj) {
                    $(args.grid._bodyGrid[0].rows[0].cells[i]).css("width", $(obj).css("width"));
                });
            }
        });
    },

Thanks for the recommendation hansena, this is working great!

You could dynamically auto-size the column. This is the solution i was able to come up with. Self.tables()[tableIndex] is an object like {Rows: [], Headers: []}. Additionally, you could get the type of the column dynamically for sorting.

    self.createJSGridTable = function (cssIdOfTable, tableIndex) {
        $("#" + cssIdOfTable).jsGrid({
            width: "100%",
            height: "400px",

            inserting: false,
            editing: false,
            sorting: true,
            paging: true,
            noDataContent: "No Record Found",
            loadMessage: "Please, wait...",

            data: self.tables()[tableIndex].Rows,

            fields: self.getColumns(self.tables()[tableIndex])
        });


        $("#sort").click(function () {
            var field = $("#sortingField").val();
            $("#" + cssIdOfTable + "").jsGrid("sort", field);
        });
    };

    self.getColumns = function (table) {
        columnsAsObjects = [];
        var props = [];

        if (table != null && table.Rows != null && table.Rows.length > 0) {
            props = self.propertyNamesOfRow(table.Rows[0]);
        }

        for (var i = 0; i < props.length; i++) {
            var currentProp = props[i].toString();
            columnsAsObjects.push({ name: currentProp, type: self.getType(table.Rows, currentProp), width: self.getColumnWidth(table.Rows, currentProp) + "px" });
        }

        return columnsAsObjects;
    };

    self.getType = function (rows, columnName) {
        for (var i = 0; i < rows.length; i++) {
            if (self.isNumber(rows[i][columnName])) {
                return "text";
            }
        }

        return "number";
    };

    self.getColumnWidth = function (rows, columnName) {
        var maxLength = columnName.toString().length;
        for (var i = 0; i < rows.length; i++) {
            if (rows[i][columnName] != null) {
                maxLength = rows[i][columnName].toString().length > maxLength ? rows[i][columnName].toString().length : maxLength;
            }
        }

        return maxLength * 10;
    };

    self.isNumber = function(value){
        return value != null && value != "" && typeof value != "number" && isNaN(parseInt(value));
    };

Additionally, I made a whole helper for doing JSGrid type stuff dynamically within my app.

```javascript

var JSGridHelper = (function () {

function Controller(records, columnsWithDate) {
    var self = this;
    self.records = records;
    self.columnsWithDate = columnsWithDate;

    self.getControllerObject = {
        loadData: function (filter) {

            return $.grep(self.records, function (record) {
                return shouldRecordBeKeptInResults(filter, record, self.records, self.columnsWithDate);
            });
        }
    };
};

var shouldRecordBeKeptInResults = function (filter, record, records, columnsWithDate) {
    var props = propertyNamesOfRow(record);
    var returnRecord = true;

    for (var i = 0; i < props.length; i++) {
        var currentProp = props[i].toString();

        if (filterAndRecordValuesAreNotEqual(filter[currentProp], record[currentProp])) {
            if (getType(records, currentProp, columnsWithDate) === "text") {
                if (record[currentProp] !== null && !doesRecordIncludeFilterValue(record[currentProp], filter[currentProp])) {
                    returnRecord = false;
                }
            }

            else {
                returnRecord = false;
            }
        }
    }

    return returnRecord;

};

var filterAndRecordValuesAreNotEqual = function (filterValue, recordValue) {
    return filterValue && filterValue !== '' && recordValue != filterValue;

};

var doesRecordIncludeFilterValue = function (recordValue, filterValue) {
    return recordValue.toLowerCase().includes(filterValue.toLowerCase());
};

var createNewJSGridForEachTable = function (tables, columnsWithDate) {
    var baseId = "jsGrid";
    for (var i = 0; i < tables.length; i++) {
        if (i == 0) {
            $("#tablesDiv").empty();
        }

        var jsGridId = baseId + (parseInt(i) + 1);
        $("#tablesDiv").append('<div id="' + jsGridId + '"></div>');
        createJSGridTable(tables, jsGridId, i, columnsWithDate);
    }
};

var createJSGridTable = function (tables, cssIdOfTable, tableIndex, columnsWithDate) {
    var controllerInstance = new Controller(tables[tableIndex].Rows, columnsWithDate);
    $("#" + cssIdOfTable).jsGrid({
        width: "100%",
        height: "400px",

        filtering: true,
        inserting: false,
        editing: false,
        sorting: true,
        autoload: true,
        paging: true,

        controller: controllerInstance.getControllerObject,

        noDataContent: "No Record Found",
        loadMessage: "Please, wait...",

        data: tables[tableIndex].Rows,

        fields: getColumns(tables[tableIndex], columnsWithDate)
    });


    $("#sort").click(function () {
        var field = $("#sortingField").val();
        $("#" + cssIdOfTable + "").jsGrid("sort", field);
    });
};

var propertyNamesOfRow = function (obj) {
    var returnArray = [];
    $.each(obj, function (key, value) { returnArray.push(key); });

    return returnArray;
};

var getColumns = function (table, columnsWithDate) {
    columnsAsObjects = [];
    var props = [];

    if (table != null && table.Rows != null && table.Rows.length > 0) {
        props = propertyNamesOfRow(table.Rows[0]);
    }

    for (var i = 0; i < props.length; i++) {
        var currentProp = props[i].toString();
        columnsAsObjects.push({ name: currentProp, type: getType(table.Rows, currentProp, columnsWithDate), width: getColumnWidth(table.Rows, currentProp) + "px" });
    }

    columnsAsObjects.push(
        {
            type: "control",
            modeSwitchButton: false,
            editButton: false,
            deleteButton: false,
        }
    );

    return columnsAsObjects;
};

var getType = function (rows, columnName, columnsWithDate) {
    if (rows && rows.length > 0) {
        if (isDate(rows[0], columnName, columnsWithDate)) {
            return "date";
        }

        for (var i = 0; i < rows.length; i++) {
            if (rows[i][columnName] !== null && !isNotANumber(rows[i][columnName])) {
                return "number";
            }
        }
    }

    return "text";
};

var getColumnWidth = function (rows, columnName) {
    var maxLength = columnName.toString().length;
    for (var i = 0; i < rows.length; i++) {
        if (rows[i][columnName] != null) {
            maxLength = rows[i][columnName].toString().length > maxLength ? rows[i][columnName].toString().length : maxLength;
        }
    }

    return maxLength * 10;
};

var isDate = function (row, columnName, columnsWithDate) {
    if (columnsWithDate && columnName) {
        var props = Object.keys(row);

        for (var j = 0; j < columnsWithDate.length; j++) {
            for (var k = 0; k < props.length; k++) {
                if (props[k] == columnName && columnsWithDate[j] == k) {
                    return true;
                }
            }
        }
    }

    return false;
};

var isNotANumber = function (value) {
    return value != null && value != "" && typeof value != "number" && isNaN(parseInt(value));
};

return {
    CreateNewJSGridForEachTable: createNewJSGridForEachTable
}

})();
'''

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fredericoregateiro picture fredericoregateiro  路  3Comments

venkatesh333 picture venkatesh333  路  4Comments

RevealedFrom picture RevealedFrom  路  3Comments

danielson317 picture danielson317  路  3Comments

eliasuardi picture eliasuardi  路  3Comments