Change width of thead ,tbody ,td dynamically - javascript

I applied this property in .css file on this table and records are 9.
.fixed tbody td, thead th {
width: 5.2%;
float: left;
}
Like when I have 4 column width must be 23.2% , when we have 5 columns width must be 18.2%, when I have 6 columns width must be 16.2% , when 7 width must be 14.2%. when columns are 8 width must be 12.2% . and maybe more columns come so I dont know but how can I manage this width ? I saw questions but they show that width must decrease or increase sequential. So what is best solution now ?

So you want to set equal width to each columns and don't know how many columns there is.
The width should then be the table width divided by the column amount.
$(document).ready(function(){
// The table to work on.
var table = $("table.fixed")
// Get table width
var tableWidth = table.width();
// Get the collection of the td on the first row.
var firstRowTdCollection = table.find("tr").first().find("td");
// Get the column count.
var columnCount = firstRowTdCollection.length;
// Calculate the width based on table width divided by the column amount.
var widthToApply = tableWidth / columnCount;
// Apply the calculated width on the first row (Not necessary on the other rows)
firstRowTdCollection.each(function(){
$(this).width(widthToApply);
});
});

Related

How can I implement collapsing rows the way datatables.js does?

I'd like to implement the data-tables responsive behavior of hiding columns when the table's width decreases past a certain point (basically the point when the tr's element overflow would occur).
I know how I can collapse the rows: simply on window resize, check for when the width of the table is greater than the width of it's container #table-wrapper. When this happens, I hide the outer most table column and place it in a stack, while adding those values to each rows extended portion (that will be toggled to be visible, the way datatables does).
If the website is accessed while the window is in a small size, upon loading the table can check for the overflow condition (table.width > table-wrapper.width), and iterate through the outer most columns, hiding them and pushing them on to the stack until the overflow condition is false.
However, How can I bring the elements back? That is, when the table is growing, I'm not sure under what condition I can pop the columns off the stack and unhide them.
I'm thinking of getting the minimum-size of the table somehow, and upon window resize, check if the wrappers width is bigger than the minimum size of the table plus the minimum size of first item on the stack? However, I don't know how to get these minimum widths.
Is there a reliable way of getting these min.widths for any font size, or is there a better way you can recommend?
<table class="test">
<tbody>
<tr>
<th> heading1 </th>
<th> heading2 </th>
<th> heading3 </th>
<th> heading4 </th>
</tr>
<tr>
<td>data1</td>
<td>data2</td>
<td>data3</td>
<td>data4</td>
</tr>
<tr>
<td>data1</td>
<td>data2</td>
<td>data3</td>
<td>data4</td>
</tr>
</tbody>
</table>
*Update:
I thought a very unelegant solution where I could place an invisible copy of the table directly behind it, and upon the window resize event firing, I could continually add an invisible column to the invisible table and check for the overflow condition (table_border > wrapper_border). This seems really inefficient though...
I also learned that min-width for < td > elements are undefined. However, Its possible to place a or inside each element that does have a min-width. For each column, the minimum with is equal to the that has the maximum length. So, I guess I could check all the data for each column and get the minimum length that way. The min. width of the table is what I need to base whether or not adding another column would cause an overflow.
What do you think; do you have any other ideas?
One approach is to have data using white-space: nowrap elements inside the table cells and calculate the min width of each row which it will be the max width of the row data elements. These "break-widths" will be calculated only once when the table is filled with data (on load or re-new the data). Having these "break-widths", we can calculate the table minimum width and compare it with the body client width and hide columns accordingly on window resize.
Calculating the "break-widths"
This is performed on loading or each time table updates with new data (i.e. pagination page change). We go through table cell elements TH and TD and we're storing the biggest cell text width of the span elements.
function calcBreakWidths() {
// Each column one base index
let thIndex = 1;
for (let th of document.querySelectorAll('thead th')) {
// Get the width of every text span of every TH column,
// this way, we'll have also header break-widths
let breakWidth = document.querySelector(`thead th:nth-child(${thIndex}) > span`).offsetWidth;
// Go through all column TD elements and keep the biggest text span width
for (let span of document.querySelectorAll(`tbody td:nth-child(${thIndex}) > span`)) {
if (span.offsetWidth > breakWidth) {
breakWidth = span.offsetWidth;
}
}
// Save the biggest text span break-width to the TH dataset
th.dataset.breakwidth = breakWidth;
// Next column index
thIndex++;
}
}
Hide / Show columns on window resize
On window.resize we calculate the table minimum widths by summing up all break-widths. Then we go through each column, starting from the most right one, and we check if the table min width including the current column is exceeded the body client width; if it did, then we hide the current column else we show the current column. This is done by using classList.add and classList.remove using the class named hidden with display: none CSS style. At the end of each column iteration, we subtract the current column break-width from the table minimum width so to have the next correct table minimum width without the current column break-width.
window.addEventListener('resize', function() {
const bodyWidth = document.body.clientWidth;
// Get all columns break-widths from the TH element dataset
const breakWidths = [...document.querySelectorAll('th')]
.map(th => parseInt(th.dataset.breakwidth));
// Sum-up all column break-widths (+2 pixels) to calculate table minimum width
let tableMinWidth = breakWidths
.reduce((total, breakWidth) => total + breakWidth + 2, 0);
for (let column = breakWidths.length; column > 0; column--) {
const tableIsLarger = tableMinWidth > bodyWidth;
// const th = document.querySelector(`th:nth-child(${column})`);
const cells = document.querySelectorAll(`th:nth-child(${column}), td:nth-child(${column})`);
// If table min width is larger than body client width,
// then hide the current column
if (tableMinWidth > bodyWidth) {
// We're hidding the column by iterating on each table cell
// and add the hidden class only if the column does not already contain
// the hidden class. We're doing this for performance reasons
if (!cells[0].classList.contains('hidden')) {
cells.forEach(cell => cell.classList.add('hidden'));
}
// Else if the table min width is not larger than body client width,
// we remove the hidden class from the column to show each column cell
} else if (cells[0].classList.contains('hidden')) {
cells.forEach(cell => cell.classList.remove('hidden'));
}
// Subtract current column break-width from the total table min width
// so to have the correct min table width for the next column
tableMinWidth -= breakWidths[column - 1] + 2;
}
});
The snippet shows this in action
Please read inline comments
// Each TH class is a field name
const fields = [...document.querySelectorAll('thead th')].map(el => el.className);
// Generate 20 rows with fake data
for (let i = 0; i <= 20; i++) {
const tr = document.createElement('tr');
fields.forEach(field => {
const td = document.createElement('td');
const text = document.createElement('span');
td.className = field;
text.textContent = fake(field);
td.appendChild(text);
tr.appendChild(td);
});
document.querySelector('table tbody').appendChild(tr);
}
// Calculate each column break width, the max data span element width
function calcBreakWidths() {
let thIndex = 1;
for (let th of document.querySelectorAll('thead th')) {
let breakWidth = document.querySelector(`thead th:nth-child(${thIndex}) > span`).offsetWidth;
for (let span of document.querySelectorAll(`tbody td:nth-child(${thIndex}) > span`)) {
if (span.offsetWidth > breakWidth) {
breakWidth = span.offsetWidth;
}
}
th.dataset.breakwidth = breakWidth;
thIndex++;
}
}
calcBreakWidths();
// Handle window resize and hide each column exceeds BODY client width
window.addEventListener('resize', function() {
const bodyWidth = document.body.clientWidth;
// Get the break widths saved to the TH datasets
const breakWidths = [...document.querySelectorAll('th')]
.map(th => parseInt(th.dataset.breakwidth));
// Calculate table min width (+2 pixels for border + padding for each cell)
let tableMinWidth = breakWidths
.reduce((total, breakWidth) => total + breakWidth + 2, 0);
// Loop from last to the first column and compare the widths
for (let column = breakWidths.length; column > 0; column--) {
const cells = document.querySelectorAll(`th:nth-child(${column}), td:nth-child(${column})`);
if (tableMinWidth > bodyWidth) {
if (!cells[0].classList.contains('hidden')) {
cells.forEach(cell => cell.classList.add('hidden'));
}
} else if (cells[0].classList.contains('hidden')) {
cells.forEach(cell => cell.classList.remove('hidden'));
}
tableMinWidth -= breakWidths[column - 1] + 2;
}
});
// Function to create fake data
function fake(what) {
switch (what) {
case 'name': return faker.name.findName();
case 'email': return faker.internet.exampleEmail();
case 'address': return faker.address.streetAddress();
case 'country': return faker.address.country();
case 'account': return faker.finance.accountName();
}
}
table {
border-collapse: collapse;
width: 100%;
}
table, th, td {
border: 1px solid black;
}
tbody td > span,
thead th > span {
white-space: nowrap;
background-color: gold;
}
thead th > span {
background-color: aquamarine;
}
.hidden {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/faker.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Faker/3.1.0/locales/en/faker.en.min.js" crossorigin="anonymous"></script>
<table>
<thead>
<tr>
<th class="name"><span>Name<span></th>
<th class="email"><span>Email</span></th>
<th class="account"><span>Personal or Business Account</span></th>
<th class="address"><span>Personal or Business Address</span></th>
<th class="country"><span>Country</span></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
You can run the above code snippet and try to resize the window, but you'll have a better view of the table responsive behavior if you try to resize the browser after opening the snippet in Full page or by navigating to the full example page here: https://zikro.gr/dbg/so/62491859/responsive-table.html
It's not perfect but it runs smoothly on my browser and it hides columns from right to left based on columns data text width.
Could you give an example of the desired behavior? If you're talking about this datatables.js behavior: https://www.datatables.net/examples/styling/semanticui.html, then the simple answer would be this CSS Rule https://www.w3schools.com/cssref/css3_pr_mediaquery.asp
#media only screen and (max-width: 600px) {
table td:nth-child(5) {
display: none;
visibility:hidden;
}
}
#media only screen and (max-width: 700px) {
table td:nth-child(6) {
display: none;
visibility:hidden;
}
}
(you get the idea)

How to autoFit the first column in a Kendo TreeList ONLY if columnwidth < maxInt?

I have a Kendo TreeList and want to autoFit the first column onDatabound and onExpand. But if the column is going to be brighter than maxInt, it should not fit (and stay the same width).
So I want something like
var width = /*treelist.columns[0].width*/;
var max = /*some int*/;
if(width < max){
treelist.autoFitColumn(0);
}
But I didn't find a simple getWidth() method
Furthermore, it would be nice if there is way to set the width manually (in order to set the max width onDatabound).
This will get the width of the first column:
dataBound:function(e){
let column = $('td').first()
let columnWidth = column[0].clientWidth

How to set the width of each header cell in a table (with fixed header) to be the same to the biggest in its column, and make the browser respect it?

Here is the infamous table:
My boss wants me, at this time of the year, to make the header of the table fixed, so the user can scroll down the table and continue reading the header. I want to preserve the original precomputed dimensions of the table, I mean, the width that every column has at the moment of its creation (widths aren't established by CSS) and then, adapt the header so its columns match the columns of the body. Following some of the answers I found in Stackoverflow, I started making the header and the body of the table display: block. And after that, I wrote this:
function setTableHeadDimensions() {
var $taskTable = $('.tablaTareas_PEMVISUALIZA'),
$firstRow = $taskTable.find('tbody > tr:first-child'),
$firstRowTds = $firstRow.find('td'),
$firstRowHead = $taskTable.find('thead > tr:first-child'),
$secondRowHead = $taskTable.find('thead > tr:eq(1)'),
$firstRowHeadThs = $firstRowHead.find('th'),
$secondRowHeadThs = $secondRowHead.find('th'),
i = 0,
cells = [];
//We prepare CSS, so we can specify width.
$taskTable
.css('table-layout', 'fixed')
.find('td, th').each(function () {
var $tdh = $(this);
$tdh.css('box-sizing', 'border-box');
$tdh.css('overflow', 'hidden');
});
//Cells of the first row of the table head.
for (i = 0; i < 3; i++) {
cells.push($($firstRowHeadThs[i]));
}
//Cells of the second row of the table head.
for (i = 0; i < $secondRowHeadThs.length; i++) {
cells.push($($secondRowHeadThs[i]));
}
//Rest of the cells for the first row.
for (i = 5; i < $firstRowHeadThs.length; i++) {
cells.push($($firstRowHeadThs[i]));
}
//Try to set the width of the current column's header cell
//to the biggest cell width in the column.
for (i = 0; i < cells.length; i++) {
var maxWidth = 0;
$taskTable.find('td:nth-child(' + (i + 1) + ')').each(function () {
var $el = $(this);
maxWidth = Math.max(maxWidth, $el.width());
});
cells[i].width(maxWidth);
}
}
But, as you can see in the picture, the browser doesn't want to cooperate. What's more, it establishes the width of the cell, but to a number that doesn't match the width of it's corresponding column:
What's more, it doesn't match the width of the row it should match:
So I have two questions:
Why does the browser behave in the way it does?
How can I solve this problem in a way compatible with IE8? (no fancy CSS3 solution, please)
Here's a codepen with the example cut down to the minimum necessary: Codepen example
I solved it. In reality, there were two problems:
The first one is that jQuery.width() returns only the width of the content of the cell, without padding and margin (even if you specify border-sizing: border-box). I found more natural to use jQuery.css('width') and then take borders and padding into account in my calculations, without specifying border-sizing: border-box because retrieving the width with border-sizing: border-box and then setting it in another element with the idea of matching both widths can be error prone (I had problems with it).
The second one is if you use rowspan in the header of the table. In that case, you have to establish the width of the rows envolved doing the proper calculations, not only one of them hopping that the rest of the rows will adapt.
Here's the codepen with the solution: http://codepen.io/PolarKuma/pen/BQXMbO

Why table height is bigger than the sum of cells heigh?

I don't know if my question is simple or naive, but I am still learning javascript, so be lenient with me.
One of my exercise was to create a DIV element with a specific width and height and then using javascript I tried to generate table inside this DIV. I thought that this is a simple problem, just take parent div width and height and divide it by the number of columns and rows. What I found is that in horizontal direction this works fine, but in vertical direction table height is bigger than the sum of cells heigh.
Let say that div height is 407px. This means that cell height (assuming 15 rows) should be 27,13px. The total height of the table should be 15*27,13=407px. But table.clientWidth gives me 420px in chrome and 453px in IE10.
I thought about borders which may be taken into account, but why then I get right width? I tried to use getComputedStyle() method instead element.clientWidth but the results were the same. Then I have read about logical vs physical pixels, but this was not helpful. I don't understand why this code behaves in this way? What I am missing?
My HTML
<div id="container">
<div id="target"></div>
</div>
My JavaScript
var createGrid = function(rows, cols, appendToID){
var appendTo = document.getElementById(appendToID);
var appendToWidth = appendTo.clientWidth;
var appendToHeight = appendTo.clientHeight;
var cellWidth = (appendToWidth-16)/cols;
var cellHeight = appendToHeight/rows;
var table = document.createElement('table');
for(var i=1; i<=rows; i++){
var row = document.createElement('tr');
for(var j=1; j<=cols; j++){
var cell = document.createElement('td');
cell.style.background = 'white';
cell.style.padding = '0px';
cell.style.margin = '0px';
cell.style.border = "1px solid blue";
cell.style.width = cellWidth+"px";
cell.style.height = cellHeight+"px";
row.appendChild(cell);
console.log("all ok");
}
table.appendChild(row);
}
table.style.borderCollapse = "collapse";
table.style.border = '1px';
appendTo.appendChild(table);
}
createGrid(15, 11, "target");
JSFiddle example
The real height of the table is: 21px * 20 cells + 21px(the borders) =441px. You have the value 21px for the borders because you added border-collapse: collapse; for the table. Without this, the height is 502px.
put the height to the row instead of the cell :
row.style.height = cellHeight+"px";
https://jsfiddle.net/4h83tu2j/17/
This is indeed related to the border adding to the size of your divs. I suggest you take a look at the css-property box-sizing, most particularly its border-box value. This adjusts the size related to the border you have set on it.
Adding it to your cell like so: cell.style['box-sizing'] = 'border-box'; and you will see a totally different result. The height of the cell should now take into account the border you have set on it. And thus make the cells fit the total height of your table.
You can find more information related to the border and the box-model here.

Setting TH min and max width extremely slow

Given a fixed-header HTML table, I'm trying to line up the header columns with the body row columns. I'm doing this because the CSS I'm using to make the headers fixed results in the headers being out of alignment with the rest of the body.
The javascript I'm using works, but is extremely slow. Any ideas on how I can speed this up?
Here's the fiddle showing the problem. Right now it's taking about 5+ seconds for a relatively small table.
http://jsfiddle.net/w93NU/
Here is the code I'm using:
function fixedHeader($table) {
//This function compares the header row with the first body row and lines up all of the widths
var firstRowTds = $table.children("tbody:first").children("tr:first").children("td");
var headerRowThs = $table.find("th");
for (var i = 0; i < firstRowTds.length; i++) {
var head = headerRowThs[i];
var cell = firstRowTds[i];
var width = (Math.max($(cell).outerWidth(), $(head).outerWidth())) + "px";
//Here are my problem pieces. Setting these values are what kills the perfomanrce
$(cell).css({
"min-width": width,
"max-width": width
});
$(head).css({
"min-width": width,
"max-width": width
});
}
}
Ok, after much trial and error, if you comment out the last item in the style sheet, then it is fast. I don't know why.
Updated fiddle here
/* fixed width for THs */
/* the tbody needs to be 16px less than the thead, for the scrollbar */
/*#readiness-grid tbody td {
width: 242px;
}*/
// changing:
var firstRowTds = $table.children("tbody:first").children("tr:first").children("td");
var headerRowThs = $table.find("th");
// to:
var firstRowTds = $table.children("tbody:first > tr:first > td");
var headerRowThs = $table.find("thead > th");
scopes the node lookup and cuts the time from ~800ms to ~2ms between the start/end times on your sample table.

Categories

Resources