Responsive Bootstrap Table with Sticky Header - html

I have a table. The only CSS I am adding to the standard Bootstrap library is the following to implement the sticky header:
.table-sticky th {
background: #fff;
position: sticky;
top: -1px;
z-index: 990;
}
<div class="table-responsive p-0 mt-2">
<table class="table table-sm table-striped table-hover table-sticky">
<thead>
<tr>
<th>#</th>
<th>Header</th>
<th>Header</th>
<th>Header</th>
<th>Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>1,001</td>
<td>Lorem</td>
<td>ipsum</td>
<td>dolor</td>
<td>sit</td>
</tr>
<tr>
<td>1,002</td>
<td>amet</td>
<td>consectetur</td>
<td>adipiscing</td>
<td>elit</td>
</tr>
</tbody>
</table>
</div>
The sticky header worked until I wrapped the table in the div.table-responsive. How do I allow these to co-exist?

There's no way to co-exist a position: sticky inside a .table-responsive without re-implementing the latter.
My solution ended up to be using .table-responsive-sm to refuse this trade-off when a responsive table is certainly not needed.

In this case you need to code it yourself. Following JS will do the job:
var element = document.getElementById("fixed-thead");
var parentElement = element.parentElement;
window.addEventListener('scroll', () => {
var coordinates = parentElement.getBoundingClientRect();
if (coordinates.y < 0) {
element.style.transform = 'translate3d(0, ' + (-coordinates.y) + 'px, 0)';
} else {
element.style.transform = 'translate3d(0,0,0)';
}
});
And in your table mark thead with id="fixed-thead" like this:
<thead id="fixed-thead">
I use translate3d to enable hardware acceleration to make it smoother. If user uses wheel to scroll scroll event is triggering very ofter I recommend using debouncing of scroll events.
In case your table has column with fixed with you can use setting position: fixed and top: 0 instead of translate3d. But in case of a variable length translate3d is preferred.
In case you need to apply this code to multiple pages on page use following code:
function applyFixedHeader(element) {
var parentElement = element.parentElement;
window.addEventListener('scroll', () => {
var coordinates = parentElement.getBoundingClientRect();
if (coordinates.y < 0) {
element.style.transform = 'translate3d(0, ' + (-coordinates.y) + 'px, 0)';
} else {
element.style.transform = 'translate3d(0,0,0)';
}
});
}
var elements = document.querySelectorAll("{yourSelector, e.g. `.table-fixed` for the tables containing class `table-fixed`}");
elements.forEach(element => applyFixedHeader(element));

without any additionnal CSS, you can fix table head by removing table-responsive from the div and adding class="bg-white sticky-top border-bottom" class to table tag. Like this
<div class="table">
<table class="table">
<thead>
<tr>
<th class="bg-white sticky-top border-bottom">FirstName</th>
<th class="bg-white sticky-top border-bottom">LastName</th>
<th class="bg-white sticky-top border-bottom">Class</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td>
<td>Cooker</td>
<td>A1</td>
</tr>
<tr>
<td>David</td>
<td>Jeferson</td>
<td>A2</td>
</tr>
...
</table>
</div>

Related

Auto increment column number in html table boostrap 5?

I am trying to auto increment column with header ID, using reactjs bootstrap5.
The table loads data from a database.
<table className="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Tick</th>
<th>Percent</th>
<th>Ratio</th>
<th>Broke</th>
<th />
</tr>
</thead>
<tbody>
{ this.props.sett_list.map(dca_sett_list_item => (
<tr key={sett_list_item.id}>
<td>{sett_list_item.id}</td>
<td>{sett_list_item.ticker}</td>
<td>{sett_list_item.percent_down}</td>
<td>{sett_list_item.pe_ratio}</td>
<td><button className='btn btn-primary btn-sm'>BROKE</button></td>
<td><button onClick={this.props.deleteSettings_T.bind(this, sett_list_item.id)}className='btn btn-danger btn-sm'>Delete</button></td>
</tr>
)) }
</tbody>
</table>
I found an answer which directs towards using css:
table {
counter-reset: tableCount;
}
.counterCell:before {
content: counter(tableCount);
counter-increment: tableCount;
}
Do I make a custom.css and add the above code to get the functionality? Or is there a better way to do this in bootstrap 5?

Jquery - Expand/Collapse table rows by level

Given the following table/code, I'd like to add or adjust three items. I don't know Jquery well enough as I just learn programming within two months and this code was refer from other places and I made some adjustment for it. Anyone who could help me I will be very appreciate, I have try this expand and collapse function for two weeks but I still cannot get the result that I want so I post here to ask for help.
Here are the three items:
For the parent that do not have child will not show '+' when I click on the hide all, for example in my fiddle, the capital share and capital contribution does not have child below it but it still appear '+'. Please see the image attached
[Current Result & Expected Result]
When I show all, I want to show '-' for the parent or child that able to collapse as a group. Please see the image attached
[Expected Output Result]
After I click Hide All, when I click expand the Fixed Assets, It should only appear Building and Computer, not including the child of Building and Computer. When I click expand on the building, then it only expand the accum. dprn building. Please see the image attached [Expected Result]
Here is the table I'm using:
$('.collapse').on('click', function () {
//console.log($(this).attr('data-depth'));
var findChildren = function (tr) {
var depth = tr.data('depth');
return tr.nextUntil($('tr').filter(function () {
return $(this).data('depth') <= depth;
}));
};
var children = findChildren($(this));
if ($(children).is(':visible')) {
$(this).addClass("closed");
$(children).hide();
} else {
$(this).removeClass("closed");
$(children).show();
var children = findChildren($(".closed"));
$(children).hide();
}
});
$('#show_all').on('click', function () {
$("#mytable tr.collapse").removeClass("closed").show();
});
$('#hide_all').on('click', function () {
$("#mytable tr.collapse:not([data-depth='0'])").hide();
$("#mytable tr.collapse.level0").addClass("closed");
});
table td,
th {
border: 1px solid #eee;
font-family: Arial;
font-size: 16px;
}
.level0 td:first-child {
padding-left: 10px;
}
.level1 td:first-child {
padding-left: 40px;
}
.level2 td:first-child {
padding-left: 70px;
}
.level3 td:first-child {
padding-left: 100px;
}
.level4 td:first-child {
padding-left: 130px;
}
.level5 td:first-child {
padding-left: 160px;
}
.closed td:first-child::before {
content: "+";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<button id="show_all"><p>Show all</p></button>
<button id="hide_all"><p>Hide all</p></button>
<table class="table table-bordered" id="mytable" style="border-collapse: collapse">
<thead>
<tr>
<th colspan="5">Name</th>
<th>DR</th>
<th>CR</th>
</tr>
<tr data-depth="0" class="collapse level0">
<td colspan="5">CAPITAL SHARE</td>
<td>10,000</td>
<td>0.00</td>
</tr>
<tr data-depth="0" class="collapse level0">
<td colspan="5">CAPITAL CONTRIBUTION </td>
<td>200,000</td>
<td>12,000</td>
</tr>
<tr data-depth="0" class="collapse level0">
<td colspan="5">FIXED ASSETS</td>
<td>200,320</td>
<td>0.00</td>
</tr>
<tr data-depth="1" class="collapse level1">
<td colspan="5">BUILDING</td>
<td>3,222,000</td>
<td>0.00</td>
</tr>
<tr data-depth="2" class="collapse level2">
<td colspan="5">ACCUM. DPN-BUILDING</td>
<td>0.00</td>
<td>0.00</td>
</tr>
<tr data-depth="1" class="collapse level1">
<td colspan="5">COMPUTER</td>
<td>320,000</td>
<td>0.00</td>
</tr>
<tr data-depth="2" class="collapse level2">
<td colspan="5">ACCUM. DPN-COMPUTER</td>
<td>0.00</td>
<td>0.00</td>
</tr>
<tr data-depth="3" class="collapse level3">
<td colspan="5">ACCUM. DPN-COMPUTER</td>
<td>0.00</td>
<td>0.00</td>
</tr>
<tr data-depth="3" class="collapse level3">
<td colspan="5">ACCUM. DPN-COMPUTER</td>
<td>0.00</td>
<td>0.00</td>
</tr>
<tr data-depth="0" class="collapse level0">
<td colspan="5">INVESTMENT</td>
<td>0.00</td>
<td>0.00</td>
</tr>
<tr data-depth="1" class="collapse level1">
<td colspan="5">INVESTMENT IN ABC COMPANY</td>
<td>0.00</td>
<td>0.00</td>
</tr>
</thead>
</table>
You are missing one thing here. When you are adding a ".closed" class on collapse you are adding it to all the "#mytable tr.collapse.level0". Instead, write an (if statement) and only add ".closed" class to those elements who have a sibling whose class contains ".level1".
$("#hide_all").on("click", function() {
$("#mytable tr.collapse:not([data-depth='0'])").hide();
let a = document.querySelectorAll("#mytable tr.collapse.level0");
for (const iterator of a) {
if (iterator.nextSibling.nextSibling) {
if (iterator.nextSibling.nextSibling.className.includes("level1")) {
$(iterator).addClass("closed");
}
}
}
})
At the end, I finally found a plugins of treegrid that is more easily for me to use and understand. Anyone who need to use treegrid or treeview of table can use the plugins.
TreeGrid Plugins. If anyone want to use my above sample also can, but the disadvantage is the page will die when load data >300rows.

Angular: HTML Table export to csv file

I'm trying to export a painted html table completely dynamically into a csv file. I tried to implement several Scripts, functions.etc but I have not obtained the desired result. If anyone can help me, I'd be very grateful.
The information in the table is loading from a Request in "jsonData", It is important to clarify that I do not require to know the "Headers", only the information inside the table body:
ts Code:
jsonData:any;
_object = Object;
"jsonData" looks like:
[{
Subject_ID: "ACCT101",
First_Available_Date: "01/01/01",
....
},
{
Subject_ID: "510862185-X",
First_Available_Date: "06/04/19"..}....
]
HTML
<table>
<thead>
<tr>
<th *ngFor="let header of _object.keys(jsonData[0]); let i = index">{{header}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of jsonData; let i = index">
<td *ngFor="let objKey of _object.keys(row); let j = index">{{ row[objKey] }} </td>
</tr>
</tbody>
</table>
<button class="btn btn-submit btn-sm float-right" type="submit" (click)="export()">
Submit
</button>
Please try below code. this will extract data from table and download as csv.
<!DOCTYPE html>
<html>
<head>
<style>
td, table,th{
border: solid 1px black;
}
</style>
</head>
<body>
<table id="data_table">
<thead>
<tr>
<th>Subject_ID</th>
<th>First_Available_Date</th>
</tr>
</thead>
<tbody>
<tr>
<td>ACCT101</td>
<td>01/01/01</td>
</tr>
<tr>
<td>510862185-X</td>
<td>06/04/19</td>
</tr>
<tr>
<td>New data</td>
<td>New data 1</td>
</tr>
</tbody>
</table>
<script>
function exportit() {
var csv='';
table=document.getElementById("data_table");
tr=table.children[0].children[0];
for(i=0;i<tr.children.length;i++)
{
csv+=tr.children[i].innerText+",";
}
csv=csv.substring(0,csv.length-1)+"\n";
tbody=table.children[1];
for(i=0;i<tbody.children.length;i++)
{
for(j=0;j<tbody.children[i].children.length;j++)
{
csv+=tbody.children[i].children[j].innerText+",";
}
csv=csv.substring(0,csv.length-1)+"\n";
}
csv=csv.substring(0,csv.length-1)+"\n";
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csv);
hiddenElement.target = '_blank';
hiddenElement.download = 'data.csv';
hiddenElement.click();
}
</script><br>
<button onclick="exportit()">export</button>
</body>
</html>

Print preview is not displaying the right layout

I need to print my html so I lay out the way I want the print out to be. But when I click on the print button and get directed to the print preview, the layout are all messed up. Please see my code below:
in html
<div id="print-section" *ngIf="propertyLedger">
<h2>SOSA | PMIS</h2>
<hr>
<table class="table borderless">
<tr>
<th class="col-md-2">Transaction Date</th>
<td>{{dateToday | date}}</td>
</tr>
<tr>
<th class="col-md-2">Collecting Agent</th>
<td>{{propertyLedger.CollectinAgentName}}</td>
</tr>
<tr>
<th class="col-md-2">Unit Name</th>
<td>{{propertyLedger.UnitName}}</td>
</tr>
<tr>
<th class="col-md-2">Payment Type</th>
<td>{{propertyLedger.PaymentType}}</td>
</tr>
<tr>
<th class="col-md-2">O.R. Number</th>
<td>{{propertyLedger.ORNumber}}</td>
</tr>
<tr>
<th class="col-md-2">Remarks</th>
<td>{{propertyLedger.Remarks}}</td>
</tr>
</table>
<hr>
<h3>Details</h3>
<hr>
</div>
<button
class="btn btn-primary" (click)="print()">Print</button>
in the ts file
print() {
let printContents, popupWin;
printContents = document.getElementById('print-section').innerHTML;
popupWin = window.open('', '_blank', 'top=0,left=0,height=100%,width=auto');
popupWin.document.open();
popupWin.document.write(`
<html>
<head>
<title>Print tab</title>
<style>
//........Customized style.......
</style>
</head>
<body onload="window.print();window.close()">${printContents}</body>
</html>`
);
popupWin.document.close();
}
in my layout
as soon as I click the print =>
in my print preview
It seems that the texts in the left part of the table goes to the center. Can you help me with this please. If you have a way to get my desired layout without any table that will be best! Thank you so much.
Just read up. A default in HTML has these properties.
th {
display: table-cell;
vertical-align: inherit;
font-weight: bold;
text-align: center
}
You have to change it to something like this:
th {
display: table-cell;
vertical-align: inherit;
font-weight: bold;
text-align: left;
}
Moreover, if you are not using HTML5 you can also make the tag something like this:
<th align="left" class="col-md-2">Unit Name</th>

Fixed columns width in a table which columns can be searched and hidden

How to apply width to the table columns to avoid changing column width when no results found inside it. Columns in this table can also be hidden dynamically so I can't set fixed width on th element.
<table>
<thead>
<tr>
<th>col1</th>
<th>col2 <i>Filter:</i> <input type="text" ng-model="searchText"></th>
<th ng-hide="hideCol3">col3 <button ng-click="hideCol3 = !hideCol3">Hide column</button></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="d in data | filter: searchText">
<td>{{d.col1}}</td>
<td>{{d.col2}}</td>
<td ng-hide="hideCol3">{{d.col3}}</td>
</tr>
</tbody>
</table>
Columns might be hidden in the table. So for example if I go to the plunker example, hide col3, and search for none existing text, lets say 'abc' the width of the table columns width changes.
I would like table to adjust width when some column is hidden (it is doing it now), but when I'm filtering and there are no results I don't want column width to change
plunker
Here is a way to do it https://jsfiddle.net/kblau237/qkn1z0LL/2/
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>NNIndex</title>
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script data-semver="1.2.13" src="http://code.angularjs.org/1.2.13/angular.js" data-require="angular.js#1.2.x"></script>
<script src="~/Scripts/app.js"></script>
<style>
th, td {
border: 1px solid black;
}
table {
width: 100%;
}
</style>
<script type="text/javascript">
$(function () {
//http://stackoverflow.com/questions/1636201/is-the-order-objects-are-return-by-a-jquery-selector-specified
var cols = $(".manageWidth"); //access each ids with [0]..[2] usejquery each
$.each(cols, function (index, value) {
$('<input>').attr({
type: 'hidden',
class: 'hdnDyn',
id: "hdnDyn" + index,
value: window.getComputedStyle(cols[index], null).getPropertyValue("width")
}).appendTo('body');
});
$("input").keyup(function () {
KeepWidthSame();
});
$(".col3").click(function () {
KeepWidthSame();
});
function KeepWidthSame() {
$("table").css("width", "auto");
var cols = $(".manageWidth");
$.each(cols, function (index, value) {
$(this).css("width", $(".hdnDyn")[index].value);
});
}
})
</script>
</head>
<body ng-app="plunker" ng-controller="MainCtrl">
<table>
<thead>
<tr>
<th class="manageWidth">col1</th>
<th class="manageWidth">col2 <i>Filter:</i> <input type="text" ng-model="searchText"></th>
<th class="manageWidth col3" ng-hide="hideCol3">col3 <button ng-click="hideCol3 = !hideCol3">Hide column</button></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="d in data | filter: searchText">
<td>{{d.col1}}</td>
<td>{{d.col2}}</td>
<td ng-hide="hideCol3">{{d.col3}}</td>
</tr>
</tbody>
</table>
</body>
</html>