Any way to synchronize table column widths with HTML + CSS? - html

I have a number of tables with the same columns and it would look a lot nicer if they shared the same column widths. Is such a thing possible? Putting them in the same table with some rows with no borders between them isn't an option.
Edit: Yeah I'm aware I can fix the widths myself but I was hoping for something that would tie in to the browser's column width algorithm but simply tied two or more tables together for the purpose of doing that layout.
I didn't think such a thing was possible but I thought I'd check just in case.

If you're not too picky about which column widths the browser comes up with, as long as they're the same across different tables, you can use the CSS table-layout property (supported by all major browsers) in combination with a table width:
table {
table-layout: fixed;
width: 100%;
}
This causes all columns (without a specified width) to have the same width, regardless of the table content.

It's only possible if you can fix-width the columns. If you can set a fixed width then some css like this should work:
td {
width: 25%;
}
You can customize each columns width like this:
<table>
<tr>
<td class="col1">...</td>
<td class="col2">...</td>
</tr>
</table>
...
<table>
<tr>
<td class="col1">...</td>
<td class="col2">...</td>
</tr>
</table>
and then specify the widths like this:
.col1 {
width: 25%;
}
.col2 {
width: 75%;
}

Here's a small JavaScript I made to resize cells to make them equal width in all tables on a page.
function resizeTables()
{
var tableArr = document.getElementsByTagName('table');
var cellWidths = new Array();
// get widest
for(i = 0; i < tableArr.length; i++)
{
for(j = 0; j < tableArr[i].rows[0].cells.length; j++)
{
var cell = tableArr[i].rows[0].cells[j];
if(!cellWidths[j] || cellWidths[j] < cell.clientWidth)
cellWidths[j] = cell.clientWidth;
}
}
// set all columns to the widest width found
for(i = 0; i < tableArr.length; i++)
{
for(j = 0; j < tableArr[i].rows[0].cells.length; j++)
{
tableArr[i].rows[0].cells[j].style.width = cellWidths[j]+'px';
}
}
}
window.onload = resizeTables;

To expand on Ken's answer, you can also specify the exact widths in pixels:
td { width: 250px }
or ems (width of the letter m):
td { width: 32em }
or ex or pt or whatever (well...actually %, pt, px, em, ex might be it). If you need your columns to be different widths, then the easy way is to give the table cells classes:
<table><tr>
<td class="col1">...</td><td class="col2">...</td>...
</tr></table>
and assign column widths to the classes:
td.col1 { width: 48em }
td.col2 { width: 200px }
...
It should be sufficient to assign column widths to the first row in each table. [edit: looks like I've been scooped on that while I was writing]
You could probably also go crazy with the CSS 2 sibling selector, and write something like
tr > td:first-child { width:48em } /* first column */
tr > td:first-child + td { width: 200px } /* second column */
tr > td:first-child + td + td { width: 5% } /* third column */
...
but if you have more than a few columns, that could get ugly. And if you're using some sort of template system or script to generate these tables, I'm sure it'll be easier/clearer to just put the class="col#" attribute on each cell in your template once.

I'm almost shocked that no one has suggested column groups! With it you can give a column a specific class, width, and other helpful properties. And since it's HTML 4.01 it's supported by all browsers that support the doctype.

Luis Siquot answer is the one I used. However instead of using clientWidth, you should use jquery width() function to normalize widths between browsers, and to not calculate padding. Using clientWidth would result in the table cells expanding on ajaxpostbacks because of the padding (if padding used in the TD's).
So, correct code using Luis Siquot's answer would be to replace
var cell = $(this)[0].rows[0].cells[j];
if(!cellWidths[j] || cellWidths[j] < cell.clientWidth) cellWidths[j] = cell.clientWidth;
with
var cell = $($(this)[0].rows[0].cells[j]);
if (!cellWidths[j] || cellWidths[j] < cell.width()) cellWidths[j] = cell.width();

The easiest way is kind of a 'dirty' way, but it works the best.
It does exactly what's required:
Just merge your two tables into one table.
In my case the only thing between the two tables was an h3
So my table
<table>
<tr></tr>
<table>
<h3>Title<h3>
<table>
<tr></tr>
<table>
became this:
<table>
<tr></tr>
<tr><td colspan="6">
<h3>Title<h3>
</td></tr>
<tr></tr>
<table>
this way your table will 'sync' it's size up.
of course this only works when there isn't too much complex stuff in between the two tables, but I'm guessing in most cases it isn't. if it was, the sync wouldn't be needed in the first place.

each pair of tables resize its columns to the same width
similar to Ole J. Helgesen but with jquery and a parameter in order to select which tables equalize.
(I cant vote but it's essentially your solution)
<table data-ss="1" border="1">
<tr><td>asdf<td>129292<td>text
</table>
<table data-ss="1" border=1>
<tr><td>a<td>1<td>each column here has the same size than the table above
</table>
<table data-ss="2" border=1>
<tr><td>asdf<td>129292<td>text
</table>
<table data-ss="2" border=1>
<tr><td>each column here has the same size than the table above<td>a<td>1
</table>
and use this sctipt
$(function(){
resizeTables('1');
resizeTables('2');
});
//please set table html attribute `data-ss="something"` to properly call this js
// ss is short for SharedSize
function resizeTables(sharedSize){
var tableArr = $('table[data-ss='+sharedSize+']');
var cellWidths = new Array();
$(tableArr).each(function() {
for(j = 0; j < $(this)[0].rows[0].cells.length; j++){
var cell = $(this)[0].rows[0].cells[j];
if(!cellWidths[j] || cellWidths[j] < cell.clientWidth) cellWidths[j] = cell.clientWidth;
}
});
$(tableArr).each(function() {
for(j = 0; j < $(this)[0].rows[0].cells.length; j++){
$(this)[0].rows[0].cells[j].style.width = cellWidths[j]+'px';
}
});
}

You can sync the column widths by combining the tables (as suggested by #Stefanvds), but using a tbody + th for each:
table {
border-collapse: collapse;
}
table thead,
table tbody {
border-bottom: solid;
}
table tbody th {
text-align: left;
}
<table>
<thead>
<tr> <th> ID <th> Measurement <th> Average <th> Maximum
<tbody>
<tr> <td> <th scope=rowgroup> Cats <td> <td>
<tr> <td> 93 <th scope=row> Legs <td> 3.5 <td> 4
<tr> <td> 10 <th scope=row> Tails <td> 1 <td> 1
<tbody>
<tr> <td> <th scope=rowgroup> English speakers <td> <td>
<tr> <td> 32 <th scope=row> Legs <td> 2.67 <td> 4
<tr> <td> 35 <th scope=row> Tails <td> 0.33 <td> 1
</table>
Source: Example in the HTML spec itself

Related

Odd table header layout when header and body are generated from a razor loop

I am laying out a table headers and body in razor loops creating text boxes 65px x 65px for each user in the user database. This works. I have the hours of the day down the y axis and the boxes for the users are laid out end to end along the x access just how I want. however the headers which are the usernames start to the right of the last text box cell instead of over the top. I cannot fathom what is going on except inspecting the header in devtools the first user or header cell looks like it's left margin is the length of the table and sure enough the computed css shows it's width some 1045px yet I don't know where that is coming from. If I explicitly set the width of the th's to say, 65px nothing happens!
Also if I add a Id10t before the header loop it joins the others at the end of body columns.
Here is the html
<table id="DayTable" hidden style="position:absolute">
<thead>
<tr>
<th>Hours</th>
#{ foreach (var item in Model.Users)
{
<th>#item.UserName</th>
}
}
</tr>
</thead>
<tbody id="DayTbody">
#{ for (int n = 0; n != 24; n++)
{
<tr>
<td id="Hours" style="display:block;border:none;position:absolute;margin-left:4px;margin-top:21px;border-radius:3px">#DateTime.Now.AddHours(-DateTime.Now.Hour + n).ToString("hh:00:tt")</td>
<td nowrap style="border:none"> <input type="text" class="evt" id="Events" style="display:inline;height:65px;width:65px;margin-left:85px"></td>
#{ foreach (var item in Model.Users)
{
<td nowrap style="border:none"> <input type="text" class="evt" id="Events" style="display:inline;height:65px;width:65px;margin-left:-21px"></td>
}
}
</tr>
}
}
</tbody>
</table>
Tried to add a couple screen snips but.....
[It looks like this to the extreme right of the x-scroll.][1]
[1]: https://i.stack.imgur.com/iIWLX.png
[This is the normal unscrolled view][2]
[2]: https://i.stack.imgur.com/AgAYY.png

HTML's Table TD with default value

Is there any way to fill the empty <td></td>, if data does not exist in this?
<table><tr><td></td><td>1</td><td></td></tr></table>
Change it to:
<table><tr><td>Not Data</td><td>1</td><td>No Data</td></tr></table>
Not with HTML itself.
Usually you would do this by generating the HTML using a data in a programming language and a template, with template logic used to insert a default value if none came from the data.
A little bit hackish, because it changes only visual aspect. You can do that with CSS:
table, td {border: 1px solid black; padding: 4px; border-collapse: collapse}
td:empty::before {content: "No Data"}
<table>
<tr>
<td></td>
<td>Real data</td>
<td></td>
</tr>
</table>
Won't really work just with html.
it depends on how you build your HTML-DOM. If it is just a pure HTML-file (*.html ) you could check for value by adding some JavaScrit/JQuery.
But if you are trying to do this, make sure to give your table/tr or td to give element-ids.
<table>
<tr id="1">
<td id="1_1"></td>
<td id="1_2">1</td>
<td id="1_3"></td>
</tr>
</table>
<script type="text">
/* For the amount of rows */
for(var outeri = 1; outeri <= 1; outeri++)
{
/* For the amount of columns */
for(var ineri = 1; ineri <= 3; ineri)
{
var innertd = String(outeri + '_' + ineri);
if(document.getElementById(inntertd).innerHTML == '')
{
document.getElementById(inntertd).innerHTML = 'No Data';
}
else {continue;}
}
}
</script>
This example is not very realistic and more likely not to be done unless your table is just static.
you can add an empty space:
This can be achieved in many ways, here are 2 (c# and jquery)
Solutions:
c#:
Use razor as in the following:
<table>
<thead>
<tr>
<td>Name</td>
<td>Date</td>
</tr>
</thead>
<tbody>
<tr>
#{
if(data)
{
<td>#data.name</td>
<td>#data.date</td>
}
else
{
<td colspan="2">NO DATA</td>
}
}
</tr>
</tbody>
</table>
jQuery:
Select the table and change the html depending on if there is any data
$(document).ready(function()
{
var table = $("table-selector");
var isData = $("data-selector").length;
if(!isData){
table.html("customize HTML right here, considering 'colspan' in case
there is a headers row as well")
}
});

How to contain the header within table width when the fields are dynamic

I have a table with few rows and recurring header. The number of columns is not fixed and can vary. The header is a single cell spanning across the whole table. When a column is taken away from the table the header width is not recalculated and exceeds the width of the table. How can I contain the header inside the table with as little code as possible. If possible I would prefer to not use JS.
I have recreated the problem here in JSFiddle.
<table>
<colgroup>
<col style="width:10px">
<col style="width:20px">
<col style="width:30px">
</colgroup>
<tr>
<th colspan=100>
numbers
<tr>
<td>1<td>2<td class="hide">3</td>
<tr>
<td>1<td>2<td class="hide">3</td>
<tr>
<th colspan=100>
numbers
<tr>
<td>4<td>5<td class="hide">6</td>
</table>
td{
border: 1px solid grey;
}
table {
border: 1px solid black;
border-collapse:collapse;
table-layout: fixed;
}
els = document.getElementsByClassName("hide")
for(var i=0;i<els.length;i++)
els[i].style.display = "none";
Edited
I am not using JQuery(I used it for shorter example). The columns are concealed with JS(on click a column disappears). The example there is as close to my problem as it gets. The headers are repeated every ~100 rows (in fact there is <tr><th>Index:</th><th>Item:</th></tr>.
http://jsfiddle.net/arhws/6/
<table>
<caption>
Numbers
</caption>
[...]
Try resetting the colspan based on the visible cells:
$(".hide").hide();
var rowlength = $( "tr:nth-child(2) td" ).length;
var hidelength = $( "tr:nth-child(2) td.hide" ).length;
$("th").attr("colspan",rowlength-hidelength);
For anyone interested in how I did it in the end:
http://jsfiddle.net/7sXv4/14/
I have added the same class "hide" to the col so that when the column is hidden the col tag is not displayed anymore.
I made the colspan on tr the exact maximum number of columns.
I have added a width to the table of the first column (minimal width possible)
BTW I know the fiddle does not work in Chrome but for some reason in my application it works both in Chrome and FF.

HTML table like `ls`

Is it possible to create an HTML/CSS Table that is formatted like the POSIX ls? That is, entries are displayed alphabetically in columns, where the number of columns is dependent on the width of each column?
For example, see in this screenshot how the columns are only as wide as necessary. The fourth column is much wider than than the others, and the third column is narrowest of all as all its file names are short. There are four columns in this screenshot but there could be more if the terminal were wider and fewer if it were narrower.
The number of columns varies based on the width of the terminal and the exact file names in the listing. As more file names are shown, more columns are added. Once the right side of the terminal is reached, ls makes the columns taller. If there are lots of short file names there could be a lot of columns, whereas if the file names are long there would be fewer.
Well, the good-news is that browsers "sorta" support this with table elements and table-layout:auto rendering (this should be the default, and is after an appropriate CSS reset). The bad news is it's only "sorta".
For starters, you must pick the number of columns and render the HTML table such that the items are in the correct column - this includes all sorting! Then, if you don't over-constrain the width of any column in the table, make sure the table is table-layout:auto, and set the width of the table then the results should be similar to that of ls - that is, the columns will "automatically adjust width" based on the content.
To get an "identical" result to ls would require something more heavy-handed, such as using a fixed-font (like that used in a terminal) and manually calculating the column widths (like ls does), perhaps in a <pre> element. CSS/HTML has limitations that ls simply avoids by doing layout calculations itself against a fixed-width terminal.
This is a table as any other. Unless there is something else you didn't specify. You may do something like this
<table>
<colgroup style="width:22%"></colgroup>
<colgroup style="width:25%"></colgroup>
<colgroup style="width:25%"></colgroup>
<colgroup style="width:25%"></colgroup>
<colgroup style="width:3%"></colgroup>
<thead>
<tr>
<th>Closed Day</th>
<th><span>|</span>Is this a paid day?</th>
<th><span>|</span>Last updated date</th>
<th><span>|</span>Last updated by</th>
<th><span>|</span>Current?</th>
</tr>
</thead>
<tbody>
<tr>
<td>08/10/2013</td>
<td>No</td>
<td>02/12/2014</td>
<td>c-jsmith</td>
<td>Yes</td>
</tr>
<tr>
<td>09/01/2013</td>
<td>Yes</td>
<td>02/12/2014</td>
<td>c-kkearney</td>
<td>Yes</td>
</tr>
<tr>
<td>12/25/2013</td>
<td>Yes</td>
<td>01/12/2014</td>
<td>c-jjohnson</td>
<td>Yes</td>
</tr>
<tr>
<td>12/26/2013</td>
<td>Yes</td>
<td>02/01/2014</td>
<td>c-gthompson</td>
<td>Yes</td>
</tr>
</tbody>
Using a table similar to above and the script below with some modifications you should be able to limit the amount of rows and stack to the right with positioning.
<script>
function tableWrap() {
var maxRows = 10;
var table = document.getElementById('myTable');
var wrapper = table.parentNode;
var tableRows = table.rows.length;
var height = 0;
if (tableRows > maxRows) {
for (var i = 0; i < maxRows; i++) {
height += table.rows[i].Height;
}
wrapper.style.height = height + "px";
}
}
</script>
I managed to get something started with javascript:
var outer = document.getElementById("outer");
function setColumns(n) {
var style = outer.style;
style.columnCount = n;
style.MozColumnCount = n;
style.WebkitColumnCount = n;
}
var i = 1;
while (outer.scrollWidth <= outer.offsetWidth) {
setColumns(++i);
}
setColumns(i - 1);
http://jsfiddle.net/jEq4R/2/
It doesn't handle the width, yet, but it at least gives a good number of columns.
EDIT: works in firefox but not webkit
Adjust the width in this jsfiddle and see http://jsfiddle.net/sajith/DdqfA/
HTML
<div>column</div><div>column</div><div>column</div>
<div>column</div><div>column</div><div>column</div>
<div>column</div><div>column</div><div>column</div>
<div>column</div><div>column</div><div>column</div>
<div>column</div><div>column</div><div>column</div>
<div>column</div><div>column</div><div>column</div>
CSS
div {
width: 200px;
display: inline-block;
}

What is the cleanest way to render a collection into a html table with "n" number of entries per row

I have an asp.net-mvc website. In my viewmodel I have a collection of cars that I want to display on an HTML table.
I want to show 5 cars per row, so I started something like this:
<table>
#foreach (var car in Model.Cars) {
if (counter == 0) {
<tr>
}
<td>#car.Name</td>
counter++;
if (counter == 5) {
</tr>
}
}
</table>
The issues are I feel like the code above is a little hacky and also what if I have 7 cars, should I put 3 blank <td>s in the last row or cut it off (again even more hacky code)?
I wanted to see if there was a cleaner way in an asp.net-mvc to display a collection in a table with a specific number of entries in the table on a row.
If you want to stick to tables, this looks the least hacky to me
#{
var colSize = 5;
var rows = (int)(Model.Cars.Count() / colSize);
}
<table>
#for (var row = 0; row <= rows ; row++){
<tr>
#foreach(var car in Model.Cars.Skip(row * colSize).Take(colSize))
{
<td>#car.Name</td>
}
</tr>
}
</table>
if you want to fill out the last row with <td> elements, you can put this in front of the <tr>:
#if (row == rows)
{
for(var i=0;i<colSize -( Model.Cars.Count() % colSize); i++){
<td></td>
}
}
You could make this slightly cleaner with something along these lines:
<table>
<tr>
#foreach (var car in Model.Cars) {
<td>#car.Name</td>
<!-- If we've output a fifth cell, break to a new row -->
if (++counter % 5 == 4)
{
</tr><tr>
}
}
</tr>
</table>
However it would probably be better to instead output block-level elements with a fixed width and float them left, like this (excuse the inline styles ;-)):
<div style="width: 300px;">
#foreach (var car in Model.Cars) {
<div style="float: left; width: 55px;">#car.Name</div>
}
</div>