How to make single row Non scrollable in HTML - javascript

I want to create a table scrollable but i need to freeze first row of the table from scrolling. how could i achieve this. Expecting any ideas from CSS or JS or Jquery

Your requirement is a very common use case, and quite sadly there is no native way of achieving it in any browser. You have plenty workarounds using CSS, JS, combinations, each solution having specific advantages and inconvenients (the main most common one being the need to fix the columns widths).
You can of course implement it yourself (browse on the web, you should have plenty tutorials for this). Or you can rely on a library that avoids common pitfalls and is most cross-browsers.
You would probably be interested in DataTables plugin for jQuery, and especially its FixedHeader extension.

example.
HTML
<table class="scroll">
<thead>
<tr>
<th>Head 1</th>
<th>Head 2</th>
<th>Head 3</th>
<th>Head 4</th>
<th>Head 5</th>
</tr>
</thead>
<tbody>
<tr>
<td> 1</td>
<td> 2</td>
<td> 3</td>
<td> 4</td>
<td>5</td>
</tr>
<tr>
<td> 1</td>
<td> 2</td>
<td> 3</td>
<td> 4</td>
<td>5</td>
</tr>
<tr>
<td> 1</td>
<td> 2</td>
<td> 3</td>
<td> 4</td>
<td>5</td>
</tr>
<tr>
<td> 1</td>
<td> 2</td>
<td> 3</td>
<td> 4</td>
<td>5</td>
</tr>
<tr>
<td>1</td>
<td> 2</td>
<td> 3</td>
<td> 4</td>
<td>5</td>
</tr>
<tr>
<td> 1</td>
<td> 2</td>
<td> 3</td>
<td>4</td>
<td> 5</td>
</tr>
<tr>
<td> 1</td>
<td> 2</td>
<td> 3</td>
<td> 4</td>
<td> 5</td>
</tr>
</tbody>
</table>
css
table.scroll {
/* width: 100%; */ /* Optional */
/* border-collapse: collapse; */
border-spacing: 0;
border: 2px solid black;
}
table.scroll tbody,
table.scroll thead { display: block; }
thead tr th {
height: 30px;
line-height: 30px;
/* text-align: left; */
}
table.scroll tbody {
height: 100px;
overflow-y: auto;
overflow-x: hidden;
}
tbody { border-top: 2px solid black; }
tbody td, thead th {
/* width: 20%; */ /* Optional */
border-right: 1px solid black;
/* white-space: nowrap; */
}
tbody td:last-child, thead th:last-child {
border-right: none;
}
JS
// Change the selector if needed
var $table = $('table.scroll'),
$bodyCells = $table.find('tbody tr:first').children(),
colWidth;
// Adjust the width of thead cells when window resizes
$(window).resize(function() {
// Get the tbody columns width array
colWidth = $bodyCells.map(function() {
return $(this).width();
}).get();
// Set the width of thead columns
$table.find('thead tr').children().each(function(i, v) {
$(v).width(colWidth[i]);
});
}).resize(); // Trigger resize handler

Using Angular you could use the FixedHeader module like this found on ngmodules http://ngmodules.org/modules/FixedHeader . Or using jquery Plug in http://markmalek.github.io/Fixed-Header-Table/
Also check out using css position: sticky http://charliepark.org/css-only-sticky-headers/

This is called Table Fixed Header Scrolling. You can use this jquery plugin it's very simple to be implemented
fixedheadertable plugin

Use plugin fixedheadertable, Datatable
Refer link Stack over Flow

Related

Dynamically adding colspan and removing the next td

I have a 3*3 table. I need to remove 3rd column if there is no content to display in it.In my js file I wrote something like below.
$('table tbody tr td').each(function () {
if (tempHTML.indexOf("#E") !== -1) { // true
$(this).attr("colspan", 2);
$(this).next("td").remove(); // removes extra td
}
});
tempHtml is my html, in my js file what should be the this to set colspan attribute?
Expected result is to increase the colspan and remove the next td and to have a 3*2 table. Please explain with detail as I'm new to js and html.
As nobody seems interested in answering you, and you asked how to realize your request in javascript, in spite of having presented code in jQuery, here is a solution:
const MyTableBody = document.querySelector('#Table-3cols tbody')
, BtRemC3 = document.querySelector('#Bt-Clean-col3')
BtRemC3.onclick=_=>
{
MyTableBody
.querySelectorAll('td:nth-child(3)')
.forEach(xTD=>
{
if (xTD.textContent==='')
{
let pTR = xTD.parentElement
pTR.removeChild(xTD)
pTR.cells[1].colSpan = 2
}
}
)
}
table { border-collapse: collapse; margin: 1em }
td { border: 1px solid grey; padding: .6em 1em}
thead { font-weight: bold }
<button id="Bt-Clean-col3">Remove empty Cols 3</button>
<table id="Table-3cols">
<thead>
<tr> <td>col 1</td> <td>col 2</td> <td>col 3</td> </tr>
</thead>
<tbody>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
<tr> <td>aa</td> <td>bb</td> <td></td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
<tr> <td>aa</td> <td>bb</td> <td></td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
</tbody>
</table>
explanations for: (arrow functions)
BtRemC3.onclick=_=>
{
MyTableBody
.querySelectorAll(`td:nth-child(3)`)
.forEach(xTD=>
{
this is like :
BtRemC3.addEventListener('click', function()
{
MyTableBody.querySelectorAll('td:nth-child(3)').forEach( function(xTD)
{
so, for the complete code this is :
const MyTableBody = document.querySelector('#Table-3cols tbody')
, BtRemC3 = document.querySelector('#Bt-Clean-col3')
;
BtRemC3.addEventListener('click', function()
{
MyTableBody.querySelectorAll('td:nth-child(3)').forEach( function(xTD)
{
if (/#E/.test(xTD.textContent) ) // previously (xTD.textContent==='')
{
let pTR = xTD.parentElement;
pTR.removeChild(xTD);
pTR.cells[1].colSpan = 2;
}
});
});
table { border-collapse: collapse; margin: 1em }
td { border: 1px solid grey; padding: .6em 1em}
thead { font-weight: bold }
<button id="Bt-Clean-col3">Remove Cols 3 with '#E'</button>
<table id="Table-3cols">
<thead>
<tr> <td>col 1</td> <td>col 2</td> <td>col 3</td> </tr>
</thead>
<tbody>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>blah blah blah#E blah </td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>blah blah#E blah </td> </tr>
<tr> <td>aa</td> <td>bb</td> <td>cc</td> </tr>
</tbody>
</table>
I also changed the test about your #E value
regular expression to test if xTD.textContent contain '#E'
please read How to check whether a string contains a substring in JavaScript?
There are a couple of things you need to do:
First, if you're using each, you should pass in the index and the value. Then use the value when getting tempHTML, and doing your modifications to the HTML.
$('table tbody tr td').each(function (i,f) {
var tempHTML = $(f).text();
if (tempHTML.indexOf("#E") !== -1) { //true
$(f).attr("colspan", 2);
$(f).next().remove();
}
});
table { border-collapse: collapse; margin: 1em }
td { border: 1px solid grey; padding: .6em 1em}
thead { font-weight: bold }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tr>
<td>ABC</td>
<td>DEF</td>
<td>GHI</td>
</tr>
<tr>
<td>AEC</td>
<td>D#EF</td>
<td>GHI</td>
</tr>
<tr>
<td>ABC</td>
<td>DEF</td>
<td>GHI</td>
</tr>
</table>

How to set the index for a dynamically changing table?

I have created a form where you can add or delete table rows using javascript and jQuery. I would like to know how I can obtain and set the index for each table row such that sequence is maintained even if I were to delete and element from the middle of the table. The table is of the form:
<thead>
<tr>
<th>Index</th>
<th>Name</th>
<th>Property</th>
<th>Edit/Delete</th>
</tr>
</thead>
<tbody>
<tr>
<td class="index">Index goes here (1)</td>
<td>NameOne</td>
<td>PropOne</td>
<td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
</tr>
<tr>
<td class="index">2</td>
<td>NameTwo</td>
<td>PropTwo</td>
<td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
</tr>
<tr>
<td class="index">3</td>
<td>NameThree</td>
<td>PropThree</td>
<td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
</tr>
</tbody>
Now what I want to achieve is if I were to delete the second row, the index of the previous third row should automatically change to 2 and if I were to add new element it should automatically take the index value of 3 and so on.
I tried to achieve this with:
function setIndex(){
$("td.index").each(function(index) {
$(this).text(++index);
});
}
But when I used the above function although the initial index when the elements were added printed properly the index wouldn't update properly when I called the function again after deleting or editing a row( I deleted the row using jQuery remove).
Also I am creating the new table rows with jQuery append().
I think that although I used remove() they don't get deleted completely as when I used a console.log("test") statement inside the setIndex() although "test" was only supposed to be printed twice(I had initially created 3 rows and deleted one of them) it go printed thrice signifying that there were 3 tr.index's.
Please help me solve the same.
You can use the CSS counter-reset and content properties.
The counter-reset property allows for automatic numbering of elements.
It works on any element.
The counter-reset property is used to reset a CSS counter to a given value.
It sets a named counter to a specific value.
body{
counter-reset: Serial; /* Set the Serial counter to 0 */
}
table{
border-collapse: collapse;
}
tr td:first-child:before{
counter-increment: Serial; /* Increment the Serial counter */
content:counter(Serial); /* Display the counter */
}
<table border="1">
<thead>
<tr>
<th>Automatic Serial number</th>
<th>Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td>
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td>
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td>
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td>
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td>
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td>
<td>Column 1</td>
<td>Column 2</td>
</tr>
<tr>
<td></td>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</tbody>
</table>
I'd suggest using the the CSS/Counter answer by #pravin-prajapati because it requires no JS overhead and will scale easily.
Was interested in what the problem was with your code because it looked fine to me so rebuilt it. Seemed to be working fine.
I'm guessing the problem is actually in the way you're attaching code to the .delete click, especially if you're adding new rows or recreating rows after an edit.
If you add new rows after the initial document.ready (or window.onload...) has attached the callback to the existing .delete elements, it will not automatically attach the callback to the new .delete elements.
In other words, don't do this in your init:
$('.delete').on('click', function(){
// do stuff
});
because that will only work for .delete elements that exist during the init. There are a few ways around this but an easy way is to listen for click events on a parent of the rows and then filter them to your actual target before running the callback. jQuery's on method makes this easy.
Below is an example with the table as the event listener.
EDIT:
If, for some reason, this isn't possible, you might look into using jQuery.clone() and setting withDataAndEvents and/or deepWithDataAndEvents to true like $('tr.template').clone(true, true);. This will copy the <tr> and any event handlers attached to it (first 'true') and any event handlers attached to any of its child elements (second 'true'). jQuery Clone Docs
$(document).ready(function(){
// your function, copied 100%
function setIndex(){
$("td.index").each(function(index) {
$(this).text(++index);
});
}
// set the index to begin with. Note the last three
// row indexes are actually empty in the sample HTML
setIndex();
// Move the click listener to the table.
$('table').on('click', '.delete', function(){
// remove the tr...
$(this).parents('tr').remove();
//... and reset the index
setIndex();
})
});
table {
font-family: sans-serif;
margin: 10px;
}
table td {
border: 1px solid #ccc;
padding: 10px;
}
.delete {
color: red;
cursor: pointer;
font-size: 80%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<th>Index</th>
<th>Name</th>
<th>Property</th>
<th>Edit/Delete</th>
</tr>
</thead>
<tbody>
<tr>
<td class="index">Index here</td>
<td>NameOne</td>
<td>PropOne</td>
<td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
</tr>
<tr>
<td class="index">2</td>
<td>NameTwo</td>
<td>PropTwo</td>
<td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
</tr>
<tr>
<td class="index">3</td>
<td>NameThree</td>
<td>PropThree</td>
<td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
</tr>
<tr>
<td class="index"></td>
<td>Name 4</td>
<td>Prop 4</td>
<td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
</tr>
<tr>
<td class="index"></td>
<td>Name 5</td>
<td>Prop 5</td>
<td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
</tr>
<tr>
<td class="index"></td>
<td>Name 6</td>
<td>Prop 6</td>
<td><span class="edit">Edit</span> <span class="delete">Delete</span></td>
</tr>
</tbody>
</table>
EDIT
Praven Prajapati's answer surprised me.
I didn't know that very cool CSS feature.
Praven's answer really is better.
And if you need to refer to that number in a JS/jQuery code... And can't get it because it's a not in DOM pseudo-element... Then use .index() for that particular part of your code! Let CSS work on the rest.
jQuery way:
You need to refer to a row index... Use the .index() method.
Then on .delete click (I'm sure you can delete the row), just call a function to update the row index cell using that method.
Same after appending a new row...
Important
Use delegation with .on() on the classes present in your table rows, since you add new rows that are not present on page load code parsing. ;)
That is a Will's catch. (See his answer)
function updateRowCount(){
$("table tbody tr").each(function(){
$(this).find(".index").html($(this).index());
});
}
// Run on load
updateRowCount();
$(document).on("click",".delete",function(){ // Use delegation here!
$(this).parents("tr").remove();
updateRowCount();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<thead>
<tr>
<th>Index</th>
<th>Name</th>
<th>Property</th>
<th>Edit/Delete</th>
</tr>
</thead>
<tbody>
<tr>
<td class="index"></td>
<td>NameOne</td>
<td>PropOne</td>
<td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
</tr>
<tr>
<td class="index"></td>
<td>NameTwo</td>
<td>PropTwo</td>
<td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
</tr>
<tr>
<td class="index"></td>
<td>NameThree</td>
<td>PropThree</td>
<td><span class="edit">Edit Icon</span> <span class="delete">Delete Icon</span></td>
</tr>
</tbody>
</table>

Tables with same height using JavaScript/CSS

I have a task where I am trying to align four tables in a page which is then printed.
But for some reason I am unable to align them with same height. I have a constraint to use only <table> <tr><td> structure.
Below is the current picture, because the data is variant in each table:
And I am trying to achieve is the below because no matter which table has how much data, all of them should be of equal height:
Any help would be great.
Take a look at flexboxes:
.wrapper {
display: flex;
}
table {
display: flex;
margin-left: 5px;
border: 1px solid #000000;
}
<div class="wrapper">
<table>
<tr>
<td>Foo</td>
</tr>
</table>
<table>
<tr>
<td>Foo</td>
</tr>
<tr>
<td>Foo</td>
</tr>
<tr>
<td>Foo</td>
</tr>
</table>
<table>
<tr>
<td>Foo</td>
</tr>
<tr>
<td>Foo</td>
</tr>
</table>
</div>
Get each table's total number of rows. Then get the maximum from these numbers :
<script type="text/javascript">
function max(tab_num_rows) { // you put the numbers into an array
var ret = tab_num_rows[0];
for (var i=1 ; i<tab_num_rows.length; i++) {
if (ret < tab_num_rows[i])
ret = tab_num_rows[i];
}
return ret;
}
</script>
Then you make a JQuery to loop each table to add extra rows until the maximum is reached.
Flexbox will be the typical solution for this (see the other answer), however, if you are also restricted to NOT use flexbox (for example because of compatibility requirements to older browsers), you can put all four tables into the cells of one "wrapper table", like
.wrap {
width: 100%;
}
td {
background: #eee;
vertical-align: top;
}
<table class="wrap">
<tr>
<td>
<table>
<tr>
<td>One</td>
</tr>
<tr>
<td>One</td>
</tr>
<tr>
<td>One</td>
</tr>
<tr>
<td>One</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td>Two</td>
</tr>
<tr>
<td>Two</td>
</tr>
<tr>
<td>Two</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td>Three</td>
</tr>
<tr>
<td>Three</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td>Four</td>
</tr>
<tr>
<td>Four</td>
</tr>
<tr>
<td>Four</td>
</tr>
</table>
</td>
</tr>
</table>

hr within table but full width

I made this block of code
<table>
{item.awards.map((obj,i) =>
<tbody>
<tr>
<td>Name</td>
<td>:</td>
<td>{obj.title}</td>
</tr>
<tr>
<td>Date</td>
<td>:</td>
<td>{obj.award}</td>
</tr>
{i !== item.awards.length-1 ? <hr /> : ''}
</tbody>
)}
</table>
It worked, every block has a separator (<hr/>) but the problem now is the hr length is not full width. I can't make the table 100% as it will effect the td.
It's invalid to have an hr as a direct child of tbody. tbody's content model only allows tr elements (and scripts). Even if it seems to work in one browser, there's no guarantee it will in another, or even in the next dot rev of the one where it used to work.
So you need to put the hr in a tr, which means putting it in a td or th:
{i !== item.awards.length-1 ? <tr><td colspan="3"><hr /></td></tr> : null}
You can then style the hr as necessary with CSS to make it as wide as you like. For instance (note the sep class on the separator rows and the CSS it applies to the row and the hr):
table {
border: 1px solid #ddd;
}
.sep hr {
position: absolute;
width: 100%;
margin-top: -0.1em;
}
.sep {
position: relative;
overflow: hidden;
height: 1em;
}
<table>
<tbody>
<tr>
<td>Name</td>
<td>:</td>
<td>{obj.title}</td>
</tr>
<tr>
<td>Date</td>
<td>:</td>
<td>{obj.award}</td>
</tr>
<tr class="sep">
<td colspan="3">
<hr />
</td>
</tr>
</tbody>
<tbody>
<tr>
<td>Name</td>
<td>:</td>
<td>{obj.title}</td>
</tr>
<tr>
<td>Date</td>
<td>:</td>
<td>{obj.award}</td>
</tr>
<tr class="sep">
<td colspan="3">
<hr />
</td>
</tr>
</tbody>
<tbody>
<tr>
<td>Name</td>
<td>:</td>
<td>{obj.title}</td>
</tr>
<tr>
<td>Date</td>
<td>:</td>
<td>{obj.award}</td>
</tr>
</tbody>
</table>
(Side note: null is a better choice than '' for the third operand of that conditional operator.)

Absolute position table cell (td) relative to table row (tr)

Is it possible to absolute position table cell (td) relative to table row (tr) containing that td.
For example consider html as below:
<table>
<tr>
<td>tr1 td 1</td>
<td>tr1 td 2</td>
<td class="last">tr1 td 3</td>
</tr>
<tr>
<td>tr2 td 1</td>
<td>tr2 td 2</td>
<td class="last">tr2 td 3</td>
</tr>
<tr>
<td>tr3 td 1</td>
<td>tr3 td 2</td>
<td class="last">tr3 td 3</td>
</tr>
</table>
and css as below:
tr{position:relative}
td.last{ position:absolute; left: 10px; top: 40px}
In above example, can I take out last td from tr and absolute position it relative to tr.
Edit: Its working in Firefox Version 33.0, but not working in Chrome Version 38. In chrome td positioned with respect to table and not with tr.
Please check the jsfiddle at http://jsfiddle.net/n5s53v32/2/ .
The browsers are very strict when it comes to tables. It does not work well when you get out of the scope of how tables are designed to work.
However, you can use a trick with fixed positioning to cheat the browser into not taking in account the missplaced table cell, since it is absolutelly off the normal flow:
Add a transform property to the table row, so it will act as a fixed position container. Choose one that will not have any visual impact, like transform: scale(1,1);
Set the table cell as position: fixed, and then you can move it relatively to the transformed row:
tr {
position:relative;
transform:scale(1,1);
}
td.last{
position:fixed;
left: 10px;
top: 40px;
}
<table>
<tr>
<td>td 1</td>
<td>td 2</td>
<td class="last">td 3</td>
</tr>
<tr>
<td>td 1</td>
<td>td 2</td>
<td class="last">td 3</td>
</tr>
<tr>
<td>td 1</td>
<td>td 2</td>
<td class="last">td 3</td>
</tr>
</table>
You can't move a cell away from the table (that I know of).

Categories