Frozen table header inside scrollable div - html

I've three divs. Header, central and footer. There is a table in central div (gridview) which is almost always longer than outer div. So I've made this div scrollable vertically. The question is: how can I make table header that it would be visible after div is scrolled down? I could have done this header with separate div or table and make it fixed but widths of columns in the table are not always the same - so I don't know how to maintain widths of columns in header then. Any clue?

I've just put together a jQuery plugin that does exactly what you want. Its very small in size and really easy to implement.
All that is required is a table that has a thead and tbody.
You can wrap that table in a DIV with a classname and the table will always resize to fit in that div. so for example if your div scales with the browser window so will the table. The header will be fixed when scrolling. The footer will be fixed (if you enable a footer). You also have the option to clone the header in the footer and have it fixed. Also if you make your browser window too small and all columns can't fit...it will also scroll horizontally (header too).
you just pass the DIV's classname to the plugin like so: $('.myDiv').fixedHeaderTable({footer: true, footerId: 'myFooterId'});
and the plugin will do the rest. FooterID is a element on the page that contains the mark-up for your footer. this is used if you want to have pagination as your footer.
If you have multiple tables on the page it will also work for each table you want to have a fixed header.
check it out here: http://fixedheadertable.mmalek.com/
Keep in mind its still 'beta' so I am adding new features and bug fixes daily.
Supported browsers: IE6, IE7, IE8, FireFox, Safari, and Chrome

Solution is really simple. You need 3 DIVs: a general container (in my case of class "outer"), a table container (in my case of class "inner") and a DIV in which you make a clone of an existing table using jQuery or javaScript (in my case of class "header").
Solution uses CSS and a few lines of jQuery code, which clones HTML of "inner" into "header" and sets its width and height. Supports fixed and variable columns width. Tested with IE8, Firefox 9, Safari and Google Chrome.
Here is a sample code:
$(document).ready(function() {
$('.header').html( $('.inner').html() );
$('.header').css('width', $('.inner table').outerWidth() );
$('.header').css('height', $('.inner table thead').outerHeight() );
});
table {
width:100%;
}
th {
border-top:1px solid #999;
text-align:left;
}
td, th {
border-bottom:1px solid #999;
background-color:#EEE;
}
.outer {
position:relative;
width:500px;
}
.inner {
height:150px;
overflow:auto;
}
.header {
position:absolute;
top:0;
left:0;
overflow:hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="outer">
<div class="inner">
<table cellspacing="0" cellpadding="0">
<thead>
<tr>
<th>a</th>
<th>b</th>
<th>c</th>
<th>d</th>
</tr>
</thead>
<tbody>
<tr><td>1</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>2</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>3</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>4</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>5</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>6</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>7</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>8</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>9</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>10</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>11</td><td>b</td><td>c</td><td>d</td></tr>
<tr><td>12</td><td>b</td><td>c</td><td>d</td></tr>
</tbody>
</table>
</div>
<div class="header">
</div>
</div>
</body>

Here is a basic solution using javascript:
function position(table) {
table.rows[0].style.position="absolute";
table.rows[0].style.top="0px";
table.style.marginTop = table.rows[0].clientHeight/1.2;
var widths = Array();
for(var i = 0; i < table.rows[0].cells.length; i++) {
widths[i] = max(table.rows[0].cells[i].clientWidth, table.rows[1].cells[i].clientWidth);
}
for(var row = 0; row < table.rows.length; row++) {
for(var col = 0; col < widths.length; col ++) {
table.rows[row].cells[col].style.width = widths[col] + "px";
}
}
}
function max(num1, num2) { return (num1 > num2) ? num1 : num2; }

You would have to put the header outside the scrollable div. Everything within the div will scroll.
EDIT
Regarding the width if you go for a separate header, I can see a few solutions:
Assuming this is dynamic content, generate "fixed" widths based on the length of the string. Obviously specify in terms of EMs and leave some margin for error.
Use javascript.
Use fixed width columns.
I haven't actually tried the first, and it might be overcomplicating things a bit. It's something to try if you're desperate for the effect though.
I should also mention that there are probably javascript libraries with table widgets that do this already. Have a look at them to see how they do it.

You need to put a table with the headers about your table of data. You can maintain the column width with table-layout:fixed. JavaScript can be used to match the column widths.

Here's a nice solution (mostly CSS) which uses fixed width columns: http://www.imaputz.com/cssStuff/bulletVersion.html
And here's a bit of jQuery code to fix cell-widths when cell contents take more width than the fixed width:
<script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"
type="text/javascript"></script>
<script>
$(function() {
$('div.tableContainer').each(function() { // for each table
var div = $(this);
var widths = [];
var changed = false;
$('table>*>tr', div).each(function(tr_i) {
$(this).children().each(function(i) {
var w = $(this).width();
if (w > widths[i]) {
widths[i] = w;
changed = true;
}
});
}).each(function(tr_i) {
if (changed)
$(this).children().each(function(i) {
var width = widths[i];
// add some width for scrollbar
if (tr_i == 0 && changed && i == widths.length-1) width += 16;
// insert a div to ensure width
$(this).append('<div style="width:'+width+'px; height:0px"> </div>');
});
});
div.width(div.children('table').width()).css('overflow-x', 'hidden');
});
});
</script>
The output is a bit off in IE when using a non-strict DTD. Tweak the widths in the CSS if you can't use standards mode.
Note that the jQuery code increases the table width at the end, and disables the horizontal scrollbar. You may or may not want that.

You may try the jQuery plugin Stupid Fixed Header. The technique is basically the same: clone a header and put it on top of the table layer.

What you actually want to be doing is making the <tbody> of the data table scrollable, so the <thead> and <tfoot> will remain naturally fixed.
Whilst this is trivial for FF et al:
tbody
{
height: 100px; /* or whatever */
overflow: auto;
overflow-y: auto;
overflow-x: hidden;
}
IE has severe and complex issues with tbody in general. It can be solved with expressions but it's non-trivial, and specific to the design.

I haven't tested this, but perhaps you could generate the table twice, once in the header and once in the scrolling div. Then in the header, make all the rows except the heading row invisible. Perhaps with 'display: none' or set their height to zero.

This solution works using CSS in Firefox and the other Gecko browsers, and CSS expressions in IE.
http://home.tampabay.rr.com/bmerkey/examples/nonscroll-table-header.html
The header and footer do not stay fixed in Opera or Safari/Chrome, but the whole table is scrollable so it is usable. Note that the columns are given percentage widths in the author's example, but they can be removed.
If you want to experiment with Opera/Safari/Chrome support, look at giving the tbody display:block and go from there.

Take a look at YUI Data Table if you are looking for a comprehensive cross-browser implementation. I believe this is done by creating another table with matching column widths.
There appears to be tricks required to fix column widths. If I recall correctly, Firefox requires <col/> tags to be present.
In any case, there were many tricks employed in YUI DataTable (50k lines). You'd be well advised to look at it and decide how far you'd like to go on your own with this.

Related

CSS Property (body's width) = HTML Tag Attribute (table's width)

How can I make this code of CSS work?
body {
width: $table.width();
}
I can't find a easy/simple way of doing this.
The reason is that I have a table and the width of that table is huge, but I don't wanna manually type xxxxx px, I want it to automatically change the width of the body to the same as that table. If I don't change the width, the headers of the table get more rows/height to decrease the columns/width so that the headers I'm constantly adding, fit on the body.
I know about other CSS Preprocesors but I would really love to know a way to do this whit "CSS", nothing more, nothing less.
P.S.: I currently use http://codepen.io/pen.
EDIT: Said to be impossible, I didn't found a solution myself. No more posts needed (mostly questions) unless with the actual solution as I don't think it will be posted, but, as long as it isn't posted, anyone can post it (I will just not answer but will check).
I would personally place the table inside a div.
That way, you can style the div's width to be page width, or any specified amount of pixels/percentage of page width, and then set the width of the table to 100%. This is assuming you have your view port statement set to device width.
HTML:
<div class = 'container'>
<table class = 'tab'>
</div>
CSS:
.container{
width = 1200px
}
.tab{
width = 100%
}
I want it to automatically change the width of the body to the same as that table
Here is my solution, Pure CSS
body {
width: auto;
height: auto;
border: 2px solid red;
display: inline-block;
}
table {
width: 100px;
height: 100px;
}
<body>
<table>
</table>
</body>
You will need to use javascript to extract out the current width of the table
var width = document.getElementById('table').style.offsethwidth;
then calling the function with AJAX and then including the value in the style. However, you will need to use <table width="$width"</table>
To be able to extract out the width, you will need to use a GET to call the variable. Thus what you want to do is technically not possible, this is the only way i can see it being done.

Fluid table: Truncate cell contents when needed, otherwise keep the table as narrow as possible

I have an HTML 5 table with three rows and four columns (two attribute/value pairs per row) on a responsive page (Bootstrap).
In desktop view (or with little content), I would like the table not to fill the entire width available, just enough to show the data. Columns should have individual widths, just enough to show their content. In other words, I want white space to the right of the table, when possible.
In mobile view (or with lots of content), I would like the cell contents to truncate (e.g. using text-overflow: ellipsis;), not to wrap. If possible, I want the attribute columns to truncate first, to a pre-set minimum. It's OK without that prioritization if it can't be solved elegantly.
Desired outcome:
I have browsed Stack Overflow etc. for several hours, and most solutions I find (e.g. 1, 2, 3) demands the width of the table to be set, often to 100%, which is undesirable for my purposes. I have seen people mentioning putting div elements inside td elements, but without examples, and I haven't been able to figure it out on my own.
I don't have to solve this using table, so other solutions are welcome.
OK, I found a solution. Fiddle here.
Strategy:
Use a hidden table to glean the desired cell widths of the visible table.
Tactic:
In addition to the table that the user shall see, a hidden "shadow" table, with identical content, must be created directly above the visible table.
The shadow table must allow the content to wrap inside the cells (this is default table behavior).
When the page has loaded and at every window resize, show() the shadow table, measure the width of every td in the top row, then hide() the shadow table. Then copy the width values to the corresponding td elements in the visible table, which must have Chris Coyier's truncate applied.
Works in all browsers I've tested, including mobile.
Bonus tips:
Use ­ to wrap long words if necessary, and to stop words from wrapping. This can be applied only in the shadow table.
Use 1px more cell padding in the shadow table due to a bug in Internet Explorer - otherwise, IE's visible table sometimes becomes slightly wider than the shadow table.
JavaScript (requiring jQuery):
<script type="text/javascript">
function loadEvents() {
initFluidTables();
}
// Resize fluid table(s)
function resizeFluidTables() {
// Show source cells
$( ".fluid-table-invisible-source" ).show(0);
var fluidTableCellWidth = [];
// Measure (normally invisible) source cells
$( ".fluid-table-invisible-source td" ).each(function( index, value ) {
fluidTableCellWidth[index] = $( this ).width();
});
// Resize (always visible) target cells. Adding 1 pixel due to apparent bug in Firefox.
$( ".fluid-table-visible-target td>i" ).each(function( index, value ) {
$( this ).css({'width': fluidTableCellWidth[index]+1 });
});
// Re-hide source cells
$( ".fluid-table-invisible-source" ).hide();
}
// Create table(s) to be fluid
function initFluidTables() {
// Create a container. Not really necessary, but keeps DOM tidier.
$(".fluid-table").wrap( "<div></div>" );
// This looks like a mess. What it does, is that .fluid-table duplicates itself, and each sibling gets a different class.
$(".fluid-table").each(function() {
$( this ).clone().appendTo( $( this ).addClass( "fluid-table-invisible-source" ).parent() ).addClass( "fluid-table-visible-target" );
});
// Add truncating element inside target cells
$(".fluid-table-visible-target td").wrapInner( "<i></i>");
// Truncate table contents at first drawing of the DOM and every time the window resizes
resizeFluidTables();
$( window ).resize(function() {
resizeFluidTables();
});
}
</script>
CSS:
.fluid-table td { padding-right: 5px; }
.fluid-table td:nth-child(odd) { color: #aaa; }
.fluid-table-visible-target td>i {
font-style: inherit;
white-space: nowrap;
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
/* source slighly more padded than target due to IE bug */
.fluid-table-invisible-source td:nth-child(even) {
padding-right: 10px;
}
.fluid-table-visible-target td:nth-child(even) {
padding-right: 9px;
}
Sample table:
Note use of ­ and to indicate where you (do not) want the text to truncate.
<table class="fluid-table">
<tr>
<td>Avail­able <i>until</i>:</td><td>No expiry date</td><td>Avail­ability:</td><td>Worldwide</td><td></td>
</tr><tr>
<td>Year:</td><td>2016</td><td>Length:</td><td>29 minutes</td><td></td>
</tr><tr>
<td>First broad­cast:</td><td>Feb 2</td><td>Last broad­cast:</td><td>Feb 3</td><td></td>
</tr>
</table>
Try putting a div inside the td and use Chris Coyier's truncate. Also, set the width of the truncate container to 100% to truncate based on the available space rather than a fixed width.

Liquid faux columns with a border

I'm designing a 2-column HTML5 interface (backwards compatibility is not important, all users will be using latest Chrome/FF), with a sidebar on the left and a main content area on the right.
I want the sidebar to have a background color as well as a right border (something like the OS X Finder sidebar). I want that background and border to extend to the bottom of the page, even if the page content isn't enough to extend the page to that size. I also want the sidebar to be set to a percentage, rather than a set amount of pixels.
Normally, I'd go with faux columns, or even liquid faux columns in this case. However I want to have a border to the right of the column, and I'm not sure if/how that's possible.
Ideally, I could do all this without images/faux columns at all. Is there a CSS3/HTML5 feature that will allow for that (as I said, backwards compatibility is not a problem)? If not, are there any other solutions to this besides making the sidebar a fixed size?
I think this demonstrates what you're looking for. If you have any questions about how this example works, don't hesitate to ask! :)
http://www.thechoppr.com/2009/02/09/2-column-fluid-layout-100-height-with-leftright-sidebar
Umm... that ?
<style type="text/css">
#left
{ height:100%;
width:25%;
min-width: 100px;
float:left;
background-color:red;
border-right:solid 3px orange;
}
</style>
<div id="left">menu</div>
<div id="right">contents</div>
You can use some jQuery:
var sidebar_height = 0;
var content_height = 0;
sidebar_height = $("#sidebar").height();
content_height = $("#content").height();
if(sidebar_height > content_height){
$("#content").css("height", sidebar_height);
} else {
$("#sidebar").css("height", content_height);
}

Is there a css3 way to fix a <thead> on the y-axis, let it scroll on the x-axis?

This has been asked a zillion times: here, there, and the other place just on SO. Yet there's no real good answer that I can find.
<recap> Often I have tables that are vertically much deeper than the viewport. I'd like to be able to scroll the table's <tbody> while its <thead> remains fixed and visible. Some of these tables are also much wider than the viewport. Here I want the table's <thead> to scroll horizontally.
To get the effect, I have dozens of lines of JS, including setInterval( ) calls to check scrollLeft and scrollTop, so that I can reposition a copy of <thead>1. It works but it's a huge, ungainly, frail and unmaintainable pain in the ass.</recap>
Question: Is there some straightforward css3 way, existent or emerging or proposed, that I can use to get a a table's <thead> and <tbody> to scroll horizontally and vertically, yet independently of each other?
Thanks!
1 Why setInterval( )? Because IE doesn't uniformly deliver onScroll events, you silly; everybody knows that!
I ended up modeling the "solution" on the suggestion of #Naveed Ahmad. I put the data part of the table in a <tbody>; and made a fake <thead> that I filled in at onLoad time by referring to the widths and offsets of the <td>s in the first table row, like this:
function position_col_heads ( ) {
// get all the TD child elements from the first row
var tds = main_tbody.children[0].getElementsByTagName('TD');
for ( var i = 0; i < tds.length; ++i ) {
thead.children[i].style.left =
parseFloat( tds[i].offsetLeft ) + 'px';
thead.children[i].style.width =
parseFloat( tds[i].scrollWidth ) + 'px';
}
}
This works more or less OK on this page.
a dirty way would be to put the header table in a separate div like:
<div class="header">
<table>
<thead><tr><td>#</td><td>v</td></tr></thead>
</table>
</div>
Then body in the another div like:
<div class="body">
<table>
<tbody>
<tr><td>1</td><td>a</td></tr>
<tr><td>1</td><td>a</td></tr>
<tr><td>1</td><td>a</td></tr>
<tr><td>1</td><td>a</td></tr>
<tr><td>1</td><td>a</td></tr>
</tbody>
</table>
</div>
Now you can give a fixed height to body div and set oveflow to auto. like:
table{border:0px solid #ccc;height:30px;}
table tr td{border:1px solid #ccc;padding:5px;}
div.body{height:70px;overflow:auto;border-bottom:1px solid #ccc;}
here is a working example I did in jsfiddle

CSS Columns Table like

Back in the day, when tables were used to layout content setting up a column type of layout was easy.I.e <table><tr><td>column 1</td><td>Column2</td></table>
I'm trying to do the same thing using CSS (with Divs etc..), however, I'm having difficulties trying to keep the two columns with the same size. Here's an example of what I'm talking about: http://www.jsfiddle.net/ybYJ9/
What I'm trying to do is have the column on the left (marked in blue) have the same size as the column on the right. The height of the column of the right will grow depending on content size, so I can't fix the size. I've tried to set the column height to be 100% and it isn't working if you can help with this it would be great.
Thanks
The best way is to use something like this.
Use jQuery (or similar scripting language) to detect the height of your main div and transfer the value to your other div :)
Most commonly used method is "Faux Columns".
You can apply a background image/color of a shorter column to an overall container. Container will strech to higher column, so it'll look like they're having same height.
Second way is background that'll look like both columns, and you'll repeat it vertically. It'll work great if u dont know which column will be higher.
At the moment there isn't a clear and safe way of doing this in CSS.
You might:
Use faux columns – if the design permits you
Use display: table, display: table-row and display: table-cell, effectively renderings semantic markup as a table. Here's some details about this, but be wary, it doesn't work in
Use JS to set equal heights to the columns. You can do this manually (comparing the height of each column, and setting each column's height to be that of the tallest), or simpler via a jquery plugin.
If you rely on JS, be careful of dynamic height changes, as you will have to update the previously set height declarations.
There are lots of ways to do it, and none of them are 100% satisfactory to me. Rebelek's answer is a good one, but my favourite method is with a jQuery plugin.
(function ($) {
$.fn.equalColumnHeights = function () {
equalise = function (element) {
var maxHeight = 0;
$(element).children().each(function() {
if ($(this).height() > maxHeight) {
maxHeight = $(this).height();
}
});
$(element).children().height(maxHeight);
};
return this.each(function () {
equalise(this);
var element = this;
$(window).resize(function () {
$(element).children().height('');
equalise(element);
});
});
};
}(jQuery));
Then in your example:
jQuery(function ($) {
$('#mainBody').equalColumnHeights();
});
Example here: http://www.jsfiddle.net/nathan/ybYJ9/9/ I haven't changed anything except for adding the above JavaScript. That's why I like this method: completely non-intrusive.
You can't set the two different div elements the same non-fixed height without using the JS.
However, you can try to emulate the table behavior with the CSS display property.
Look over these 3 properties:
display: table;
display: table-row;
display: table-cell;
Set the display: table for your outer div (which will act like HTML <table> tag)
For your rows use display: table-row and for table cells use display: table-cell
You will have something like this:
<div style="display: table;">
<div style="display: table-row;">
<div style="display: table-cell;">
1 cell
</div>
<div style="display: table-cell;">
2 cell
</div>
</div>
</div>
But please be careful since not all browsers handle these properties correctly.
Hope it helps.