HTML Table Row nth-child Dynamic Content Issue - javascript

I have a table whose <tr>s I populate on the fly by manually changing the table's innerHTML. When I do, the CSS I have in place (which implements nth-child(odd/even)) fails.
tr:nth-child(odd) {
background-color: #000000;
}
tr:nth-child(even) {
background-color: #FFFFFF;
}
The resulting table has all <tr>s with the color scheme of an nth-child(odd) selector.
When the table is populated dynamically with tableVar.innerHTML += "<tr>...</tr>" the issue arises, however, it turns out if you change the way the innerHTML is modified by first assembling a string with stringVar += "<tr>...</tr>" and then assigning the innerHTML to the string with tableVar.innerHTML = stringVar the problem goes away and the expected behavior returns.
Why does this happen?

The browser wraps your new rows in <tbody> elements when you concatenate a new row directly to the table element, so each subsequent row is a single row per new <tbody>, making it apply to the nth-child(odd) selector. This explains why constructing new HTML from scratch and assigning it to the table doesn't show that problem. You can see this by inspecting the HTML generated in your browser's developer console when you run this example:
var table = document.getElementById("table");
setInterval(function() {
table.innerHTML += "<tr><td>Test1</td><td>Test 2</td></tr>";
}, 1000)
tr:nth-child(odd) {
background-color: #cccccc;
}
tr:nth-child(even) {
background-color: #FFFFFF;
}
<table id="table">
<tbody id="tbody">
<tr>
<td>Test 1</td>
<td>Test 2</td>
</tr>
<tr>
<td>Test 1</td>
<td>Test 2</td>
</tr>
<tr>
<td>Test 1</td>
<td>Test 2</td>
</tr>
</tbody>
</table>
If you append directly to the table's <tbody> element instead, you'll see the CSS apply to your new rows as expected:
var tbody = document.getElementById("tbody");
setInterval(function() {
tbody.innerHTML += "<tr><td>Test1</td><td>Test 2</td></tr>";
}, 1000)
tr:nth-child(odd) {
background-color: #cccccc;
}
tr:nth-child(even) {
background-color: #FFFFFF;
}
<table id="table">
<tbody id="tbody">
<tr>
<td>Test 1</td>
<td>Test 2</td>
</tr>
<tr>
<td>Test 1</td>
<td>Test 2</td>
</tr>
<tr>
<td>Test 1</td>
<td>Test 2</td>
</tr>
</tbody>
</table>

Related

Group column with the same subheader name in datatable using Javascript/jQuery

I have a table like this
In which I have different city where we have demand-supply of different products.
Now what I want as here demand is different for all the products However supply is the same on all of three product, so I want that table looks like in this manner.
What I want to do is I want only to show the supply column once in the last of the table. This has to be done dynamically as in the future we have multiple products
Can anyone help me with this?
What the code below does is:
Identify the positions of the "Supply"'s and store them in ind array, in this case will be [3, 5, 7]
Loops through ind except for the last element 7(as one "Supply" will be left) and hide all td's; $("td:nth-child("3"), $("td:nth-child("5")
The "Demand"s that precede each of these elements will be assigned two spaces.
let ind = [];
$("td:contains('Supply')").each(function (index) {
ind.push($(this).index() + 1);
});
$(".hide").on("click", function () {
for (let i = 0; i < ind.length - 1; i++) {
let el = $("td:nth-child(" + ind[i] + ")");
el.prev().attr("colspan", "2");
el.hide();
}
});
table,
th,
td {
border: 1px solid black;
border-collapse: collapse;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr>
<th>City</th>
<th colspan="2">Product 1</th>
<th colspan="2">Product 2</th>
<th colspan="2">Product 3</th>
</tr>
<tr>
<td></td>
<td>Demand</td>
<td>Supply</td>
<td>Demand</td>
<td>Supply</td>
<td>Demand</td>
<td>Supply</td>
</tr>
<tr>
<td>City 1</td>
<td>50$</td>
<td>60$</td>
<td>90$</td>
<td>60$</td>
<td>100$</td>
<td>60$</td>
</tr>
<tr>
<td>City 2</td>
<td>50$</td>
<td>60$</td>
<td>90$</td>
<td>60$</td>
<td>100$</td>
<td>60$</td>
</tr>
<tr>
<td>City 3</td>
<td>50$</td>
<td>60$</td>
<td>90$</td>
<td>60$</td>
<td>100$</td>
<td>60$</td>
</tr>
<tr>
<td>City 4</td>
<td>50$</td>
<td>60$</td>
<td>90$</td>
<td>60$</td>
<td>100$</td>
<td>60$</td>
</tr>
</table>
<button class="hide">Hide</button>

Get prepend() dynamically

I have a table generated in a backend. My task is to wrap text in each header column and add it to each body column cells by using .html() and .prepend(). It works as expected (you will see text in green).
Problem: Tables are generated in a backend, sometimes a table has 3 columns, sometimes 4 columns or more! How to write my Jquery dynamically in order to work on each table.
Please give me a hand. Thanks!
$(document).ready(function() {
var firstHead = $("table thead tr th:first-child").html();
var firstBodyCode = $("<span></span>").text(firstHead);
$('table tbody tr td:first-child').prepend(firstBodyCode);
var secondHead = $("table thead tr th:nth-child(2)").html();
var secondBodyCode = $("<span></span>").text(secondHead);
$('table tbody tr td:nth-child(2)').prepend(secondBodyCode);
});
table {
width: 100%;
border: 1px solid gray
}
span {
color: green
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<tbody>
<tr>
<td>January</td>
<td>$100</td>
</tr>
<tr>
<td>February</td>
<td>$80</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Sum</td>
<td>$180</td>
</tr>
</tfoot>
</table>
Use a loop to iterate over all the columns.
$(document).ready(function() {
$("table thead tr th").each(function(i) {
let bodyCode = $("<span>", {
text: $(this).text()
});
$(`table tbody tr td:nth-child(${i+1})`).prepend(bodyCode);
});
});
table {
width: 100%;
border: 1px solid gray
}
span {
color: green
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<th>Month</th>
<th>Savings</th>
</tr>
</thead>
<tbody>
<tr>
<td>January</td>
<td>$100</td>
</tr>
<tr>
<td>February</td>
<td>$80</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Sum</td>
<td>$180</td>
</tr>
</tfoot>
</table>

Add event listener to dynamically create table row

I have a table with ajax call to create rows within the tbody element. I have the table created on the html page.
<table id='mytable'>
<thead>
<tr>
<th>First Col</th>
<th>Second Col</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
My javascript code to attach the event to second cell of each row in tbody
$('#mytable tbody').on( 'click', 'tr td:eq(2)', function() {
console.log($(this).html())
});
This code only works for the second cell of the first row of the tbody. Clicking the second cell of all other rows did not trigger the event. I have to work around this by check the cell index
if (this.cellIndex == 2) console.log($(this).html())
I still want to know how to make the correct selection.
To select the specific td of each row use nth-child() instead of eq():
$('#mytable tbody').on( 'click', 'tr td:nth-child(3)', function() {
console.log($(this).html())
});
You can just have an event listener for the entire table and then test what was clicked. Adding the event listener to the table you don't need to assign it again if the content of the table changes.
Adding a class name can both be useful for the usability (styling the cursor) and easier to find elements using JS.
document.getElementById('mytable').addEventListener('click', e => {
let td = e.target.closest('td[class="clickable"]');
if (td) {
console.log(e.target.innerText, 'was clicked');
}
});
td.clickable {
cursor: pointer;
}
<table id='mytable'>
<thead>
<tr>
<th>First Col</th>
<th>Second Col</th>
</tr>
</thead>
<tbody>
<tr>
<td>First</td>
<td class="clickable">Second 1</td>
</tr>
<tr>
<td>First</td>
<td class="clickable">Second 2</td>
</tr>
<tr>
<td>First</td>
<td class="clickable">Second 3</td>
</tr>
<tr>
<td>First</td>
<td class="clickable">Second 4</td>
</tr>
</tbody>
</table>

How to display row number in HTML table in specific column?

I have a table in HTML that has 5 columns. The first column is the "row number", where I want to show which row it is--starting from 1.
Here's a picture
I have tried using this CSS:
body {
/* Set the Serial counter to 0 */
counter-reset: Serial;
}
table {
border-collapse: separate;
}
tr td:first-child:before {
/* Increment the Serial counter */
counter-increment: Serial;
/* Display the counter */
content: "Serial is: " counter(Serial);
}
You can use next option which is through css again:
Note: class="css-serial"
<table class="css-serial">
<thead>
<tr>
<th>#</th>
<th>1st Column</th>
<th>2nd Column</th>
</tr>
</thead>
<tbody>
<tr>
<td></td> <!--intentionally left blank-->
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td> <!--intentionally left blank-->
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td> <!--intentionally left blank-->
<td>Column 1</td>
<td>Column 2</td>
</tr>
</tbody>
</table>
And add next style:
<style>
/*adding row numbers through css*/
.css-serial {
counter-reset: serial-number; /* Set the serial number counter to 0 */
}
.css-serial td:first-child:before {
counter-increment: serial-number; /* Increment the serial number counter */
content: counter(serial-number); /* Display the counter */
}
</style>
Here is the working code for this:
<html>
<head>
<script type="text/javascript">
function displayResult()
{
var index = document.getElementById("myTable").rows.length;
var new_row = '<td>'+index+'</td><td>cell 1</td><td>cell 2</td>';
document.getElementById("myTable").insertRow(-1).innerHTML = new_row;
}
</script>
</head>
<body>
<table id="myTable" border="1">
<tr>
` <td>0</td>
<td>cell 1</td>
<td>cell 2</td>
</tr>
</table>
<br />
<button type="button" onclick="displayResult()">Insert new row</button>
</body>
</html>
Without the code of how you're doing it it's hard to say.
I'm assuming the rows are in a collection, since you have an add-function.
Can't you just use the index + 1?
If the add functions just adds the raw html, you can get the table element and count the children (or use the last child) and calculate your number from that.
With the little info you gave that's all I can say.

How can I let a table's body scroll but keep its head fixed in place?

I am writing a page where I need an HTML table to maintain a set size. I need the headers at the top of the table to stay there at all times but I also need the body of the table to scroll no matter how many rows are added to the table. Think a mini version of excel. This seems like a simple task but almost every solution I have found on the web has some drawback. How can I solve this?
I had to find the same answer. The best example I found is http://www.cssplay.co.uk/menu/tablescroll.html - I found example #2 worked well for me. You will have to set the height of the inner table with Java Script, the rest is CSS.
I found DataTables to be quite flexible. While its default version is based on jquery, there is also an AngularJs plugin.
I saw Sean Haddy's excellent solution to a similar question and took the liberty of making some edits:
Use classes instead of ID, so one jQuery script could be reused for
multiple tables on one page
Added support for semantic HTML table elements like caption, thead, tfoot, and tbody
Made scrollbar optional so it won't appear for tables that are "shorter" than the scrollable height
Adjusted scrolling div's width to bring the scrollbar up to the right edge of the table
Made concept accessible by
using aria-hidden="true" on injected static table header
and leaving original thead in place, just hidden with jQuery and set aria-hidden="false"
Showed examples of multiple tables with different sizes
Sean did the heavy lifting, though. Thanks to Matt Burland, too, for pointing out need to support tfoot.
Please see for yourself at http://jsfiddle.net/jhfrench/eNP2N/
Have you tried using thead and tbody, and setting a fixed height on tbody with overflow:scroll?
What are your target browsers?
EDIT: It worked well (almost) in firefox - the addition of the vertical scrollbar caused the need for a horizontal scrollbar as well - yuck. IE just set the height of each td to what I had specifed the height of tbody to be. Here's the best I could come up with:
<html>
<head>
<title>Blah</title>
<style type="text/css">
table { width:300px; }
tbody { height:10em; overflow:scroll;}
td { height:auto; }
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>One</th><th>Two</th>
</td>
</tr>
</thead>
<tbody>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
<tr><td>Data</td><td>Data</td></tr>
</tbody>
</table>
</body>
</html>
What you need is :
have a table body of limited height as scroll occurs only when contents is bigger than the scrolling window. However tbody cannot be sized, and you have to display it as a block to do so:
tbody {
overflow-y: auto;
display: block;
max-height: 10em; // For example
}
Re-sync table header and table body columns widths as making the latter a block made it an unrelated element. The only way to do so is to simulate synchronization by enforcing the same columns width to both.
However, since tbody itself is a block now, it can no longer behave like a table. Since you still need a table behavior to display you columns correctly, the solution is to ask for each of your rows to display as individual tables:
thead {
display: table;
width: 100%; // Fill the containing table
}
tbody tr {
display: table;
width: 100%; // Fill the containing table
}
(Note that, using this technique, you won't be able to span across rows anymore).
Once that done, you can enforce column widths to have the same width in both thead and tbody. You could not that:
individually for each column (through specific CSS classes or inline styling), which is quite tedious to do for each table instance ;
uniformly for all columns (through th, td { width: 20%; } if you have 5 columns for example), which is more practical (no need to set width for each table instance) but cannot work for any columns count
uniformly for any columns count, through a fixed table layout (i.e. same width for all).
I prefer the last option, which requires adding:
thead {
table-layout: fixed; // Same layout for all cells
}
tbody tr {
table-layout: fixed; // Same layout for all cells
}
th, td {
width: auto; // Same width for all cells, if table has fixed layout
}
See a demo here, forked from the answer to this question.
Edit: This is a very old answer and is here for prosperity just to show that it was supported once back in the 2000's but dropped because browsers strategy in the 2010's was to respect W3C specifications even if some features were removed: Scrollable table with fixed header/footer was clumsily specified before HTML5.
Bad news
Unfortunately there is no elegant way to handle scrollable table with fixed thead/tfoot
because HTML/CSS specifications are not very clear about that feature.
Explanations
Although HTML 4.01 Specification says thead/tfoot/tbody are used (introduced?) to scroll table body:
Table rows may be grouped [...] using the THEAD, TFOOT and TBODY elements [...].
This division enables user agents to support scrolling of table bodies independently of the table head and foot.
But the working scrollable table feature on FF 3.6 has been removed in FF 3.7 because considered as a bug because not compliant with HTML/CSS specifications. See this and that comments on FF bugs.
Mozilla Developer Network tip
Below is a simplified version of the MDN useful tips for scrollable table
see this archived page or the current French version
<style type="text/css">
table {
border-spacing: 0; /* workaround */
}
tbody {
height: 4em; /* define the height */
overflow-x: hidden; /* esthetics */
overflow-y: auto; /* allow scrolling cells */
}
td {
border-left: 1px solid blue; /* workaround */
border-bottom: 1px solid blue; /* workaround */
}
</style>
<table>
<thead><tr><th>Header
<tfoot><tr><th>Footer
<tbody>
<tr><td>Cell 1 <tr><td>Cell 2
<tr><td>Cell 3 <tr><td>Cell 4
<tr><td>Cell 5 <tr><td>Cell 6
<tr><td>Cell 7 <tr><td>Cell 8
<tr><td>Cell 9 <tr><td>Cell 10
<tr><td>Cell 11 <tr><td>Cell 12
<tr><td>Cell 13 <tr><td>Cell 14
</tbody>
</table>
However MDN also says this does not work any more on FF :-(
I have also tested on IE8 => table is not scrollable either :-((
Not sure if anyone is still looking at this but they way I have done this previously is to use two tables to display the single original table - the first just the original table title line and no table body rows (or an empty body row to make it validate).
The second is in a separate div and has no title and just the original table body rows. The separate div is then made scrollable.
The second table in it's div is placed just below the first table in the HTML and it looks like a single table with a fixed header and a scrollable lower section. I have only tested this in Safari, Firefox and IE (latest versions of each in Spring 2010) but it worked in all of them.
The only issue it had was that the first table would not validate without a body (W3.org validator - XHTML 1.0 strict), and when I added one with no content it causes a blank row. You can use CSS to make this not visible but it still eats up space on the page.
This solution works in Chrome 35, Firefox 30 and IE 11 (not tested other versions)
Its pure CSS:
http://jsfiddle.net/ffabreti/d4sope1u/
Everything is set to display:block, table needs a height:
table {
overflow: scroll;
display: block; /*inline-block*/
height: 120px;
}
thead > tr {
position: absolute;
display: block;
padding: 0;
margin: 0;
top: 0;
background-color: gray;
}
tbody > tr:nth-of-type(1) {
margin-top: 16px;
}
tbody tr {
display: block;
}
td, th {
width: 70px;
border-style:solid;
border-width:1px;
border-color:black;
}
This caused me huge headaches trying to implement such a grid for an application of ours. I tried all the various techniques out there but they each had problems. The closest I came was using a jQuery plugin such as Flexigrid (look on http://www.ajaxrain.com for alternatives), but this doesn't seem to support 100% wide tables which is what I needed.
What I ended up doing was rolling my own; Firefox supports scrolling tbody elements so I browser sniffed and used appropriate CSS (setting height, overflow etc... ask if you want more details) to make that scroll, and then for other browsers I used two separate tables set to use table-layout: fixed which uses a sizing algorithm that is guarenteed not to overflow the stated size (normal tables will expand when content is too wide to fit). By giving both tables identical widths I was able to get their columns to line up. I wrapped the second one in a div set to scroll and with a bit of jiggery pokery with margins etc managed to get the look and feel I wanted.
Sorry if this answer sounds a bit vague in places; I'm writing quickly as I don't have much time. Leave a comment if you want me to expand any further!
Here's a code that really works for IE and FF (at least):
<html>
<head>
<title>Test</title>
<style type="text/css">
table{
width: 400px;
}
tbody {
height: 100px;
overflow: scroll;
}
div {
height: 100px;
width: 400px;
position: relative;
}
tr.alt td {
background-color: #EEEEEE;
}
</style>
<!--[if IE]>
<style type="text/css">
div {
overflow-y: scroll;
overflow-x: hidden;
}
thead tr {
position: absolute;
top: expression(this.offsetParent.scrollTop);
}
tbody {
height: auto;
}
</style>
<![endif]-->
</head>
<body>
<div >
<table border="0" cellspacing="0" cellpadding="0">
<thead>
<tr>
<th style="background: lightgreen;">user</th>
<th style="background: lightgreen;">email</th>
<th style="background: lightgreen;">id</th>
<th style="background: lightgreen;">Y/N</th>
</tr>
</thead>
<tbody align="center">
<!--[if IE]>
<tr>
<td colspan="4">on IE it's overridden by the header</td>
</tr>
<![endif]-->
<tr>
<td>user 1</td>
<td>user#user.com</td>
<td>1</td>
<td>Y</td>
</tr>
<tr class="alt">
<td>user 2</td>
<td>user#user.com</td>
<td>2</td>
<td>N</td>
</tr>
<tr>
<td>user 3</td>
<td>user#user.com</td>
<td>3</td>
<td>Y</td>
</tr>
<tr class="alt">
<td>user 4</td>
<td>user#user.com</td>
<td>4</td>
<td>N</td>
</tr>
<tr>
<td>user 5</td>
<td>user#user.com</td>
<td>5</td>
<td>Y</td>
</tr>
<tr class="alt">
<td>user 6</td>
<td>user#user.com</td>
<td>6</td>
<td>N</td>
</tr>
<tr>
<td>user 7</td>
<td>user#user.com</td>
<td>7</td>
<td>Y</td>
</tr>
<tr class="alt">
<td>user 8</td>
<td>user#user.com</td>
<td>8</td>
<td>N</td>
</tr>
</tbody>
</table>
</div>
</body></html>
I've changed the original code to make it clearer and also to put it working fine in IE and also FF..
Original code HERE
Here's my alternative. It also uses different DIVs for the header, body and footer but synchronised for window resizing and with searching, scrolling, sorting, filtering and positioning:
My CD lists
Click on the Jazz, Classical... buttons to see the tables. It's set up so that it's adequate even if JavaScript is turned off.
Seems OK on IE, FF and WebKit (Chrome, Safari).
Sorry I haven.t read all replies to your question.
Yeah here the thing you want (I have done already)
You can use two tables, with same class name for similar styling, one only with table head and another with your rows.
Now put this table inside a div having fixed height with overflow-y:auto OR scroll.
The main problem I had with the suggestions above was being able to plug in tablesorter.js AND being able to float the headers for a table constrained to a specific max size. I eventually stumbled across the plugin jQuery.floatThead which provided the floating headers and allowed sorting to continue to work.
It also has a nice comparison page showing itself vs similar plugins.
Live JsFiddle
It is possible with only HTML & CSS
table.scrollTable {
border: 1px solid #963;
width: 718px;
}
thead.fixedHeader {
display: block;
}
thead.fixedHeader tr {
height: 30px;
background: #c96;
}
thead.fixedHeader tr th {
border-right: 1px solid black;
}
tbody.scrollContent {
display: block;
height: 262px;
overflow: auto;
}
tbody.scrollContent td {
background: #eee;
border-right: 1px solid black;
height: 25px;
}
tbody.scrollContent tr.alternateRow td {
background: #fff;
}
thead.fixedHeader th {
width: 233px;
}
thead.fixedHeader th:last-child {
width: 251px;
}
tbody.scrollContent td {
width: 233px;
}
<table cellspacing="0" cellpadding="0" class="scrollTable">
<thead class="fixedHeader">
<tr class="alternateRow">
<th>Header 1</th>
<th>Header 2</th>
<th>Header 3</th>
</tr>
</thead>
<tbody class="scrollContent">
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>And Repeat 1</td>
<td>And Repeat 2</td>
<td>And Repeat 3</td>
</tr>
<tr class="normalRow">
<td>Cell Content 1</td>
<td>Cell Content 2</td>
<td>Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>More Cell Content 1</td>
<td>More Cell Content 2</td>
<td>More Cell Content 3</td>
</tr>
<tr class="normalRow">
<td>Even More Cell Content 1</td>
<td>Even More Cell Content 2</td>
<td>Even More Cell Content 3</td>
</tr>
<tr class="alternateRow">
<td>End of Cell Content 1</td>
<td>End of Cell Content 2</td>
<td>End of Cell Content 3</td>
</tr>
</tbody>
</table>
If its ok to use JavaScript here is my solution
Create a table set fixed width on all columns (pixels!) add the class Scrollify to the table and add this javascript + jquery 1.4.x set height in css or style!
Tested in: Opera, Chrome, Safari, FF, IE5.5(Epic script fail), IE6, IE7, IE8, IE9
//Usage add Scrollify class to a table where all columns (header and body) have a fixed pixel width
$(document).ready(function () {
$("table.Scrollify").each(function (index, element) {
var header = $(element).children().children().first();
var headerHtml = header.html();
var width = $(element).outerWidth();
var height = parseInt($(element).css("height")) - header.outerHeight();
$(element).height("auto");
header.remove();
var html = "<table style=\"border-collapse: collapse;\" border=\"1\" rules=\"all\" cellspacing=\"0\"><tr>" + headerHtml +
"</tr></table><div style=\"overflow: auto;border:0;margin:0;padding:0;height:" + height + "px;width:" + (parseInt(width) + scrollbarWidth()) + "px;\">" +
$(element).parent().html() + "</div>";
$(element).parent().html(html);
});
});
//Function source: http://www.fleegix.org/articles/2006-05-30-getting-the-scrollbar-width-in-pixels
//License: Apache License, version 2
function scrollbarWidth() {
var scr = null;
var inn = null;
var wNoScroll = 0;
var wScroll = 0;
// Outer scrolling div
scr = document.createElement('div');
scr.style.position = 'absolute';
scr.style.top = '-1000px';
scr.style.left = '-1000px';
scr.style.width = '100px';
scr.style.height = '50px';
// Start with no scrollbar
scr.style.overflow = 'hidden';
// Inner content div
inn = document.createElement('div');
inn.style.width = '100%';
inn.style.height = '200px';
// Put the inner div in the scrolling div
scr.appendChild(inn);
// Append the scrolling div to the doc
document.body.appendChild(scr);
// Width of the inner div sans scrollbar
wNoScroll = inn.offsetWidth;
// Add the scrollbar
scr.style.overflow = 'auto';
// Width of the inner div width scrollbar
wScroll = inn.offsetWidth;
// Remove the scrolling div from the doc
document.body.removeChild(
document.body.lastChild);
// Pixel width of the scroller
return (wNoScroll - wScroll);
}
Edit: Fixed height.
I do this with javascript (no library) and CSS - the table body scrolls with the page, and the table does not have to be fixed width or height, although each column must have a width. You can also keep sorting functionality.
Basically:
In HTML, create container divs to position the table header row and the
table body, also create a "mask" div to hide the table body as it
scrolls past the header
In CSS, convert the table parts to blocks
In Javascript, get the table width and match the mask's width... get
the height of the page content... measure scroll position...
manipulate CSS to set the table header row position and the mask
height
Here's the javascript and a jsFiddle DEMO.
// get table width and match the mask width
function setMaskWidth() {
if (document.getElementById('mask') !==null) {
var tableWidth = document.getElementById('theTable').offsetWidth;
// match elements to the table width
document.getElementById('mask').style.width = tableWidth + "px";
}
}
function fixTop() {
// get height of page content
function getScrollY() {
var y = 0;
if( typeof ( window.pageYOffset ) == 'number' ) {
y = window.pageYOffset;
} else if ( document.body && ( document.body.scrollTop) ) {
y = document.body.scrollTop;
} else if ( document.documentElement && ( document.documentElement.scrollTop) ) {
y = document.documentElement.scrollTop;
}
return [y];
}
var y = getScrollY();
var y = y[0];
if (document.getElementById('mask') !==null) {
document.getElementById('mask').style.height = y + "px" ;
if (document.all && document.querySelector && !document.addEventListener) {
document.styleSheets[1].rules[0].style.top = y + "px" ;
} else {
document.styleSheets[1].cssRules[0].style.top = y + "px" ;
}
}
}
window.onscroll = function() {
setMaskWidth();
fixTop();
}
For me, nothing related to scrolling really worked until I removed the width from the table CSS. Originally, the table CSS looked like this:
.table-fill {
background: white;
border-radius:3px;
border-collapse: collapse;
margin: auto;
width: 100%;
max-width: 800px;
padding:5px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
animation: float 5s infinite;
}
As soon as I removed the width:100%; all scrolling features started working.
If you have low enough standards ;) you could place a table that contains only a header directly above a table that has only a body. It won't scroll horizontally, but if you don't need that...

Categories