Here is my solution to a similar issue. I needed to generate a printable page and display information from a table in our database. The appearance of the page required me to have 3 fixed-size rows and 3 fixed-size columns per row.
I added styling to give it a grid-like appearance, but here is how I generated the table with page breaks after 3 rows:
This is where I have the table setup in the HTML (cshtml) file:
<table id="employee-table" class="m-auto">
</table>
Here is my JavaScript function which is located in an external JS file (don't forget to reference the file in your HTML if that's how you're setting it up)
function GeneratePrintablePDF(employeeData) {
document.getElementById('loader').classList.add('hidden'); //this is just the "loading" spinner for when the page is loading
dataContainer = document.getElementById('employee-table');
numberOfRows = employeeData.length / 3;
if (employeeData % 3 != 0) {
numberOfRows = Math.trunc(numberOfRows);
numberOfRows += 1;
}
var emp = 0;
do
{
for (var row = 1; row <= numberOfRows; row++) {
var tableRowDiv = document.createElement('div');
dataContainer.appendChild(tableRowDiv);
var tableRow = document.createElement('tr');
tableRowDiv.appendChild(tableRow);
for (var col = 1; col < 4; col++)
{
if (emp < employeeData.length) {
var tableCol = document.createElement('td');
tableCol.className = 'pdf-employee-box';
tableRow.appendChild(tableCol);
var empName = document.createElement('div');
empName.className = 'fw-bold';
empName.innerHTML = `${employeeData[emp].firstName} ${employeeData[emp].lastName}`;
tableCol.appendChild(empName);
var empPhoto = document.createElement('img');
var employeePhotoUrl = employeeData[emp].azurePhotoURL;
if (employeePhotoUrl == null) {
empPhoto.src = '/Images/lg_unavailable.jpg';
} else {
empPhoto.src = employeePhotoUrl;
}
var empPhotoOnError = document.createAttribute('onerror');
empPhotoOnError.value = "this.onerror=null;this.src='/Images/lg_unavailable.jpg';";
empPhoto.setAttributeNode(empPhotoOnError);
empPhoto.alt = 'Employee Photo';
empPhoto.className = 'employee-photo-pdf pdf-image';
tableCol.appendChild(empPhoto);
var empTitle = document.createElement('div');
empTitle.innerHTML = employeeData[emp].title != null ? `Title: ${employeeData[emp].title}` : 'Title: Unknown';
empTitle.className = 'pdf-details';
tableCol.appendChild(empTitle);
var empDept = document.createElement('div');
empDept.innerHTML = employeeData[emp].department != null ? `Department: ${employeeData[emp].department}` : 'Department: Unknown';
empDept.className = 'pdf-details';
tableCol.appendChild(empDept);
var empEmail = document.createElement('div');
empEmail.innerHTML = (employeeData[emp].emailAddress != null && !employeeData[emp].emailAddress.toLowerCase().includes('no ')) ? `Email: ${employeeData[emp].emailAddress}` : 'Email: Unknown/None';
empEmail.className = 'pdf-details';
tableCol.appendChild(empEmail);
var empStartDate = document.createElement('div');
empStartDate.innerHTML = employeeData[emp].startDate != null ? `Start Date: ${employeeData[emp].startDate}` : 'Start Date: Unknown';
empStartDate.className = 'pdf-details';
tableCol.appendChild(empStartDate);
var empLocation = document.createElement('div');
empLocation.innerHTML = employeeData[emp].location != null ? `Location: ${employeeData[emp].location}` : 'Location: Unknown';
empLocation.className = 'pdf-details';
tableCol.appendChild(empLocation);
if (col == 2) tableCol.className = tableCol.className + ' pdf-grid-lines';
emp++;
}
}
if (emp % 9 == 0) {
tableRowDiv.className += 'page-break-after';
}
}
} while (emp < employeeData.length)
}
I decided to calculate the number of rows I need based on how many employee records we have. So, if we have 436 employees, then I'm determining that I need 146 rows. So I'm programming it to generate the precise number of rows I need, and only put 3 items (columns) in each row. The 146th row in this case only has 1 record.
As you can see, I have the code in a loop only running until it reaches the last employee, so that it doesn't throw an index out of range exception.
This particular code is where I am telling the table to break to the next page, so that employee records aren't running over onto another page and splitting up the information.
if (emp % 9 == 0) {
tableRowDiv.className += 'page-break-after';
}
Here is the CSS that I'm referencing in an external CSS file:
@media print {
.page-break-after {
break-after: page;
}
}
I understand that I am by no means the ultimate programming kingpin, so there could be better or easier ways of doing this. However, this is how I made it work with my environment and it does exactly what I need it to do without using some framework or library.
I found in my research that many PDF generator frameworks, libraries, etc. now require licensing. So if generating a printable document is your goal, then this could work for you too. Anyways, good luck and I hope I have helped someone.