Efficient way to measure the longest cell in a table column

I am trying to determine the widest content in a fixed width HTML table cell. I am currently using an external sizer DIV:

<div id="xdivSizer" style="position:absolute;display:inline;visibility:hidden"> </div> 

in this code

 var iColWidth = 0; for (var Y = 0; Y < oDataTable.rows.length; Y++) { oDivSizer.innerHTML = oDataTable.rows[Y].cells[0].innerHTML; iColWidth = Math.max(oDivSizer.offsetWidth, iColWidth) } 

It works, but extremely slow, even if the table has only 100 rows or so. It looks like the main culprit is calculating offsetWidth . Is there a more efficient way to achieve this?

I do this because I need to resize this column of the table to the size of the widest data. If there is another better way, that would be great.

(More precisely, I'm trying to implement the "autostart columns" function in the Infragistics WebHierarchicalDataGrid control for ASP.NET - the columns should accept the widest Max(Header Width, Data Width) widths while maintaining fixed header / pager positions).

Thanks!

+4
source share
2 answers

Rewriting DIV contents will cause multiple redraws, your CSS applications, DOM manipulations, etc.

Try putting all the cells from a column in a DIV all at the same time , separated by line breaks, or each in its own sub-DIV. The width of the resulting DIV should be the longest.

 var html = []; for (var Y = 0; Y < oDataTable.rows.length; Y++) { html.push(oDataTable.rows[Y].cells[0].innerHTML); } oDivSizer.innerHTML = html.join("<br />"); var iColWidth = Math.max(oDivSizer.offsetWidth, 1); 

Another possibility is a simple heuristic based on each inner cell text.

 var iColWidth = 0; for (var Y = 0; Y < oDataTable.rows.length; Y++) { var w = Math.floor(oDataTable.rows[Y].cells[0].innerText.length * 11.5); iColWidth = Math.max(w, iColWidth); } 

And another possibility, as indicated in my last comment, is to redraw only the TD in the column that consumes the largest area.

 var a = 0; var n = null; for (var Y = 0; Y < oDataTable.rows.length; Y++) { var _a = oDataTable.rows[Y].cells[0].offsetHeight * oDataTable.rows[Y].cells[0].offsetWidth; if (Number(_a) > a) { a = _a; n = oDataTable.rows[Y].cells[0]; } } oDivSizer.innerHTML = n.innerHTML; var iColWidth = Math.max(oDivSizer.offsetWidth, 1); 

Or, taking another step, divide the calculated area of ​​each cell by the number of found BSs to account for “intentionally” high cells.

 var a = 0; var n = null; for (var Y = 0; Y < oDataTable.rows.length; Y++) { var _a = oDataTable.rows[Y].cells[0].offsetHeight * oDataTable.rows[Y].cells[0].offsetWidth; var brs = oDataTable.rows[Y].cells[0].getElementsByTagName('br'); _a = Number(_a) / brs.length; if (_a > a) { a = _a; n = oDataTable.rows[Y].cells[0]; } } oDivSizer.innerHTML = n.innerHTML; var iColWidth = Math.max(oDivSizer.offsetWidth, 1); 

But, as the complexity of fuzzy approaches grows and the result becomes more accurate, you can expect a decrease in performance.

As a side note, if this automatic selection is applied as a result of user interaction, I would suggest performing calculations / calibration processes immediately after the table was available for analysis, rather than waiting for the user to start resizing.

+2
source

By default, WebDataGrid will change the content area of ​​the grid to its contents if there is no width in the column and the grid itself. Then the headers get the size for the piece of data. This will work well if the mesh is in the container or outside the container.

Since some of the data in the grid is the correct size, all we need to do is size the columns, which is larger, the width of the first cell in the grid, or a dimension to display the full header. The following JavaScript will do the following:

 function resizeGrid(grid) { var textWidthChecker = document.getElementById('textWidthChecker'); var columns = grid.get_columns(); var widths = new Array(columns.get_length()); var row = grid.get_rows().get_row(0); var newGridWidth = 0; for (var colIdx = 0; colIdx < columns.get_length(); colIdx++) { var col = columns.get_column(colIdx); textWidthChecker.innerHTML = col.get_headerText(); var cellElement = row.get_cell(colIdx)._element; if (!col.get_hidden()) { if (textWidthChecker.offsetWidth > cellElement.offsetWidth) { // 8 for padding in header. newGridWidth += textWidthChecker.offsetWidth + 8; widths[colIdx] = textWidthChecker.offsetWidth + 8; } else { widths[colIdx] = cellElement.offsetWidth; newGridWidth += cellElement.offsetWidth; } } } newGridWidth += columns.get_length() * 16; // added for padding on cells var gridElement = grid.get_element(); gridElement.style.width = newGridWidth + 'px'; for (var colIdx = 0; colIdx < columns.get_length(); colIdx++) { var col = columns.get_column(colIdx); col.set_width(widths[colIdx] + 'px'); } } 

The above requires a range with id "textWidthChecker" for this to work using the same CSS classes as the mesh heading for the font:

 <div style="height: 0px;overflow: hidden;" class="igg_HeaderCaption ig_Control"> <span id="textWidthChecker"></span> </div> 

This approach works well when the resizeGrid function is called using the client-side initialization method if the grid is inside a container that is wide enough for the grid to resize headers.

If the grid is in a smaller container, this will not be done because WebDataGrid sets the internal width, and this affects the size of the columns. To get around this, the resizeGrid function must be called earlier, although there is no event for this.

In particular, resizeGrid must be called during grid function initialization between calls to _initializeObjects and _adjustGridLayout. To do this, you can modify the WebDataGrid prototype to replace _adjustGridLayout with another one that calls resizeGrid and calls the original _adjustGridLayout:

 var adjustGridLayoutFunction = $IG.WebDataGrid.prototype._adjustGridLayout; $IG.WebDataGrid.prototype._originalAdjustGridLayout = adjustGridLayoutFunction; $IG.WebDataGrid.prototype._adjustGridLayout = function () { resizeGrid(this); this._originalAdjustGridLayout(); }; 

Note that changing the prototype affects every grid on the page, so if you want to control which grid is applicable to you, you will need to add conditional logic to the new _adjustGridLayout function. For instance:

 if (this.get_id() == "WebDataGrid1") resizeGrid(this); 

To prevent text wrapping inside the grid, you also need to use css for ItemStyle:

 .noWrapCellStyle { white-space: nowrap; } 

To do this, specify the desired width on the mesh container, and the mesh width should remain empty:

 <div style="width:300px;overflow: auto;"> <ig:WebDataGrid ID="WebDataGrid1" runat="server" Height="450px" AutoGenerateColumns="True" ItemCssClass="noWrapCellStyle"> </ig:WebDataGrid> </div> 

If you use a row selector, then the calculations should be updated in the logic that I provide for the row selector account.

0
source

All Articles