Sticky header inside a container - javascript

I have a table that is in a container on a page. I am trying to get the headers of the table to stick to the top of the page when the user scrolls past. I have tried multiple methods to make the headers sticky but I am not having much luck.
The table data is being generated in JS.
Any help would be great!
HTML
<div class="container-fluid">
<div id="userTable" class="sticky-table">
<table id="ticketsTable">
<thead id="head" class="sticky-header"</thead>
<tbody id="body">
</tbody>
</table>
</div>
</div>
JavaScript
function generateTableHeader() {
var headerArray = generateHeaderArray(),
headerString = "<thead id='head'><tr>" + "<th></th>";
if (!headerArray.length) {
$("#head").empty();
$("#userTable").append("<h1 id='noTicketsFound'>No tickets found.</h1>");
return;
}
headerOrder.forEach(function(key) {
var isChecked = key;
if (!$(".dropdown-menu-fixed #" + key).is(":checked")) {
isChecked += " uncheckedColumn";
}
headerString += "<th data-property='" + key + "' class='sortableHeader " + isChecked + "'>" +
dictionary[key] + "</th>";
});
headerString += "</tr></thead>";
// replaceWith seems faster than separate calls to empty then append.
$("#head").replaceWith(headerString);
// Add SORTCLASS to SORTPROPERTY column, since that is already sorted.
$(".sortableHeader." + SORTPROPERTY).addClass(SORTCLASS);
}
make headers sticky on scroll function
function stickyTableHeader() {
$(".sticky-table").each(function() {
var el = $(this),
offset = el.offset(),
scrollTop = $(window).scrollTop(),
stickyHeader = $(".stickyHeader", this);
if (scrollTop > offset.top && scrollTop < offset.top + el.height()) {
stickyHeader.css({
visibility: "visible"
});
} else {
stickyHeader.css({
visibility: "hidden"
});
}
});
}
// DOM Ready
$(function() {
var clonedHeaderRow;
$(".sticky-table").each(function() {
clonedHeaderRow = $(".sticky-header", this);
clonedHeaderRow
.before(clonedHeaderRow.clone())
.css("width", clonedHeaderRow.width())
.addClass("stickyHeader");
});
$(window)
.scroll(stickyTableHeader)
.trigger("scroll");
});

This is pretty simple using CSS:
th {
position: sticky;
top: 0;
background: #eee;
}
td, th {
padding: 10px;
}
<div class="container">
<h1>Some Title</h1>
<table>
<thead>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
</tbody>
</table>
</div>

I believe the position:sticky; is not yet supported by all browsers.
I have a solution that is a bit heavy-handed but it works across all browsers. Basically you use one Div as a mask over another Div.
Both of these divs contain the same exact table.
The mask-div will effectively crop the table to show only the thead.
<div class = 'mask-div'>
<table>Copy of Table A</table>
</div>
<div class='scrolling-div">
<table> Table A </table>
</div>
<style>
div{
top:0px;
left:0px;
}
.mask-div{
width:100%;
position: fixed;
height:40px;
overflow: hidden;
background:white;
}
<style>

th {
position: sticky;
top: 0;
background: #eee;
}
td, th {
padding: 10px;
}
<div class="container">
<h1>Some Title</h1>
<table>
<thead>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
<tr>
<td>Thing 1</td>
<td>Thing 2</td>
<td>Thing 3</td>
</tr>
</tbody>
</table>
</div>

Related

Sticky HTML elements within nested scroll windows don't render correctly

In the following scenario...
JSFiddle
...if you scroll the outer table a little bit, you'll see that the sticky table headers within the inner scrolling-container actually render ON TOP of the header for the outer scrolling-container.
Shouldn't these two sets of headers exist in separate stacking contexts?
Is there any workaround for this, aside from just hard-coding z-index values? This is just a mockup of what's being generated dynamically, so I'd have to put a lot of plumbing in place to dynamically calculate the correct z-index values to use at each level, relative to all other levels.
No JavaScript framework being used here, just TypeScript and require.
HTML Code:
<div class="scroll-container">
<table class="outer-table">
<thead>
<tr>
<th>Outer Column 1</th>
<th>Outer Column 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td colspan="2">
<div class="scroll-container">
<table class="inner-table">
<thead>
<tr>
<th>Inner Column 1</th>
<th>Inner Column 2</th>
<th>Inner Column 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
<tr>
<td>Inner Value 1</td>
<td>Inner Value 2</td>
<td>Inner Value 3</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
<tr>
<td>Outer Value 1</td>
<td>Outer Value 2</td>
</tr>
</tbody>
</table>
</div>
CSS:
div.scroll-container {
max-height: 20em;
overflow-y: scroll;
}
table.outer-table div.scroll-container {
max-height: 10em;
}
table {
border: 1px solid black;
margin: 1em;
}
td, th {
border: 1px solid blue;
}
th {
position: sticky;
top: 0;
height: 2em;
}
table.outer-table th {
background: tan;
}
table.inner-table th {
background: gray;
}
You only have one class working for both tables, so there is no distinction made about which one appears on top or beneath the other. You will probably have no choice but to use two separate classes and add a z-index for each. For example - using the same entire class definition for each table:
table.outer-table thead tr th {
position:sticky;
top:0px;
height:2em;
z-index:99;
}
table.inner-table thead tr th {
position:sticky;
top:0px;
height:2em;
z-index:98;
}
I tested this in your JSFiddle page and it seemed to do what you wanted, so it definitely looks like z-index is important here. If nesting of tables can be extensive, a class could target them using something like:
table {z-index:99}
table > table {z-index:98}
table > table > table {z-index:97}
ie - a table, a table within a table and a table within a table within a table.

JQUERY - Hide all table cells underneath table columns based on cell class

we are currently developing an internal report for a client and I can't quite crack this one...
I have a HTML table with a variable amount of column headers and then a variable amount of data rows. Each in the table is assigned a class of either 'td-Red' 'td-Green' or 'td-Grey'. This controls the colour of the cell.
If all of a column's tds have the 'td-Grey' class, we want to hide the entire column including the header.
I'm sure this can be done with jQuery but I'm failing to do so...
Is anybody able to help?
Loop through the headings , use heading index to filter data cells in each column that also have the grey class. Compare length of that collection to total rows and hide accordingly
var $dataRows = $('#myTable tbody tr'),
rowCount = $dataRows.length;
$('#myTable thead th').each(function(i){
var $greyCells = $dataRows.find('td:eq(' + i + ').td-Grey');
if($greyCells.length === rowCount){
$greyCells.add(this).hide();
console.log('Hiding column index = ',i)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="myTable">
<thead>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
<th>Heading 4</th>
<th>Heading 5</th>
</tr>
</thead>
<tbody>
<tr>
<td class="td-Grey">Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
<td class="td-Grey">Col 4</td>
<td>Col 5</td>
</tr>
<tr>
<td class="td-Grey">Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
<td class="td-Grey">Col 4</td>
<td>Col 5</td>
</tr>
<tr>
<td class="td-Grey">Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
<td class="td-Grey">Col 4</td>
<td>Col 5</td>
</tr>
<tr>
<td class="td-Grey">Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
<td class="td-Grey">Col 4</td>
<td>Col 5</td>
</tr>
<tr>
<td class="td-Grey">Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
<td class="td-Grey">Col 4</td>
<td>Col 5</td>
</tr>
</tbody>
</table>

Coloring every second row with rowspan

I want to color every second row of a table. While every regular table can be colored using this:
$('tr:odd').css( "background-color", "orange" );
In my case there are several rowspan, what makes the task more difficult.
This is my desired output:
Using this code above doesn't lead to the desired result:
Here is a fiddle.
$('tr:odd').css("background-color", "orange");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border="1">
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
</tr>
<tr>
<td rowspan="2">Col 1</td>
<td>Col 2</td>
<td rowspan="2">Col 3</td>
</tr>
<tr>
<td>Col 1</td>
</tr>
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
</tr>
<tr>
<td rowspan="2">Col 1</td>
<td rowspan="2">Col 2</td>
<td>Col 3</td>
</tr>
<tr>
<td>Col 1</td>
</tr>
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
</tr>
</table>
Do something like this:
$("table tr").filter(function() {
return $(this).children().length == 3;
}).filter(':odd').addClass('alt');
$("tr.alt td[rowspan]").each(function() {
$(this).parent().nextAll().slice(0, this.rowSpan - 1).addClass('alt');
});
.alt { background-color: orange; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border="1">
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
</tr>
<tr>
<td rowspan="2">Col 1</td>
<td>Col 2</td>
<td rowspan="2">Col 3</td>
</tr>
<tr>
<td>Col 1</td>
</tr>
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
</tr>
<tr>
<td rowspan="2">Col 1</td>
<td rowspan="2">Col 2</td>
<td>Col 3</td>
</tr>
<tr>
<td>Col 1</td>
</tr>
<tr>
<td>Col 1</td>
<td>Col 2</td>
<td>Col 3</td>
</tr>
</table>

html table rows height issue

Okay so I made that table which I want it too look like a gradebook but I cant edit the row heights.
In the picture below I drew a line around the rows I want their size to be decrease so they can look different than the student's rows.
and heres the HTML
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<div class="col-lg-12 table-responsive">
<table class="table table-bordered" style="background-color:white;">
<tbody>
<tr>
<th rowspan="4">Student</th>
<th>Assignment</th>
<td>Assignment 1</td>
<td>Assignment 2</td>
<td>Assignment 3</td>
<td>Assignment 4</td>
</tr>
<div>
<tr>
<th>Category:</th>
<td>Category 1</td>
<td>Category 2</td>
<td>Category 3</td>
<td>Category 4</td>
</tr></div>
<tr>
<th>Due:</th>
<td>Due 1</td>
<td>Due 2</td>
<td>Due 3</td>
<td>Due 4</td>
</tr>
<tr>
<th>Points:</th>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>Moemen Waleed</td>
<td>90% A-</td>
<td>Moemen 1</td>
<td>Moemen 2</td>
<td>Moemen 3</td>
<td>Moemen 4</td>
</tr>
<tr>
<td>Mazen Waleed</td>
<td>93% A</td>
<td>Mazen 1</td>
<td>Mazen 2</td>
<td>Mazen 3</td>
<td>Mazen 4</td>
</tr>
</tbody>
</table>
</div>
Add this style to your hmtl:
<style>
tr:nth-child(-n+4){
line-height: 10px;
min-height: 10px;
height: 10px;
}
</style>
This styles all first four rows in the table.
Here is the result: https://jsfiddle.net/Lbx8xh1a/4/
You can add line-height to that <tr> tags.
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<div class="col-lg-12 table-responsive">
<table class="table table-bordered" style="background-color:white;">
<tbody>
<tr style="line-height: 6px;">
<th rowspan="4">Student</th>
<th>Assignment</th>
<td>Assignment 1</td>
<td>Assignment 2</td>
<td>Assignment 3</td>
<td>Assignment 4</td>
</tr>
<div>
<tr style="line-height: 6px;">
<th>Category:</th>
<td>Category 1</td>
<td>Category 2</td>
<td>Category 3</td>
<td>Category 4</td>
</tr></div>
<tr style="line-height: 6px;">
<th>Due:</th>
<td>Due 1</td>
<td>Due 2</td>
<td>Due 3</td>
<td>Due 4</td>
</tr>
<tr style="line-height: 6px;">
<th>Points:</th>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>Moemen Waleed</td>
<td>90% A-</td>
<td>Moemen 1</td>
<td>Moemen 2</td>
<td>Moemen 3</td>
<td>Moemen 4</td>
</tr>
<tr>
<td>Mazen Waleed</td>
<td>93% A</td>
<td>Mazen 1</td>
<td>Mazen 2</td>
<td>Mazen 3</td>
<td>Mazen 4</td>
</tr>
</tbody>
</table>
</div>

Resizable columns tables with fixed header

I'm trying to create a table with resizable columns and fixed header.
But when I'm trying to drag the header column, table columns change their size, but some gap is created.
For example:
What I get after dragging:
My sample:
<script>
$(document).ready(function(){
$('#tableHeader').colResizable({
liveDrag: true,
onDrag: resFun
});
function resFun() {
var c1 = $('#c1').width();
var c2 = $('#c2').width();
var c3 = $('#c3').width();
$('.c1').width(c1);
$('.c2').width(c2);
$('.c3').width(c3);
};
resFun();
});
</script>
<div style="padding-right: 17px; height: 39px;">
<table class="table table-bordered" id="tableHeader" style="width: 100%; height: 39px;">
<tr>
<th id="c1">first</th>
<th id="c2">second</th>
<th id="c3">third</th>
</tr>
</table>
</div>
<div style="overflow: auto; height: 200px;">
<table class="table table-bordered" id="table">
<tr>
<td class="c1">col 1</td>
<td class="c2">col 2</td>
<td class="c3">col 3</td>
</tr>
<tr>
<td class="c1">col 1</td>
<td class="c2">col 2</td>
<td class="c3">col 3</td>
</tr>
<tr>
<td class="c1">col 1</td>
<td class="c2">col 2</td>
<td class="c3">col 3</td>
</tr>
<tr>
<td class="c1">col 1</td>
<td class="c2">col 2</td>
<td class="c3">col 3</td>
</tr>
<tr>
<td class="c1">col 1</td>
<td class="c2">col 2</td>
<td class="c3">col 3</td>
</tr>
<tr>
<td class="c1">col 1</td>
<td class="c2">col 2</td>
<td class="c3">col 3</td>
</tr>
<tr>
<td class="c1">col 1</td>
<td class="c2">col 2</td>
<td class="c3">col 3</td>
</tr>
<tr>
<td class="c1">col 1</td>
<td class="c2">col 2</td>
<td class="c3">col 3</td>
</tr>
</table>
</div>
The problem is that the cells of the second table have a padding set and the cells of the header doesn't.. add padding to the header as well and should be ok

Categories