I'm sure this has to be documented somewhere, but I'm so new at html that I can't figure out where to look, so feel free to just direct me to the correct reading if it's something obvious. As a note, I code in several languages, but mostly things like python or LaTeX, nothing like html. I have included my attempt at the bottom, but tried to include what I could in terms of my design setup and what problems I am running into. Any solutions to any of the listed problems would be appreciated :)
The setup;
I need to form gigantic table of information that will be populated based on a JSON string given on another webpage. I am currently using javascript to parse the JSON string and form a single global object of my own that has all the relevant information (the JSON string that the user will plug in is huge and I only need a relatively small portion of it). The created object has the following form;
cruData = {hero:[{"cruID":0,"EP":0,"Owned":"No","GearName1":"Empty","GearRarity1":"Empty","GearName2":"Empty","GearRarity2":"Empty","GearName3":"Empty","GearRarity3":"Empty"}]}
With the cruID iterating from 0 to 103 (plans to make it at least a couple hundred higher at some point). All the strings will be populated with strings, and EP/ID are true numbers.
Proposed Setup:
My original plan was to pre-make the (blank) table structure on the separate web page, and then after grabbing the JSON file and parsing it into the cruData object, to populate that table with relevant info (technically ID 1 to 103 for now, no 0 ID in this table) upon page-load when the user goes to it.
The Problems:
1) My concern with this method of forming the table upon page load is that the table is sizable and I'd really rather not have the table get formed on every page load. Is there a way to save this locally so that it gets formed once and then it's in cache?
2) Assuming pre-formatting the table really is a good idea (feel free to tell me I'm stupid for thinking so heh), I will have some 350+ cells (all the 'rarity' cells) that are all identical dropdown boxes. Thus I'd like to make a class type for cells that are a drop-down selection list, but can't figure out how to do that in the style section. I can get a specific td to work, but not a class of td to work. Again, I'm sure this is because I just don't really understand how defining classes or the style section works (I haven't used a class-inheritance system coding before).
To be specific, I would like there to be a drop-down box in 3 different cells of every row of this 100+ row table. They are all identical drop-down selections. To do this, would a form be better? Is there a way to define the drop-down selections one time in a class, and then just point to the class in each cell so that I don't have to re-add the selection options to 3 cells of every single row?
3) I would like each of the rarity cells and the EP cell for each row to be editable by the human, having the new value saved for calculations elsewhere. My idea for this would be to copy the cruData object to a cruComp object that is used for computations, then have that get overwritten by player input; that way I can add a "reset" button on the table page to fix everything back after user remorse kicks in :)
4) I want to color row blocks in the table that correspond to something in the game I'm recording data for. Right now I need every 5 rows to be blocked together in a color after the heading, but that 5 may universally change to another number. Is there a way to set a dynamic variable for this so that I can change one number instead of a bunch? Also in my attempt below it works for the first like 50-70 rows and then starts having all kinds of weird behavior, but I can't figure out why?
[ Solved, found the thead tbody tags which was the problem ]
5) Not a real problem but, is there a way to just have x rows get created without each one being created by hand? The only wrinkle is that the ID numbers in the example given below will have all the numbers 1-103+ but not in that order. I figured I could do some sort of vector that has the numbers in the correct order and then pull the consecutive numbers from that vector in order to number them in order, but I don't know if you can create rows using a FOR loop.
Thanks for the help!
My Attempt: Warning, I'm sure this will make anyone that knows what they are doing die inside, as I keep saying I really have little to no idea what I'm doing in this language. Hence me starting with a silly project to learn by doing :)
<!DOCTYPE html>
<html>
<head>
<title>COTLI Calculator and Ref</title>
<!-- <link rel="stylesheet" href="StyleRef.css"> -->
<script>
function Load_Data() {
var i = 0;
/*
<td>The Bush Whacker</td>
<td id="CrEP_1">Blank</td>
<td id="CrGN1_1">Blank</td>
<td id="CrGR1_1">Blank</td>
<td id="CrGN2_1">Blank</td>
<td id="CrGR2_1">Blank</td>
<td id="CrGN3_1">Blank</td>
<td id="CrGR3_1">Blank</td>
<td id="CrUnLock_1">Blank</td>
*/
for (i = 1; i < lootTable.hero.length; i++) {
"crEP_"+i = cruData.hero[i].EP;
};
}
window.onload = Load_Data;
</script>
</head>
<body style="background-color:lightgrey; text-align:center;">
<style>
<!-- Below is the color coding for the rows broken into batches of 4 bench slots at a time. The offset value is because of how the rows are counted and the "header" row. -->
<!-- tr:nth-child(35n+1) {background-color: #4682b4;} -->
<!-- tr:nth-child(21n+1) {background-color: #e36f8a;} -->
tr:nth-child(20n+2) {background-color: #4682b4;}
tr:nth-child(20n+3) {background-color: #4682b4;}
tr:nth-child(20n+4) {background-color: #4682b4;}
tr:nth-child(20n+5) {background-color: #4682b4;}
tr:nth-child(20n+6) {background-color: #4682b4;}
tr:nth-child(20n+7) {background-color: #3abda0;}
tr:nth-child(20n+8) {background-color: #3abda0;}
tr:nth-child(20n+9) {background-color: #3abda0;}
tr:nth-child(20n+10) {background-color: #3abda0;}
tr:nth-child(20n+11) {background-color: #3abda0;}
tr:nth-child(20n+12) {background-color: #e09e87;}
tr:nth-child(20n+13) {background-color: #e09e87;}
tr:nth-child(20n+14) {background-color: #e09e87;}
tr:nth-child(20n+15) {background-color: #e09e87;}
tr:nth-child(20n+16) {background-color: #e09e87;}
tr:nth-child(20n+17) {background-color: #93b881;}
tr:nth-child(20n+18) {background-color: #93b881;}
tr:nth-child(20n+19) {background-color: #93b881;}
tr:nth-child(20n) {background-color: #93b881;}
tr:nth-child(20n+1) {background-color: #93b881;}
table {
border-collapse: collapse;
border: 5px solid black;
tex-align:center;
}
th {
padding: 20px;
border: solid black 3px;
}
td {
padding: 20px;
border: solid black 1px;
}
td.rarity {
<select>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
}
</style>
<h1>
Crusader Table
</h1>
<table align="center">
<tr style="background-color:#e36f8a; text-align:center;">
<th>Crusader Name</th>
<th>EP</th>
<th>Gear 1 Name</th>
<th>Gear 1 Rarity</th>
<th>Gear 2 Name</th>
<th>Gear 2 Rarity</th>
<th>Gear 3 Name</th>
<th>Gear 3 Rarity</th>
<th>Unlocked</th>
</tr>
<!-- Below is the master table for Crusaders. Tags are: CrEP_# for EP, CrGN1_# for Gear 1 name, CrGR1_# for Gear 1 Rarity (similarly gear 2 and 3) and CrUnlock_# for unlock, where # is Crusader ID. -->
<!-- Bench One
The Bush Whacker - 1
RoboRabbit - 38
Graham The Driver - 66
Warwick the Warlock - 75
Slisiblyp, the Alien Scientist - 89
-->
<tr>
<td>The Bush Whacker</td>
<td id="CrEP_1" value=CruData.hero[1].EP> </td>
<td id="CrGN1_1">Blank</td>
<td class="rarity" id="CrGR1_1"></td>
<td id="CrGN2_1">Blank</td>
<td id="CrGR2_1">Blank</td>
<td id="CrGN3_1">Blank</td>
<td id="CrGR3_1">Blank</td>
<td id="CrUnLock_1">Blank</td>
</tr>
Without doing it for you - here are the angles that I would solve these problems
You can cache it locally to a cookie as a string of the literal HTML, but the process really isn't that expensive. If the length is a concern - maybe think about breaking it down into rendering the first X amount of elements.
Create a class for the cell. <td class='dropdown-cell'> [your data] </td> and then in your css: .dropdown-cell{[your css rules} will format only the table cells that have the class dropdown-cell. edit: CSS classes work the same way they would if it was a <td class="custom-class"> or <select class="custom-class"> or <option class="custom-class> whatever element you put it on will inherit that styling as specified by .custom-class in the CSS.
.dropdown{
height: 50px;
width: 200px;
background-color: gray;
}
.dropdown-option{
background-color: lightgray;
}
<select class="dropdown">
<option class="dropdown-option">A</option>
<option class="dropdown-option">B</option>
<option class="dropdown-option">C</option>
</select>
If I'm understanding it right, if you want these edits to be bound to some object. You should create a class for each row, and then run a function onChange or onClick or onKeyup of these editable events and modify the related Object.
class SomeObject{
constructor(id, name, someVal){
this.id = id;
this.name = name;
this.someVal = someVal;
}
}
var numberOfObjects = 5;
var yourObjectsArray = [];
//Create i number of objects and add them to an array of your row objects
//This would be done by looping through your JSON
for(var i = 0; i < numberOfObjects; i++){
yourObjectsArray.push(new SomeObject(i, "Object " + i, "Some Value"));
}
//Build your table
for(var i = 0; i < yourObjectsArray.length; i++){
//The syntax below is Jquery - I suggest using it for event bindings and DOM manipulation
$('#output-table').append('<tr><td class="id">'+ yourObjectsArray[i].id +'</td><td>'+ yourObjectsArray[i].name +'</td><td class="change-val">'+ yourObjectsArray[i].someVal +'</td></tr>');
}
//Bind an event to a click or any jquery event handler
$(document).on('click', '.change-val', function(){
//Get the ID of the row that you clicked
var id = $(this).closest('tr').find('.id').text(); //use value or something else
//Modify the text in the table
var newVal = "New Value";
$(this).text(newVal);
//Parse the array of objects to find the one you need to modify
for(var i = 0; i < yourObjectsArray.length; i++){
if(yourObjectsArray[i].id == id){
yourObjectsArray[i].someVal = newVal;
}
}
//Prove that your object value changed not just the text
$('#output').html("<br>");//clear the output
for(var i = 0; i < yourObjectsArray.length; i++){
$('#output').append('ID: ' + yourObjectsArray[i].id + " Value: " + yourObjectsArray[i].someVal + "<br>");
}
});
table {
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click on a value cell below to change it
<table id="output-table">
<thead>
<tr>
<td>ID</td>
<td>Name</td>
<td>Value</td>
</tr>
</thead>
</table>
<div id="output"></div>
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName add a class to each row and then in a for loop, add conditional formatting for row[i] modulus 5.
You can create rows using a for loop, you just have to give the Javascript the HTML formatting and then use an append to add it to the end https://www.w3schools.com/jsref/met_node_appendchild.asp
I'd recommend Jquery for your DOM manipulation as it makes drilling down to specific ID's, classes, and elements very easy.
Related
I am creating a table in plain JavaScript, and filling it up using onClick listener event in two ways - either clicking on a external button, or clicking on one of the cells itself; and in both ways, calling a function to randomly assign values to some of the cells. I am not able to hide the contents of my table cells using either of the methods available- display: none, and visibility: hidden. But I am still able to do it using fontSize = 0., wherin another problem crops-up viz the cells borders are lost. Thus my problem is that I want to hide my table cells contents whilst rendering values in them withput affecting the table structure.
I have already tried conventional methods available viz.
1) td {display: none;}, and td {visibilty: hidden}
2) I have also tried inline CSS style method to hide the cell contents, but all these methods blank the table itself i.e. oblivate the cell borders themselves.
3) When I use:
document.getElementById('myelement').getElementsByTagName('td')[n[i]].style.fontSize = 0; I am able to hide the contents, but then cell borders are lost.
<body>
<div class="container-fluid">
<table id="myelement">
<tr>
<td> </td>
... .... ...
<td> </td>
</tr>
</table>
</div>
</body>
<script>
...
for(let i=0;i<mycount;i++){
td[n[i]].firstChild.nodeValue='X';
document.getElementById('myelement').getElementsByTagName('td')
[n[i]].style.fontSize = 0;
}
...
</script>
All the techniques available blank the table itself i.e. oblivate the cell borders themselves. The expected result is intact table structure when hiding the cells contents.
Something like this?
Cell with class="hidden" will appear to be empty but you can change background: #fff; to any other color you want
td.hidden {
position: relative;
}
td.hidden:after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: #fff;
z-index: 10;
}
<table border=1>
<tr>
<td class="hidden">asd</td>
<td>zxc</td>
<td>test</td>
</tr>
<tr>
<td>asd</td>
<td class="hidden">zxc</td>
<td>test</td>
</tr>
<tr>
<td>asd</td>
<td>zxc</td>
<td class="hidden">test</td>
</tr>
</table>
I am using Awesome Tables (which I will refer to as AT) to take data from a Google Sheets document and present the data in a format I can embed into a website. I have used a HTML template in the sheets to display the data in the AT form, which utilises inline CSS formatting. The template is only for the table output and as such, only the <body> element exists for that table - i.e. I have no access to <head> section, etc.
I have a piece of data (${"Status"}) pulled in from Google Sheets that can insert one of three text outputs: Active, Delivered or Cancelled. This is called to the output by:
<div style="display:inline-block;color:rgb(87, 87, 87);font-size: 14px;padding: 10px 10px 10px 0;flex-shrink: 0;margin-right: auto;text-transform: capitalize;">
<p><b>Name:</b> ${"Name"}</p>
<p><b>Start Date:</b> ${"Start Date"}</p>
<p><b>Completed Date:</b> ${"Completed Date"}</p>
<p><b>Status:</b> ${"Status"}</p>
</div>
I would like to color format the output text of ${"Status"} so that "Active" = orange, "Cancelled" = red and "Delivered" = green but not 100% sure how to do this. I have read that I probably need to use some sort of JavaScript to achieve this, but to be honest, do not know where to start.
Any help appreciated.
So, following on from the response received, here is my first attempt of writing JavaScript after a bit of online research. Am I heading along the right track?
var jobStatus = "${"Status"}";
if (jobStatus = "Delivered") {
document.getElementById("status").style.color = "green";
} else if (jobStatus = "Active") {
document.getElementById("status").style.color = "orange";
} else {
document.getElementById("status").style.color = "red";
}
If you can edit the html template, I would suggest to use css classes to apply the colors. Therefore just apply the value also as the css class and create a bit of css to format your text (or what ever) how you want it:
<div style="display:inline-block;color:rgb(87, 87, 87);font-size: 14px;padding: 10px 10px 10px 0;flex-shrink: 0;margin-right: auto;text-transform: capitalize;">
<p><b>Name:</b> ${"Name"}</p>
<p><b>Start Date:</b> ${"Start Date"}</p>
<p><b>Completed Date:</b> ${"Completed Date"}</p>
<p class="${"Status"}"><b>Status:</b> ${"Status"}</p>
</div>
The css is static. You can put it in the header section of your document (I understood, you do not have dynamic access to that, is that assumption correct?). Otherwise some js to append it to the body would work as well.
.Active {
color: orange;
}
.Cancelled {
color: red;
}
.Delivered {
color: green;
}
I have to build a table with +450 row / +90 columns dynamically and show it in Internet Explorer. I've been heavily optimizing for the last two days, and ended up creating a collection of table rows as a very long string and assigning it to the innerHTML of the tBody of the table.
it works just fine in Chrome/Mozilla, a reflow takes about .2 seconds, but it's very slow in Internet Explorer. it takes about 4 seconds to render (i say "about" because if the console is open (for time measurement) it takes 19 seconds to render). Another problem is that innerHTML is not even supported in IE9 and below. So the question is: What's the fastest way to render a whole lot of HTML as fast as possible in IE9?
HTML Sample:
<tr class="data-row" ><td class="hidden" style="width: -21px; padding:
10px;">"1"</td><tdclass="structureCatagory" style="width: 119px; padding:
10px;">"0381"</td><td class="structureCatagory" style="width: 139px;
padding: 10px;">"Some text"</td><td class="structureCatagory"
style="width: 139px; padding: 0px;"><img src="/Content/Images/info.png"
onclick="Interface.OnImageClicked($(this))" ></td>...
And so forth for a total of 4178521 characters.
Javascript:
function Update() {
var displayData = Model.GetData();
if (displayData == undefined || displayData.length == 0)
return false;
var rows = "", len = displayData.length;
for (var i = 0; i < len; i++) rows += GetRow(displayData[i]);
//until here it's very fast
GlobalQueries.dataTableBody[0].innerHTML = rows;
//^ this line takes alot of time
return true;
}
Thanks in advance!
Edit: The table itself:
<div class="grid">
<table class="fixed">
<tbody></tbody>
</table>
</div>
<style>
.grid { margin-top: 240px; margin-left: 10px; }
.grid td, .header-row td { border: 1px solid black; }
table.fixed { table-layout: fixed; }
</style>
I think only lazy rendering will help you here. This way you will reduce the amount of HTML nodes on the page and make it lighter. You don't need to render rows that are not visible on the screen...
There are examples with jQuery, React, Polymer ... and so on.
To make the paint look faster you can batch the inserts and not insert the entire table at once but in chunks with methods like requestAnimationFrame.
To modify the database format SQL uses two functions called PIVOT and UNPIVOT. I was wondering, is there a way of achieving the same thing using a script on the client side?
Let's take this table for example:
Rowa. pro_img#1|${product.name}|${product.price}|${product.description}
Rowb. prod_img#2|${product.name}|${product.price}|${product.description}
Rowc. prod_img#3|${product.name}|${product.price}|${product.description}
Rowd. pro_img#4|${product.name}|${product.price}|${product.description}
As you already know, every html table with dynamic data will print rows. What i'm trying to do is having an html table with dynamic data printing columns so I can display a nice product listing like in this example:
Product_img#1| Product_img#2| Product_img#3|
Product_name| Product_name| Product_name|
Product_price| Product_price| Product_price|
Product_description| Product_description| Product_description|
I'm trying to display 3 objects per row. Is there a way of doing this with a simple javascript?
I'm trying to achieve exactly the same thing as Vans (example here)!
Edited New code
<div id="product_container">
<c:forEach var="product" items="${categoryProducts}" varStatus="iter">
<div id="product_image"><a href="viewProduct?${product.id}">
<img class="img" alt="" src="${initParam.productImagePath}${product.id} (1).jpg" /></a></div>
<div id="product_name">${product.name}</div>
<div id="product_price">${product.price}</div>
<div id="add_toList"><form id="wishlistForm" action="addToWishlist" method="post">
<input name="productId" value="${product.id}" type="hidden">
<input class="submit" value="<fmt:message key='AddToWishlist'/>" type="submit">
</form></div>
<div id="add_toCart"><form id="cartForm" action="addToCart" method="post">
<br>
<br>
<input name="productId" value="${product.id}" type="hidden">
<input class="submit" value="<fmt:message key='AddToCart'/>" type="submit">
</form></div>
</c:forEach>
</div>
jsfiddle DEMO
This function takes an HTML table and returns a table which has the given table's rows and columns swapped.
Example input:
----------------
A1 | A2 | A3
----------------
B1 | B2 | B3
----------------
Output:
---------
A1 | B1
---------
A2 | B2
---------
A3 | B3
---------
Javascript:
function convertTable(tbl) {
var rows = tbl.rows.length;
var cols = tbl.rows[0].cells.length;
var tbl2 = document.createElement('table');
for (var i = 0; i < cols; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < rows; j++) {
var td = document.createElement('td');
var tdih = tbl.rows[j].cells[i].innerHTML;
td.innerHTML = tdih;
tr.appendChild(td);
}
tbl2.appendChild(tr);
}
return tbl2;
}
You could create custom divs for each object:
function generateProductDiv(product) {
return
'<div class="product">' +
'<img src="' + product.image + '">' +
'...' +
'</div>';
}
Create a div for each product put them in a parent divs and style them with css (the CSS property display: table; and table-* might be of interest to you if you want to do it this way, another possibility is to use libraries).
The easier solution is just to put those divs inside the cells of a table although you should only use tables if you really want to display tabular data.
You seem to have JSP code, not Javascript, here is how to generate the forms in JSP (btw. JSP is the same as Java Servlets, just another way of writing it, simply put: JSP = html with Java, Servlet = Java with html). Using forms instead of divs because that seems to be what you want:
<c:forEach var="product" items="${categoryProducts}" varStatus="iter">
<form id="wishlistForm" action="addToWishlist" method="post">
...
<a href="viewProduct?${product.id}">
<img class="img" alt="" src="${initParam.productImagePath}${product.id} (1).jpg" />
</a>
...
</form>
</c:forEach>
The best (easiest, most maintainable, most semantic) way to do this is pure CSS. You shouldn't have to change the markup at all.
And it's actually pretty easy.
You just have to get the browser to stop treating it like table data. The key is the display: CSS property.
display defaults to table-row for <tr> elements and table-cell for <td> elements, which makes sense. You have to "break" that property.
tr, td {
display: block;
}
In other words, you're telling the browser to "display table row and table data elements just like any other block-level element (like a div, for example)".
This gets you almost all the way there. Now you have to get the table "rows" (which are now being laid out like divs) to stack next to each other, instead of on top of each other.
tr {
float: left;
}
Now, you end up with the possibility of the new "rows" not being the same height. If, for example, your product images are not all the same height, or if your descriptions are different lengths, you'd end up with the table being way out of alignment.
So, you need to give your <td>s all the same height.
td{
height: 150px; /*or whatever height you want*/
}
So, the full CSS code looks like this:
tr {
display: block;
float: left;
}
td {
display: block;
height: 150px;
}
Obviously, this is only the most basic code to get your foot in the door. In the real world, you'd give each type of <td> its own height, something like:
td.product-image {
height: 150px;
}
td.product-description {
height: 70px;
}
td.product-price {
height: 40px;
}
Here's a codepen that puts it all together: table data pivoted
Also, here's some more info on the CSS display property if you're interested.
Best of luck. Feel free to ask in comments if you have any further questions.
I have a dynamic table in my web page that sometimes contains lots of rows. I know there are page-break-before and page-break-after CSS properties.
Where do I put them in my code in order to force page breaking if needed?
You can use the following:
<style type="text/css">
table { page-break-inside:auto }
tr { page-break-inside:avoid; page-break-after:auto }
</style>
Refer the W3C's CSS Print Profile specification for details.
And also refer the Salesforce developer forums.
Wherever you want to apply a break, either a table or tr, you needs to give a class for ex. page-break with CSS as mentioned below:
/* class works for table row */
table tr.page-break{
page-break-after:always
}
<tr class="page-break">
/* class works for table */
table.page-break{
page-break-after:always
}
<table class="page-break">
and it will work as you required
Alternatively, you can also have div structure for same:
CSS:
#media all {
.page-break { display: none; }
}
#media print {
.page-break { display: block; page-break-before: always; }
}
Div:
<div class="page-break"></div>
I have looked around for a fix for this. I have a jquery mobile site that has a final print page and it combines dozens of pages. I tried all the fixes above but the only thing I could get to work is this:
<div style="clear:both!important;"/></div>
<div style="page-break-after:always"></div>
<div style="clear:both!important;"/> </div>
Unfortunately the examples above didn't work for me in Chrome.
I came up with the below solution where you can specify the max height in PXs of each page. This will then splits the table into separate tables when the rows equal that height.
$(document).ready(function(){
var MaxHeight = 200;
var RunningHeight = 0;
var PageNo = 1;
$('table.splitForPrint>tbody>tr').each(function () {
if (RunningHeight + $(this).height() > MaxHeight) {
RunningHeight = 0;
PageNo += 1;
}
RunningHeight += $(this).height();
$(this).attr("data-page-no", PageNo);
});
for(i = 1; i <= PageNo; i++){
$('table.splitForPrint').parent().append("<div class='tablePage'><hr /><table id='Table" + i + "'><tbody></tbody></table><hr /></div>");
var rows = $('table tr[data-page-no="' + i + '"]');
$('#Table' + i).find("tbody").append(rows);
}
$('table.splitForPrint').remove();
});
You will also need the below in your stylesheet
div.tablePage {
page-break-inside:avoid; page-break-after:always;
}
this is working for me:
<td>
<div class="avoid">
Cell content.
</div>
</td>
...
<style type="text/css">
.avoid {
page-break-inside: avoid !important;
margin: 4px 0 4px 0; /* to keep the page break from cutting too close to the text in the div */
}
</style>
From this thread: avoid page break inside row of table
When converting to PDF with SelectPdf I couldn't get a group of rows to stay together. Tried to put them in a <div style="break-inside: avoid;"> but that didn't work.
Nothing was working until I found this: https://stackoverflow.com/a/27209406/11747650
Which made me rethink my logic and place the things I didn't want to split inside a <tbody>.
<table>
<thead style="display: table-header-group;">
<tr>
<th></th>
</tr>
</thead>
-- Repeating content --
<tbody style="break-inside: avoid;">
-- First row from group --
<tr>
<td> Only shown once per group </td>
</tr>
-- Repeating rows --
<tr>
<td> Shown multiple times per group </td>
</tr>
</tbody>
This results in a table that has multiple <tbody> but that's something that is completely fine as many people use this exact pattern to group together rows.
If you know about how many you want on a page, you could always do this. It will start a new page after every 20th item.
.row-item:nth-child(20n) {
page-break-after: always;
page-break-inside: avoid;
}
I eventually realised that my bulk content that was overflowing the table and not breaking properly simply didn't even need to be inside a table.
While it's not a technical solution, it solved my problem to simply end the table when I no longer needed a table; then started a new one for the footer.
Hope it helps someone... good luck!
Here is an example:
Via css:
<style>
.my-table {
page-break-before: always;
page-break-after: always;
}
.my-table tr {
page-break-inside: avoid;
}
</style>
or directly on the element:
<table style="page-break-before: always; page-break-after: always;">
<tr style="page-break-inside: avoid;">
..
</tr>
</table>
We tried loads of different solutions mentioned here and elsewhere and nothing worked for us. However we eventually found a solution that worked for us and for us it seems to somehow be an Angular issue. I don't understand why this works, but for us it does and we didn't need any page break css in the end.
#media print {
ng-component {
float: left;
}
}
So just hoping this helps someone else as it took us days to fix.
You should use
<tbody>
<tr>
first page content here
</tr>
<tr>
..
</tr>
</tbody>
<tbody>
next page content...
</tbody>
And CSS:
tbody { display: block; page-break-before: avoid; }
tbody { display: block; page-break-after: always; }