I have written JavaScript functions that are responsible for generating holiday summary table and loading it with data. Below is a function that performs AJAX call and fetches the data from back end.
const loadHolidaySummary = () => {
if (XMLHttpRequest){
request = new XMLHttpRequest();
} else if (ActiveXObject){
request = new ActiveXObject("Microsoft.XMLHTTP");
} else {return false;}
request.open("POST", "loadHolidaySummary.php", true);
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.onreadystatechange = function(){
if(request.readyState == 4 && request.status == 200){
let response = JSON.parse(this.responseText);
//errors here are unlikely to happen
let errorsArrayLength = Object.keys(response.errors).length;
if (response.sessionIsSet === false){$('#notLoggedInModal').modal('show');notLoggedIn();}
else {
if (errorsArrayLength>0){
for (let i=0; i<errorsArrayLength; i++) {
$("#holidaySummaryBody").addClass("errorStyle TAC");
let errorText = '<br>';
errorText += response.errors[i];
errorText += '<br>';
holidaySummaryBody.innerHTML = errorText;
}
} else {
//generate holiday summary table
populatedHolidaySummaryTable(response);
}
}
} else if(request.readyState==4 && request.status==0 ) {
notConnectedError();
} else {}
}
request.send();
}
Apart from JavaScript, I have also amended HTML code. Right after the table that stores holiday summary I’ve added a button that copies all table once it is clicked. Additional HTML code for this button is below:
<div class="row PTB1">
<div class="col-md-3 hidden-xs"></div>
<div class="col-md-6 col-xs-12">
<div class="col-sm-9 col-xs-8 rDiv" id="copyHolSumTableResponse"></div>
<div class="col-sm-3 col-xs-4">
<button type="button" class="btn btn-success btn-lg button bSh" id="copyHolSumTable">Copy Table</button>
</div>
</div>
</div>
Then I wrote a function to generate table, a function to copy table and 4 more functions that are used to avoid repetitive code. The functions that generate summary table and copy table are demonstrated below:
const copyTable = (table, responseDiv) => {
// Select the table contents
let range = document.createRange();
range.selectNodeContents(table);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
// Copy the table contents
document.execCommand("copy");
// Deselect the table contents
window.getSelection().removeAllRanges();
responseDiv.textContent = 'Table Copied!';
setTimeout(function(){responseDiv.textContent=" ";},1500);
}
const populatedHolidaySummaryTable = response => {
let holidaySummaryTable = document.getElementById('holidaySummaryTable');
//first clear the table.
holidaySummaryTable.innerHTML = "";
//a table row to insert "Holidays Used" heading
holidaysUsedBookedTR(holidaySummaryTable, "Holidays Used");
//a table row to insert "Days" and "Date" heading
tableRowForDateAndDays(holidaySummaryTable);
//if holidays have been used add them to the table, else add a table row that informs the user that no holidays have been used.
let usedHolidayCount = Object.keys(response.usedHolidaysArray).length;
if (usedHolidayCount>0){
for(let i=0; i<usedHolidayCount;i++) {
//call a function that created table row with 2 td elements that store date and day value.
createTRForHolidaysTable(holidaySummaryTable, "holidayUsedColor", response.usedHolidaysArray[i]["holidayDate"], response.usedHolidaysArray[i]["holidayValue"]);
}
} else {
//insert message that informs user that no holidays have been used.
createTRForHolidaysTable(holidaySummaryTable, "holidayUsedColor", "No used holidays", 0);
}
//a row that shows the number of total holidays used
tableRowForHolSummaryTotals(holidaySummaryTable, "Total Holidays Used", response.holidaysUsed);
//BOOKED HOLIDAYS TABLE PART
//a table row to insert "Holidays Used" heading
holidaysUsedBookedTR(holidaySummaryTable, "Holidays Booked");
//a table row to insert "Days" and "Date" heading
tableRowForDateAndDays(holidaySummaryTable);
let bookedHolidayCount = Object.keys(response.bookedHolidaysArray).length;
if (bookedHolidayCount>0){
for(let i=0; i<bookedHolidayCount;i++) {
//call a function that created table row with 2 td elements that store date and day value.
createTRForHolidaysTable(holidaySummaryTable, "holidayBookedColor", response.bookedHolidaysArray[i]["holidayDate"], response.bookedHolidaysArray[i]["holidayValue"]);
}
} else {
//insert message that informs user that no holidays have been booked.
createTRForHolidaysTable(holidaySummaryTable, "holidayBookedColor", "No booked holidays", 0);
}
//a row that shows the number of total holidays booked
tableRowForHolSummaryTotals(holidaySummaryTable, "Total Holidays Booked", response.holidaysBooked);
//a row that shows the number of total holidays available
tableRowForHolSummaryTotals(holidaySummaryTable, "Total Holidays Available", response.availableHolidays);
//Activate copy table button
let responseDiv = document.getElementById("copyHolSumTableResponse");
let copyTableButton = document.getElementById("copyHolSumTable");
copyTableButton.onclick = function () { copyTable(holidaySummaryTable, responseDiv);}
}
As table rows in his summary table are repetitive: there are 2 heading rows, 2 sub heading rows, 3 rows that show holidays used, booked and available, and a variable number of rows that store dates when holidays where used. To avoid writing repetitive code, I created these functions below:
//a table row to insert the date for holiday and used holiday day value
const createTRForHolidaysTable = (holidaySummaryTable, color, date, dayValue) =>{
let tableRow = document.createElement("tr");
//table width 80% and 20% gets inherrited so no need to add these classess!
let tableData = document.createElement("td");
tableData.setAttribute("class", color);
tableData.textContent = date;
tableRow.appendChild(tableData);
let tableData2 = document.createElement("td");
tableData2.setAttribute("class", color);
tableData2.textContent = dayValue;
tableRow.appendChild(tableData2);
holidaySummaryTable.appendChild(tableRow);
}
//a table row to insert "Holidays Used" heading
const holidaysUsedBookedTR = (holidaySummaryTable, titleText) => {
let holidaysUsedHeading = document.createElement("tr");
let tableDataUsedHolidaysHeading = document.createElement("td");
tableDataUsedHolidaysHeading.setAttribute("colspan", "2");
tableDataUsedHolidaysHeading.textContent = titleText;
holidaysUsedHeading.appendChild(tableDataUsedHolidaysHeading);
holidaySummaryTable.appendChild(holidaysUsedHeading);
}
//a table row to insert "Days" and "Date" heading
const tableRowForDateAndDays = holidaySummaryTable=> {
let holidaysUsedSubHeading = document.createElement("tr");
let tableDataSubHeading1 = document.createElement("td");
tableDataSubHeading1.setAttribute("class", "width80");
tableDataSubHeading1.textContent = "Date";
holidaysUsedSubHeading.appendChild(tableDataSubHeading1);
let tableDataSubHeading2 = document.createElement("td");
tableDataSubHeading2.setAttribute("class", "width20");
tableDataSubHeading2.textContent = "Days";
holidaysUsedSubHeading.appendChild(tableDataSubHeading2);
holidaySummaryTable.appendChild(holidaysUsedSubHeading);
}
//a table row to insert total holidays used, booked or available.
const tableRowForHolSummaryTotals = (holidaySummaryTable, totalsText, totalsDays) => {
let totalHolidaysUsedRow = document.createElement("tr");
let totalHolidaysUsedTD1 = document.createElement("td");
totalHolidaysUsedTD1.setAttribute("class", "width80 TARI");
totalHolidaysUsedTD1.textContent = totalsText;
totalHolidaysUsedRow.appendChild(totalHolidaysUsedTD1);
let totalHolidaysUsedTD2 = document.createElement("td");
totalHolidaysUsedTD2.setAttribute("class", "width20");
totalHolidaysUsedTD2.textContent = totalsDays;
totalHolidaysUsedRow.appendChild(totalHolidaysUsedTD2);
holidaySummaryTable.appendChild(totalHolidaysUsedRow);
}
And once finished I get the table I have desired. Below is an example of table that I have generated using this new feature, then copied it using the “Copy table” button and send it via email.

However, while coding JavaScript, I have realised that it could be done even better. For example some holidays are consecutive, so instead of showing them in table like this:

It would be a lot more helpful if in “Date” field it would display the start and end date for consecutive days, and in the “Days” field a total number of holidays used for this period like this:

This would look a lot more like how we write down the days on our holiday passports. I believe this can be achieved, but it will require some work on the PHP file 😉