Javascript - Fill HTML table on PDF with Woocommerce order table data - javascript

I'm developing a way to export Woocommerce order data into a PDF doc table, using javascript.
For that I'm storing the innerText values of each order table cell in different let variables and later
use those variables to populate a HTML table which will appear in my final PDF doc.
I feel like I'm close to hitting gold, but I get a console error of Uncaught TypeError: Cannot read property 'innerText' of undefined on browser.
This is my code so far, any idea why I'm getting this error and any way I could fix it?
function printData(){
let orderTableData = document.getElementsByClassName("woocommerce_order_items")[0].innerHTML;
let orderTableSize = document.getElementById("order_line_items");
let tamanho = orderTableSize.rows.length;
let orderPricesData = document.getElementsByClassName("wc-order-totals")[0].innerHTML;
let orderItemName = document.getElementById("order_line_items").getElementsByClassName("name")[0].innerText;
let orderItemPrice = document.getElementById("order_line_items").getElementsByClassName("item_cost")[0].innerText;
let orderItemQtd = document.getElementById("order_line_items").getElementsByClassName("quantity")[0].innerText;
let orderItemTotal = document.getElementById("order_line_items").getElementsByClassName("line_cost")[0].innerText;
for(var j = 0; j <= tamanho; j++){
window.frames["print_frame"].document.body.innerHTML = "<table border='1' width='100%'><tr><th>Item</th><th>Preço uni.</th><th>Qtd.</th><th>Total</th></tr><tr><td>" + document.getElementById("order_line_items").getElementsByClassName("name")[j].innerText + "</td><td>" + document.getElementById("order_line_items").getElementsByClassName("item_cost")[j].innerText + "</td><td>" + document.getElementById("order_line_items").getElementsByClassName("quantity")[j].innerText + "</td><td>" + document.getElementById("order_line_items").getElementsByClassName("line_cost")[j].innerText + "</td></tr></table>";
}
window.frames["print_frame"].window.focus();
window.frames["print_frame"].window.print();
}
I'll also leave a print of the Woocommerce table I'm using for test.

In your loop, you don't need to use the condition <= (less than or equal) because you are begining with 0 (because its a array index), just use < (less than) in these cases.
Another thing, You should use a iframe to put the content you want to print, I didn't understand the window.frames logic you used.
I tested that snippet and it works:
function printData(){
let orderTableData = document.getElementsByClassName("woocommerce_order_items")[0].innerHTML;
let orderTableSize = document.getElementById("order_line_items");
let tamanho = orderTableSize.rows.length;
let orderPricesData = document.getElementsByClassName("wc-order-totals")[0].innerHTML;
let orderItemName = document.getElementById("order_line_items").getElementsByClassName("name")[0].innerText;
let orderItemPrice = document.getElementById("order_line_items").getElementsByClassName("item_cost")[0].innerText;
let orderItemQtd = document.getElementById("order_line_items").getElementsByClassName("quantity")[0].innerText;
let orderItemTotal = document.getElementById("order_line_items").getElementsByClassName("line_cost")[0].innerText;
let iframe = document.createElement('iframe');
document.body.appendChild(iframe);
iframe.style.display = 'none';
for(var j = 0; j < tamanho; j++){
iframe.contentDocument.body.innerHTML = "<table border='1' width='100%'><tr><th>Item</th><th>Preço uni.</th><th>Qtd.</th><th>Total</th></tr><tr><td>" + document.getElementById("order_line_items").getElementsByClassName("name")[j].innerText + "</td><td>" + document.getElementById("order_line_items").getElementsByClassName("item_cost")[j].innerText + "</td><td>" + document.getElementById("order_line_items").getElementsByClassName("quantity")[j].innerText + "</td><td>" + document.getElementById("order_line_items").getElementsByClassName("line_cost")[j].innerText + "</td></tr></table>";
}
iframe.contentWindow.focus();
iframe.contentWindow.print();
document.body.removeChild(iframe);
}

Related

element.appendChild() giving unexpected result: removes existing children

I am creating a 'photos' page on a website. It uses PHP to retrieve the filenames in a directory, and then attempts to create divs (with images in them) programmatically with javascript. However, when I try to create 'w3-third' divs, edit the innerHTML so that it embeds an image, and (the problematic step) add them to the 'w3-row' div, it removes the existing children. Hence, there is only one image per row.
I have been looking for alternate code / solutions, but the element.appendChild() function seems to be the only method; I have tried element.children.push(), but element.children is an [HTMLCollection] which (I guess) is read-only.
$.getJSON("content/photospage/get_filenames.php", function(data){
var photoFileNames = data;
console.log(photoFileNames.length + " images to display.");
var photosDiv = document.getElementById("divPhotos");
for(var i = 0; i < photoFileNames.length; i += 3){
console.log("Loop! i=" + i);
var newRow = document.createElement("div");
newRow.classList.add("w3-row");
newRow.classList.add("w3-padding-8")
var newImg1 = newImg2 = newImg3 = document.createElement("div");
newImg1.classList.add("w3-third")
newImg2.classList.add("w3-third")
newImg3.classList.add("w3-third")
newImg1.innerHTML = "<img src='" + dir + photoFileNames[i] + "' class='w3-round w3-margin-bottom constrained'>";
newRow.appendChild(newImg1);
console.log("displayed img " + (i))
if(i+1 < photoFileNames.length){
newImg2.innerHTML = "<img src='" + dir + photoFileNames[i+1] + "' class='w3-round w3-margin-bottom constrained'>";
newRow.appendChild(newImg2);
console.log("displayed img " + (i+1))
}
if(i+2 < photoFileNames.length){
newImg3.innerHTML = "<img src='" + dir + photoFileNames[i+2] + "' class='w3-round w3-margin-bottom constrained'>";
newRow.appendChild(newImg3);
console.log("displayed img " + (i+2))
}
console.log(newRow.children);
photosDiv.appendChild(newRow);
}
The html element that exists by default:
<div class="w3-container w3-content w3-center w3-padding-32 " id="divPhotos">
</div>
Sorry for the large amount of code above. Thanks for any assistance, and I'm happy to clarify anything that I failed to mention. :)
Also, I am aware that the code is clunky and inefficient, so let me know if you pick up on anything I could do better. Thanks again! :)
With
var newImg1 = newImg2 = newImg3 = document.createElement("div");
you've created one object (an HTMLDivElement) in memory, which 3 variable names (newImg1, newImg2, newImg3) refer to. You do not have 3 separate elements. When you call appendChild with one of the elements, you remove it from wherever it previously existed in the DOM.
Since you want separate elements, you should do so explicitly:
var newImg1 = document.createElement("div");
var newImg2 = document.createElement("div");
var newImg3 = document.createElement("div");
You could make the code less repetitive by using another for loop instead of creating separate standalone elements:
for (let j = 0; j < 3; j++) {
const thisIndex = i + j;
if (thisIndex >= photoFileNames.length) {
break;
}
const img = document.createElement("div");
img.innerHTML = "<img src='" + dir + photoFileNames[thisIndex] + "' class='w3-round w3-margin-bottom constrained'>";
newRow.appendChild(img);
}

Issues attempting to display data from JSON file

Premise:
I'm playing around with javascript and have been trying to display a populated JSON file with an array of people on the browser. I've managed to display it through ajax, but now I'm trying to perform the same task with jQuery.
Problem:
The problem is that it keeps saying customerdata[i] is undefined and can't seem to figure out why.
$(function() {
console.log('Ready');
let tbody = $("#customertable tbody");
var customerdata = [];
$.getJSON("MOCK_DATA.json", function(data) {
customerdata.push(data);
});
for (var i = 0; i < 200; i++) {
//Cell for name
let nameTD = $('<td>').text(customerdata[i].first_name + ", " + customerdata[i].last_name);
//Cell for birthdate
let mDate = moment(customerdata[i].birthdate);
let formattedmDate = mDate.format('YYYY-MM-DD');
let birthdateTD = $('<td>').text(formattedmDate);
//Cell for Address
let addressTD = $('<td>').html("City: " + customerdata[i].city + '<br>' + "Email: " + customerdata[i].email + '<br>' + '<a href=' + customerdata[i].website + '>Website</a>');
//Cell for Credits
let creditTD = $('<td>').text(customerdata[i].credits);
let row = $('<tr>').append(nameTD).append(birthdateTD).append(addressTD).append(creditTD);
tbody.append(row);
}
})
SAMPLE CONTENT OF MOCK_DATA.json
[
{"id":1,"first_name":"Tracey","last_name":"Jansson","email":"tjansson0#discuz.net","gender":"Female","ip_address":"167.88.183.95","birthdate":"1999-08-25T17:24:23Z","website":"http://hello.com","city":"Medellín","credits":7471},
{"id":2,"first_name":"Elsa","last_name":"Tubbs","email":"etubbs1#uol.com.br","gender":"Female","ip_address":"61.26.221.132","birthdate":"1999-06-28T17:22:47Z","website":"http://hi.com","city":"At Taḩālif","credits":6514}
]
Firstly, you're pushing an array into an array, meaning you're a level deeper than you want to be when iterating over the data.
Secondly, $.getJSON is an asynchronous task. It's not complete, meaning customerdata isn't populated by the time your jQuery is trying to append the data.
You should wait for getJSON to resolve before you append, by chaining a then to your AJAX call.
$.getJSON("MOCK_DATA.json")
.then(function(customerdata){
for(var i = 0; i < 200; i++){
//Cell for name
let nameTD = $('<td>').text(customerdata[i].first_name + ", " + customerdata[i].last_name);
//Cell for birthdate
let mDate = moment(customerdata[i].birthdate);
let formattedmDate = mDate.format('YYYY-MM-DD');
let birthdateTD = $('<td>').text(formattedmDate);
//Cell for Address
let addressTD = $('<td>').html("City: " +
customerdata[i].city + '<br>' + "Email: " +
customerdata[i].email + '<br>' + '<a
href='+customerdata[i].website+'>Website</a>');
//Cell for Credits
let creditTD = $('<td>').text(customerdata[i].credits);
let row = $('<tr>').append(nameTD).append(birthdateTD).append(addressTD).append(creditTD);
tbody.append(row);
}
})
You also won't need to define customerdata as an empty array at all with this approach.
The problem is that data is already an array.
so you should use:
customerdata = data;
otherwhise you are creating an array in the pos 0 with all the data

How to stop getting error "Unable to get property 'childNodes' of undefined or null reference?

I have a script where I am looping through a xml.responseXML and putting the childNodes[0].nodeValue into a table body. It works perfectly except when there are no results in the xml.responseXML.
The results are comments about an observation and there may be times where there are no comments. So what I want is if there are no comments to not build the table body and not throw the error "Unable to get property 'childNodes' of undefined or null reference.
I tried putting this into an if statement where if childNodes = "" then do nothing else build my table body but I haven't had any luck. As of right now every time you open an observation that doesn't have comments the browser throws an error, if it has comments then it works perfectly. Any suggestions or help would be appreciated as I am stuck.
function viewComments(xml) {
var i;
var xmlDoc = xml.responseXML;
var tbody = "";
var x = xmlDoc.getElementsByTagName("allComments");
for (i = 0; i < x.length; i++) {
tbody += "<tr><td>" +
x[i].getElementsByTagName("COMMENT")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("DATE_COMMENT_UPDATED")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("COMMENT_ADDED_BY")[0].childNodes[0].nodeValue +
"</td ></tr >";
}
$("#COMMENTS tbody").html(tbody);
$("#COMMENTS").trigger("update");
}
</script>
for (i = 0; i < x.length; i++) {
var el = x[i].getElementsByTagName("COMMENT");
if (el.length) {
tbody += "<tr><td>" +
x[i].getElementsByTagName("COMMENT")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("DATE_COMMENT_UPDATED")[0].childNodes[0].nodeValue +
"</td><td>" +
x[i].getElementsByTagName("COMMENT_ADDED_BY")[0].childNodes[0].nodeValue +
"</td ></tr >";
}
}

javascript - unable to populate 2D array

My instructor tasked us to build a 2D array and populate it with values from our HTML form. He gave us this example to create the array.
var tasks = new Array();
var index = 0;
He then said to insert the values into the two columns using this code.
tasks[index]["Date"] = tempdate;
tasks[index]["Task"] = temptask;
However, something about these two lines is causing the script to break, because when I comment them out the final line of my script returns a value to the correct div. When I uncomment these lines no value is returned. Is there something wrong in my syntax?
This is my complete js file:
var tasks = new Array();
var index = 0;
function addTask() {
var tempdate = new Date();
var temptask = document.getElementById("taskinfo").value;
var td = document.getElementById("taskdate").value;
tempdate = td + " 00:00";
tasks[index]["Date"] = tempdate;
tasks[index]["Task"] = temptask;
index++
tasks.sort(function (a, b) { return b.date - a.date });
var tablecode = "<table class = 'tasktable'>" +
"<tr>"+
"<th>Date</th>"+
"<th>Task</th>"+
"</tr>";
for (var i = 0; i < tasks.length; i++) {
tablecode = tablecode + "<tr>" +
"<td>" + tasks[i]["Date"].toDateString() + " </td>" +
"<td>" + tasks[i]["Task"] + " </td>" +
"</tr>";
}
tablecode = tablecode + "</table>";
//I am only returning "temptask" to test with, I will be returning "tablecode".
document.getElementById("bottomright").innerHTML = temptask;
return false;
}
tasks[index] (in the first case, tasks[0]) doesn't yet exist, so you can't give it properties. Try this to create an object and assign it to tasks[index]:
tasks[index] = {
Date: tempdate,
Task: temptask
};
in place of
tasks[index]["Date"] = tempdate;
tasks[index]["Task"] = temptask;
Alternatively, you can use
tasks[index] = {};
tasks[index]["Date"] = tempdate;
tasks[index]["Task"] = temptask;

onclick event not firing

var subjectList;
function PageMaster()
{
this.contentDiv = document.getElementById("content");
}
/**
* Builds the main part of the web page based on the given XML document object
*
* #param {Object} xmlDoc the given XML document object
*/
PageMaster.prototype.doIt = function(xmlDoc)
{
alert("PageMaster()");
alert("Clear page...");
this.contentDiv.innerHTML = "";
if (null != xmlDoc)
{
alert("Build page...");
//create div Post
var divPost = document.createElement("div");
divPost.className = "post";
//create h1 element
var h1Element = document.createElement("h1");
var headingText = document.createTextNode("Invitations");
h1Element.appendChild(headingText);
//insert h1 element into div post
divPost.appendChild(h1Element);
subjectList = xmlDoc.getElementsByTagName("subject");
var groupList = xmlDoc.getElementsByTagName("group");
for (var i = 0; i < subjectList.length; i++) //for each subject
{
var divEntry = document.createElement("div");
divEntry.className = "entry";
var subjectNum = subjectList[i].attributes[0].nodeValue;
var subjectName = subjectList[i].attributes[1].nodeValue;
var groupId = groupList[i].attributes[0].nodeValue;
var groupName = groupList[i].attributes[1].nodeValue;
var ownerId = groupList[i].attributes[2].nodeValue;
//set up the invitation table attributes
var table = document.createElement("table");
table.width = 411;
table.border = 3;
table.borderColor = "#990000"
tableRow = table.insertRow(0);
tableCell = tableRow.insertCell(0);
var cellContent = "";
//create invitation message
var invitationMsg = "<p>Subject : " + subjectNum + " - " + subjectName + "</p>";
invitationMsg += "<p>You are invited to join " + groupName + " (groupId : " + groupId + ") by owner Id:" + ownerId + "</p>";
cellContent += invitationMsg;
//create buttons
cellContent += "<input type='button' id='acceptButton" + i + "' value='Accept' onclick='acceptInvitation(i)'>"
cellContent += "<input type='button' id='declineButton" + i + "' value='Decline'>"
tableCell.innerHTML = cellContent;
divEntry.appendChild(table);
var blankSpace = document.createElement("p");
divEntry.appendChild(blankSpace);
divPost.appendChild(divEntry);
}
//insert div post into div content
this.contentDiv.appendChild(divPost);
}
};
function acceptInvitation(i)
{
alert("hello");
//alert(subjectList[i].attributes[0].nodeValue);
}
above is extract of my javascript code. What the code do is to create a table of inviting group from the xml file with accept and decline button. when user press accept, the table will disappear and the table below will move up. For now I am only testing my accept invitation button to see if it works.But
my onclick function in the accept button does not work for some reason I don't understand. the alert in acceptInvitation() is not read. Any help will be appreciated. Thanks
What about:
cellContent += "[...] onclick='acceptInvitation("+i+")'>"
This ensures that i is evaluated with the value of the variable instead of as a literal
Try to call it like this
onclick='acceptInvitation();'
Not like this
onclick='acceptInvitation(i)'
dont know if that's what's causing your problem but your outputting onclick='acceptInvitation(i)' Im guessing you want to output acceptInvitation(value-of-i), that is acceptInvitation(" + i + ")
while perhaps not addressing the central problem,
onclick='acceptInvitation(i)'
in this case i would be undefined.
onclick='acceptInvitation("+i+")'
would solve at least one problem. Also, you're using an unusual mixture of innerHTML and DOM methods. Why not stick to the DOM methods and use attachEvent/AddEventListener?
edit: A list apart has a good article on binding of variables at http://www.alistapart.com/articles/getoutbindingsituations/
The following is a somewhat specialized example. See the article for more generalized case (or use a library like Prototype)
var click_hdlr = function() {
return function() {
return acceptInvitation.apply(this,arguments);
}
}
var acc_btn = document.createElement("input");
acc_btn.setAttribute("type","button");
acc_btn.setAttribute("id","accept");
acc_btn.setAttribute("value","Accept");
acc_btn.addEventListener("click",click_hdlr(i),false);

Categories

Resources