Why do these two functions give different results?
var table1 = document.getElementById("table1");
var table2 = document.getElementById("table2");
var register = [
{att1: 1, att2: 2, att3: 3},
{att1: 4, att2: 5, att3: 6},
{att1: 7, att2: 8, att3: 9}
];
//table1.innerHTML = "";
//table2.innerHTML = "";
function drawTable1() {
for (var i = 0; i < register.length; i++) {
table1.innerHTML += "<tr><td>" + register[i].att1 + "</td><td>" + register[i].att2 + "</td><td>" + register[i].att3 + "</td></tr>";
}
}
function drawTable2() {
for (var i = 0; i < register.length; i++) {
table2.innerHTML += "<tr>";
table2.innerHTML += "<td>" + register[i].att1 + "</td>";
table2.innerHTML += "<td>" + register[i].att2 + "</td>";
table2.innerHTML += "<td>" + register[i].att3 + "</td>";
table2.innerHTML += "</tr>";
}
}
drawTable1();
drawTable2();
table {
display: inline;
}
<body>
<table>
<thead>
<tr>
<th>Att1</th>
<th>Att2</th>
<th>Att3</th>
</tr>
</thead>
<tbody id="table1">
</tbody>
</table>
<table>
<thead>
<tr>
<th>Att1</th>
<th>Att2</th>
<th>Att3</th>
</tr>
</thead>
<tbody id="table2">
</tbody>
</table>
</body>
I'm just beginning with js, and I've noticed this thing. From a logical point of view i see no differences between the two functions, the second has just been broken up to make the code easier to read. It should simply be adding strings to a string, but it seems like at every operation the opened tags get closed by the program, resulting in a multitude of rows.
Why is this? How is this useful?
innerHTML isn't actually a string. It's an interface to the DOM (the elements on the page); reading from it generates a string version of what's currently there, and assigning a value to it modifies the tree.
It's impossible to have an unclosed <tr> element in the DOM -- when you perform an operation like table1.innerHTML += "<tr>", the browser sees the unclosed <tr> tag as invalid HTML and has to repair it by inserting a closing </tr>. When you later access innerHTML to perform another modification, you see the "repaired" version, not the value you initially assigned.
The easiest fix will be to build the entire table as a string, then assign to innerHTML all at once, e.g.
var html = "";
for (...) {
html += "<tr>";
html += "<td>example</td>";
html += "</tr>";
}
table1.innerHTML = html;
You may also want to investigate Javascript DOM methods to create HTML elements (like document.createElement()) as an alternative -- innerHTML is a clumsy interface.
Well the innerHTML content is — as the name implies — HTML and HTML is not just a string. I assume you know that browsers build a DOM out of it. Basically a tree out of nodes that know their tag, attributes, children etc.
Now you need this DOM to render anything. Sure, it is nearly impossible to get invalid html as non-html can just be interpreted as a mere string (which is valid html). However, the browser tries to fullfill the html standard as much as possible. Therefore it also generates missing end tags in order to produce well-formed html. (even when it is not in the html, he will implicitely generate them for the DOM and in some browsers you can see that in the HTML provided in the dev console).
So now you add a random <tr> attribute to your html like this table2.innerHTML += "<tr>". This would produce not-well-formed html. Therefore it should generate the missing end tag. Whether that is done while running the js-code or afterwards when refreshing the DOM, I don't know, but generally it helps generate well-formed HTML.
I'm sure you know how to circumvent that problem, but anyways: Instead of using an temporary string, you might want to look at document.createElement(). This is generally used to generate well-formed html in a non-confusing and safe (as in "something unexpected like above doesn't happen safe") way.
Related
I have a table in HTML where the ID is dynamically generated from a row counter:
$(table).find('tbody').append("<tr>name=\"tableRow\"</tr>"
+ "<td>"
+ "<select id=\"shapeSelect_" + rowCount + "></td>"
+ "<option onclick=\"sphereSelect()\" value=\"sphere\">Sphere</option>"
+ "<option onclick=\"cylinderSelect()\" value=\"cylinder\">Cylinder</option>"
+ "</select>"
+ "</td>"
+ "<td><input type=\"text\" id=\"altitude" + rowCount + "\"</td>"
+ "<td><input type=\"text\" name=\"maxAlt\" id=\"maxAltitude_" + rowCount + "></td>"
+ "</tr>"
I need maxAltitude to become disabled for input when sphere is selected. When cylinder is selected, it should become enabled for input.
Every example I find is pretty simple but requires knowing exactly what the ID is, where in my code it is dynamically generated. This is an example of what I'm finding:
$(#maxAltitude).prop("disabled", true);
How can I do this when maxAltitude will be something more like: maxAltitude_10? There may be 1-n rows in a table, and I need to specifically disable the max altitude in the row where the dropdown select was changed.
I've tried jQuery and javascript but can't seem to find a good way to do this:
<option onclick="shapeSelect()" value="sphere">Sphere</option>
<option onclick="shapeSelect()" value="cylinder">Cylinder</option>
function shapeSelect() {
var shapeSelects = document.getElementsByName("shapeSelect");
var maxAlts = document.getElementsByName("maxAlt");
for(var i = 0; i < shapeSelects.length; i++) {
switch(shapeSelects[i].value) {
case "sphere":
maxAlts[I].disabled = True;
break;
case "cylinder":
maxAlts[i].disabled = False;
}
}
}
With the above code I get: SyntaxError: unexpected token: identifier whenever shapeSelect() is fired.
I've modified the code as follows:
<table class="myTable" id="myTable"></table>
$(table).find('tbody').append("<tr>name=\"tableRow\"</tr>"
+ "<td>"
+ "<select id=\"shapeSelect_" + rowCount + "></td>"
+ "<option value=\"sphere\">Sphere</option>"
+ "<option value=\"cylinder\">Cylinder</option>"
+ "</select>"
+ "</td>"
+ "<td><input type=\"text\" id=\"altitude_" + rowCount + "\"</td>"
+ "<td><input class=\"maxAltitudeInput\" type=\"text\" id=\"maxAltitude_" + rowCount + "\" disabled></td>"
+ "</tr>"
$('#myTable').on('change','.shapeSelector',function(){
var shouldDisableInput = $(this).val() === 'sphere';
$(this).closest('tr').find('.maxAltitudeInput').attr('disabled',shouldDisableInput);
}
And still nothing happens when I change the shape selector dropdown.
EDIT:
Apologies on the naming mismatches. My dev machine is on an airgapped network and I was hand jamming the post here on Stack Overflow. The rowCount variable was being created and incremented in another function. I was trying to only put relevant code in the post for brevity.
I was missing a class from shapeSelector. That was the missing link. It works now!
jQuery actually makes this really easy by binding this to whichever element triggered an event.
For instance, instead of writing a generic function for when that value changes, you could use jQuery to bind an event listener to them:
$('#myTable').on('change','.shapeSelector',function(){
var shouldDisableInput = $(this).val() === 'sphere';
$(this).closest('tr').find('.maxAltitudeInput').attr('disabled',shouldDisableInput);
}
You'll notice a few things in this snippet:
The element we are binding the listener to is the table, not the individual row. That's because the row is dynamic, and we don't want to have to keep adding listeners every time we add a row. Instead we add it to the parent which is stable, but then we specify that we are interested in its children that match ".shapeSelector"
The listener relies on class names, not IDs, since we want to match multiple copies of them, not just a specific one. So you'd need to add those class names or a similar way of matching more than one item
Inside the callback function that runs, you'll notice a couple uses of this. jQuery has bound that to the element that triggered the event listener, in this case, the <select> control. So when we use this, we have to think of it from that perspective. We can get its value by $(this).val(), we can find its parentt with $(this).parent(), etc. In this case, I'm travelling up to the nearest tr, then from there down to that tr's input that I want to disable. You'd need to adjust a little depending on your dom.
Also note that this is a DOM element, not a jQuery result. That's why when we want to run more jQuery commands on it, we have to put it in $() again.
That's how I'd approach it. We don't have your entire code here, so you'll have to adjust a bit, but hopefully that pushes you off in the right direction.
EDIT
To be honest, there were a lot of naming mismatches and things that didn't line up. For instance, you were attempting to append onto a tbody tag, but that tag didn't exist. You were using a rowCount variable, but didn' ever set that up or increment it. The select tag sill didn't have the class name you were trying to use.
I suggest you look at your code piece by piece, ask yourself what you're telling the browser to do, and then do that instruction in your mind to make sure the computer can do it.
HTML:
<table class="myTable" id="myTable"><tbody></tbody></table>
JavaScript:
var rowCount = 0;
function addRow(){
$('.myTable tbody').append(`<tr name="tableRow">
<td>
<select class="shapeSelector" id="shapeSelect_${rowCount}">
<option value="sphere">Sphere</option>
<option value="cylinder">Cylinder</option>
</select>
</td>
<td><input type="text" id="altitude_${rowCount}" /></td>
<td><input class="maxAltitudeInput" type="text" id="maxAltitude_${rowCount}" disabled></td>"
</tr>`);
rowCount++;
}
$('.myTable').on('change','.shapeSelector',function(){
var shouldDisableInput = $(this).val() === 'sphere';
$(this).closest('tr').find('.maxAltitudeInput').attr('disabled',shouldDisableInput);
});
addRow();
addRow();
addRow();
https://jsfiddle.net/32vnjq81/
document.getElementById("outputDiv").innerHTML = "";
document.getElementById("outputDiv").innerHTML += "<table border=1 width=100%><tr>";
for(j=1;j<=10;j++)
{
document.getElementById("outputDiv").innerHTML += "<td align=center>"+String.fromCharCode(j+64)+"</td>";
}
document.getElementById("outputDiv").innerHTML += "</tr></table>";
I want to draw a table using Javascript.
So I wrote the code like above.
I think it draw one row that has 10 columns, but it doesn't work.
Anyone know about this problem???
I ran into this problem years ago, too.
The problem is that when you use the innerHTML property to add HTML, after each update, the underlying engine will close unclosed tag (and fix other bad HTML) for you. So after the second line, the <table> and <tr> tags are automatically closed and all content after that will just be written outside the table.
Method 1
(The easy way)
Use a string to store the HTML for the whole table and update it all at once.
var HTML = "<table border=1 width=100%><tr>";
for(j=1;j<=10;j++)
{
HTML += "<td align=center>"+String.fromCharCode(j+64)+"</td>";
}
HTML += "</tr></table>";
document.getElementById("outputDiv").innerHTML = HTML;
Fiddle
Method 2
(The better way)
Use DOM functions
var table = document.createElement('table');
table.setAttribute('border','1');
table.setAttribute('width','100%')
var row = table.insertRow(0);
for(j=1; j<=10; j++){
var text = document.createTextNode(String.fromCharCode(j+64));
var cell = row.insertCell(j-1);
cell.setAttribute('align','center')
cell.appendChild(text);
}
document.getElementById("outputDiv").appendChild(table);
Fiddle
Method 2 enhanced
(The yet better way)
Use CSS instead of HTML attributes. The latter is generally depreciated as of latest specs.
A great resource to start learning CSS is the Mozilla Developer Network
Fiddle
Method 3
(The long way, but the best in the long-run)
Use jQuery.
$('<table>').append('<tr>').appendTo('#outputDiv');
for(j=1; j<=10; j++)
$('<td>').text(String.fromCharCode(j+64)).appendTo('tr');
Fiddle
I think the main problem is that your attributes are not quoted.
But it's almost always a bad idea to repeatedly update the content of a dom element in a loop—each time you update dom content it causes some internal work to be done by the browser to make sure the page layout is current.
I would build the html string up locally, then make one final update when done. (and of course make sure your attributes are quoted)
document.getElementById("outputDiv").innerHTML = "";
var newTable = "<table border='1' width='100%'><tr>";
for(j = 1; j <= 10; j++) { //opening braces should always be on the same line in JS
newTable += "<td align='center'>" + String.fromCharCode(j+64) + "</td>";
}
newTable += "</tr></table>";
document.getElementById("outputDiv").innerHTML = newTable;
I have modified my previous code to allow for a data array to be passed to a different function. Everything is working until I add this data array.
Currently what works (old version) is only passing the this operator and an ID.
Below in my code you will see that I build out the HTML along with a table and pass the html to be appended at a later time.
You can see that I am building a table with a delete button. It all works well, except now that I am passing the data array it is telling me "Unexpected identifier"
I have tested it with passing getDeleteItems(this,ID,ID) and it works fine
but getDeleteItems(this,ID,data) does not.
Below is my code, and you can see I am passing an array (that works throughout my code and other functions (including this one already!))
I am just stuck now and can't seem to see past this, maybe some fresh eyes can help!
Thanks!
function mouseOverTable(ID,parent,data)
{
// ID is in the first column (0)
// Type is in the second column (1)
// Title is in the third column (2)
// popup_ItemClicked()
var family = findFamily(ID,parent,data)
var sHTML = "";
sHTML += "<table class="+"table table-hover id='relationshipTable'"+">";
sHTML += "<thead>";
sHTML += "<tr>";
sHTML += "<th>"+"Select Item"+"</th>";
sHTML += "<th>"+"ID"+"</th>";
sHTML += "<th>"+"Content Name"+"</th>";
sHTML += "<th>"+"Type"+"</th>";
sHTML += "<th>"+"Status"+"</th>";
sHTML += "<th>"+"Date"+"</th>";
sHTML += "</tr>";
sHTML += "</thead>";
sHTML += "<tbody>";
//displays if it's a article.. blog ... etc..
for(var i = 0; i<family.length;i++)
{
sHTML += "<tr id='relationshipRow"+family[i].ID+"'>";
sHTML += "<td><input type = 'checkbox' value = '"+family[i].ID+"'></input></td>";
sHTML += "<td>"+family[i].ID+"</td>";
sHTML += "<td>"+family[i].Title+"</td>";
//shows the date that the contentitem was created
sHTML += "<td>" + family[i].Type +"</td>";
sHTML += "<td>" + family[i].Status +"</td>";
sHTML += "<td>" + (family[i].Date) +"</td>";
sHTML += "</tr>";
}
sHTML += "</tbody>";
sHTML += "</table>";
//console.log(data)
sHTML += "<input type = 'Button' id ='DeleteItems' value = 'Delete Selected' onclick='javascript: getDeleteItems(this,";
sHTML += ID;
sHTML += ",";
sHTML += data;
sHTML += ");'></input>";
return sHTML;
}
I just looked at console and the element tabs in chrome: it looks like for some reason it is parsing the object as such
onclick="javascript: getDeleteItems(this,791,[object Object],[object Object],[object Object],.... and this goes on...
Your error in the onclick="javascript: getDeleteItems
the third parameter expected to be an array you missed {} for the array objects.
So you must call getDeleteItems function like this
onclick="javascript: getDeleteItems(this,791,{[object Object],[object Object],[object Object]})"
As #Fatehi mentioned, the following lines are giving the error
sHTML += "<input type = 'Button' id ='DeleteItems' value = 'Delete Selected' onclick='javascript: getDeleteItems(this,";
sHTML += ID;
sHTML += ",";
sHTML += data;
sHTML += ");'></input>";
I believe that adding the curly braces wont fix your issue since that data variable is a parameter of the function and his value it's not global and the scope when you send it to the DOM is for global variables. You should better use data attributes for each value and then you could access them like this:
<input data-test="test" onclick="console.log(this.attributes['data-test'].value)">
So I figured out this puzzle. I had the following functions 1) that called mouseover, the mouseover function, then the delete function. Before I was creating a delete button in mouseover(and I still am). However I made mouseover return 2 items now, one that returns the html for the table and the other that returns a tableid. I also removed the findfamily function in mouseover and moved it up to where it was being called from, then passed that as the data.
functionThatCalledMouseOver(ID,parent,data){
// below have variables defining row family =findparent etc...
row.child(mouseOverTable(ID,parent,findParent)[0]).show();
//now create onclick even passing fullData
$('#'+DeleteItems).on('click',function(){
getDeleteItems(this,ID,data);
});
}
My ultimate goal was the following: I had some sharepoint list items I was deleting from a table. Usually when I create charts tables etc, I will do a call and store all data locally in an array. The array is what created the table I was making. So in theory after the table data is deleted from sharepoint, it is still in my local array.
The problem with this was that when I removed the elements (visually showing they were deleted), it I hid the children of the parent in the main table I created, then clicked to show it again, it would show the deleted items since it was not server information and just a local array.
For future people reading this, I tried my original method with turning the object into a JSON string, then using the escape function. So... escape(JSON.stringify(data)).. this got passed to the delete function. This does work for appearances only. Once decoded on the getDeleteItems function it is separate data from the original data I was trying to delete. So in my case this wouldn't work.
however, moving everything to the function that originally called the mouseover was the way to go. It allowed me to get to the object I needed to remove items from.
I am dynamically creating a table through Javascript and I DO want the table to continue off the right side of the page. Doing this manually lets the table continue off, but once I feed this into a for loop the <td>s wrap into a second line in the rendered HTML, creating two or more table rows when they reach the end of the page.
<div id="panelindex" style="overflow:scroll;text-align:center;">
<table border="0">
<tr></tr>
</table>
</div>
This is inside a table of its own (no style formatting). Then the Javascript:
var q = Math.floor((1/numpanels)*500);
if(q>50) q=50;
panelindex.innerHTML = "<table border='0'><tr>"
for(i=0; i<numpanels; i=i+1)
{
panelindex.innerHTML = panelindex.innerHTML + "<td><div id='panel" + i + "' onclick='jumppage(" + i + ")' style='float:left;text-align:center;margin:8px;border-width:3;border-color:white;border-style:none;'><a href='#" + i + "'><img src='thumbnails.php?image=blowem" + zeroFill(i,2) + ".gif&GIF&tw=128&th=128&quality=" + q + "'>\n" +
"<br />" + i + "</a></div></td>\n";
}
panelindex.innerHTML = panelindex.innerHTML + "</tr></table>"
You may notice that there is a <div> in the <td> and that is so I can apply a border marking the panel. Without the <div> it seems I cannot do that, and there are some other undesired effects. Any ideas what I can do so that all the <td>s end up on one line rather than split to a new line?
Example of what I want: http://edwardleuf.org/comics/jwb/009-conmet
What is happening: https://jsfiddle.net/w4uh0a3j/7/
Click the Show link.
innerHTML does not hold the string value you assign to it.
It parses the value as HTML, creates a DOM from it, inserts it into the document and then, when you read it back, it converts that DOM back into HTML.
This means that the string you assign is subject to error recovery and normalisation. In particular, the end tags you omitted are fixed.
panelindex.innerHTML = "<table border='0'><tr>"
console.log(panelindex.innerHTML);
<div id="panelindex" style="overflow:scroll;text-align:center;">
<table border="0"><tr>
</tr></table>
</div>
So when you start appending more data to it:
panelindex.innerHTML = panelindex.innerHTML + "<td>etc etc
You end up with:
<table border="0"><tbody><tr></tr></tbody></table><td>etc etc
Store your data in a regular variable. Only assign it to .innerHTML once you have the complete HTML finished.
A better approach then that would be to forget about trying to build HTML by mashing strings together (which is error prone, especially once you start dealing with characters that need escaping in HTML) and use DOM (createElement, appendChild, etc) instead.
OK,here is fixed html and js code. It seems like innerHTML fixes missing closing when updating html before all the code is building the rest of innerHTML. This code works :
<div id="panelindex" style="overflow:scroll;text-align:center;">
</div>
and js code :
var numpanels = 100;
var q = Math.floor((1/numpanels)*500);
if(q>50) q=50;
panelindex.innerHTML = "<table border='0'><tr>";
var html = "<table border='0'><tr>";
for(i=0; i<numpanels; i=i+1) {
html += "<td><div id='panel" + i + "' onclick='jumppage(" + i + ")' style='float:left;text-align:center;margin:8px;border-width:3;border-color:white;border-style:none;'><a href='#" + i + "'><img src='thumbnails.php?image=blowem" + ".gif&GIF&tw=128&th=128&quality=" + q + "'>\n" +
"<br />" + i + "</a></div></td>";
}
html += "</tr></table>";
document.getElementById("panelindex").innerHTML = html;
Skip right to the bottom if you'd just like to read the questions. The top lot is just context. Not vital but might help.
While working on a table in html I realised something. The code was terrible, repetitive and wasteful. Might as we have been manually adding the array.
<!--
<table border="4px" >
<caption>
Pet Table
</caption>
<tr> // Images
<td>
<script>
display(bat)
</script>;
</td>
<td>
<script>
display(goat)
</script>
</td>
<td>
<script>
display(butterfly)
</script>
</td>
<td>
<script>
display(buzzard)
</script>
</td>
<td>
<script>
display(breezie)
</script>
</td>
<td>
<script>
display(turtle)
</script>
</td>
</tr >
<tr> // Names
<td>
name
</td>
<td>
name
</td>
<td>
name
</td>
<td>
name
</td>
<td>
name
</td>
<td>
name
</td>
</tr>
<tr> //Desc
<td>
desc
</td>
<td>
desc
</td>
<td>
desc
</td>
<td>
desc
</td>
<td>
desc
</td>
<td>
desc
</td>
</tr>
<tr> //Food
<td>
food
</td>
<td>
food
</td>
<td>
food
</td>
<td>
food
</td>
<td>
food
</td>
<td>
food
</td>
</tr>
<tr> //button
<td>
button
</td>
<td>
button
</td>
<td>
button
</td>
<td>
button
</td>
<td>
button
</td>
<td>
button
</td>
</table>
-->
This lead me to spending the morning theorising and experimenting with creating tables dynamically from javaScript instead.
This was the code I was came up with.
In JavaScript (*Edit added pet array)
function pet(species, name, colour, size, food, limb, img) {
this.species = species; //property of a pet
this.name = name; //property of a pet
this.colour = colour; //property of a pet
this.size = size; //property of a pet
this.food = food; //property of a pet
this.limb = limb; //property of a pet
this.img = img; //property of a pet
this.move = move; //a function of a pet defined to the pet
var bat = new pet("fruit bat", "bats", "grey", "small", "apples", "wings", "1", move);
var goat = new pet("goat", "bastard", "off white", "goat-sized", "clothing", "hooves", "2", move);
var butterfly = new pet("butterfly", "flutterby", "rainbow", "petite", "nectar", "wings", "3", move);
var buzzard = new pet("buzzard", "Buzz", "molted black and white", "bigish", "carrion", "wings", "4", move);
var breezie = new pet("pixie", "petty", "blue", "tiny", "souls", "wings", "5", move);
var turtle = new pet("tortoise", "Tank", "Green", "smoothbacked", "lettuce", "legs", "6", move);
var len = pet.length;
function buildTable() {
document.getElementById("work");
//i is a counter, f is a flag
var f = 0;
while (f = 0) {
document.write("<table border='4px'>" + "</br>");
document.write("<caption>Pets Avaliable</caption>" + "</br>");
document.write("<tr>" + "</br>");
for (i = 0; i <= len; i++) {
document.write("<td>" + imgArray[i].outerHTML + "</td>" + "</br>");
}
document.write("</tr>" + "</br>");
document.write("<tr>" + "</br>");
for (i = 0; i <= len; i++) {
document.write("<td>" + pet[i].species + "</td> " + "</br>");
}
document.write("</tr>" + "</br>");
document.write("<tr>" + "</br>");
for (i = 0; i <= len; i++) {
document.write("<td>" + pet[i].name + "</td>" + "</br>");
}
document.write("</tr>" + "</br>");
document.write("<tr>" + "</br>");
for (i = 0; i <= len; i++) {
document.write("<td>" + pet[i].colour + "</td> " + "</br>");
}
document.write("</tr>" + "</br>");
document.write("<tr>" + "</br>");
for (i = 0; i <= len; i++) {
document.write("<td>" + pet[i].size + "</td>" + "</br>");
}
document.write("</tr>" + "</br>");
document.write("<tr>" + "</br>");
for (i = 0; i <= len; i++) {
document.write("<td>" + pet[i].food + "</td>" + "</br>");
}
document.write("</tr>" + "</br>");
document.write("</table>" + "</br>");
f = 1;
}
}
document.getElementById("please");
The HTML
<button id="please" onclick="buildTable(pet)"> Work you blighter </button>
<p id="work"></p>
I just couldn't get it to work. Console was showing no errors yet the button provided no results. After a long while of digging I did manage to come across a button that worked. (I added .innerHTML = "The button is working" to document.getElementById("work")in order to test it.) So the button was alright. Therefore the problem must be in the function itself right? Bloody right. I know it's wrong and I'll try again tomorrow using similar codes to these posts
Dynamically creating a table in javascript /
http://jsfiddle.net/ahEkH/1/
and
Create vertical column table based on Array of Objects
I don't mind working towards an answer nor trial and error but what grinds my garters is that I can't figure out what was the problem. It's good to find out how the code works but I'd like to find out why it works the way it does as well.
Questions
Why can't the console and debugger find any errors yet the page is obviously not working.
How would I label these kinds of problems in the future?What was at fault? The button? The function? Both? Neither? Myself?
I'm planning to make the table in a vertical column format instead of horizontal. Would this be problematic to do? (I'm thinking a few more for loops should do the trick without a fuss)
I'd like to use a pre-constructed array as a source for data(e.g pet[i].size but would this work or would it be better to dis-assemble the array into variables in the function?
if I do need to make variables for all the data would I be able to recycle or reuse a variable if it's inside one of the loops?
In http://jsfiddle.net/ahEkH/1/ why is "tbdy" a child of tab or is appendChild used to assign "tbdy" to tab?
I now know how to go about fixing the dynamic table but I don't know what specifically was wrong in the first place. Please share your knowledge with us.
There are a few things in your code.
while(f=0){ will always evaluate to 0, and hence false. You probably wanted while(f === 0) { (yes, triple ===). This is causing the "I don't see anything in the console" problem, because the code is actually (as you suspected) working albeit not as you expect.
Every one of your loops has for(i = 0; i <= len; i++), where var len = pet.length; You will get an index out of bounds with this, you would want to change to for(i = 0; i < len; i++).
And finally:
You probably don't want to use document.write. The first document.write call will clear the document and replace it with your table. You would be better off with document.createElement and friends to do DOM manipulation (per the jsfiddles you posted). If you go this route, you would be much better off using a library like jQuery, where you could end up with something like this:
//
// Dummy data to make the sample work.
//
var imgArray = [
{outerHTML: '1.jpg'}
, {outerHTML: '2.jpg'}
, {outerHTML: '3.jpg'}
, {outerHTML: '4.jpg'}
];
var pet = [
{species: 'species1', name: 'name1', colour: 'colour1', size: 'size1', food: 'food1'}
, {species: 'species2', name: 'name2', colour: 'colour2', size: 'size2', food: 'food2'}
, {species: 'species3', name: 'name3', colour: 'colour3', size: 'size3', food: 'food3'}
, {species: 'species4', name: 'name4', colour: 'colour4', size: 'size4', food: 'food4'}
];
var attributes = $.map(pet[0], function(n, v) { return v; });
function generateTable(id) {
var $table = $(id);
var $tbody = $('<tbody></tbody>');
$table.append($tbody);
// the images
var $tr = $('<tr />');
$.each(imgArray, function (ignored, image) {
$tr.append($('<td />').html(image.outerHTML));
});
$tbody.append($tr);
$.each(attributes, function (ignored, attribute) {
$tr = $('<tr />');
$.each(pet, function (i, p) {
// i == index, p == pet[i]
$tr.append($('<td />').text(p[attribute]));
});
$tbody.append($tr);
});
}
$(function () {
$('#generateTable').click(function () {
generateTable('#tableHere');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="generateTable">Generate table</button>
<div id="tableHere"></div>
Hope that helps.
Why can't the console and debugger find any errors yet the page is obviously not working.
There are no errors in code, it just doesn't make, what you'd like it to do.
How would I label these kinds of problems in the future?What was at fault? The button? The function? Both? Neither? Myself?
There were a few critical errors, hard to point out one deciding.
I'm planning to make the table in a vertical column format instead of horizontal. Would this be problematic to do? (I'm thinking a few more for loops should do the trick without a fuss)
Making it horizontal may be even easier.
I'd like to use a pre-constructed array as a source for data(e.g pet[i].size but would this work or would it be better to dis-assemble the array into variables in the function?
It is good idea to make a variable for pet[i], if you are going to use it a few times. If you meant making separate tables for size, name etc. - no, there is no need for that.
if I do need to make variables for all the data would I be able to recycle or reuse a variable if it's inside one of the loops?
I'm not sure what do you mean, but probably answer is yes. In javascript variables are local to function, and all loops, ifs etc. inside one function use the same variables.
In http://jsfiddle.net/ahEkH/1/ why is "tbdy" a child of tab or is appendChild used to assign "tbdy" to tab?
tbdy is a child of table element tab, which means that it is inside it.
Ok, now problems with your code:
Code inside while never executes. You have assignment f=0 instead of equality check f==0 (or better f===0) in condition. f=0 has value 0, so it is false. And also, there is no need for loop at all, if you want it to execute exactly once.
document.write is not going to work sensibly while called from onclick. document.write does make sense almost exclusively when called directly in <script> tag, and should be avoided in general.
Your calls to document.getElementById just return object which is then forgotten - they don't have any effect.
You call buildTable with argument pet, but buildTable does not take any arguments (it is not a big problem now, because pet seems to be global variable, but it may be misleading).
You don't declare i as local variable, so it is global. It will work, but in very bug-prone manner.
Putting <br> tag inside <table> does make no sense and is incorrect, and </br> (as you write it) isn't even legal tag.
You might want to put newline character (\n) there, but it is not needed, you don't have to separate html tags.
Your code is very repetitive, with lots of almost identical for loops.
Indent your code, it will make it much nicer to read.
Possible solutions using approach similar to yours (building html piece by piece from strings):
(before using (not recommended), read carefully notes below)
var pet = [
{
species: 'species1',
name: 'name1',
colour: 'colour1',
size: 'size1',
},
{
species: 'species2',
name: 'name2',
colour: 'colour2',
size: 'size2',
},
];
function makeTr(pet, attrName) {
var html = '',
i;
html += "<tr>";
for (i = 0; i < pet.length; ++i) {
html += "<td>" + pet[i][attrName] + "</td>";
}
html += "</tr>";
return html;
}
function buildTable(pet) {
var html='';
html += "<table border='4px'>";
html += "<caption>Pets Avaliable</caption>";
html += makeTr(pet, "species");
html += makeTr(pet, "name");
html += makeTr(pet, "colour");
html += makeTr(pet, "size");
html += "</table>"
document.getElementById("work").innerHTML = html;
}
(jsfiddle)
Note, that it is far from being the best solution, and putting html manually
may be considered bad by principle (althought it may be a bit faster and
could be justified in some cases). In general, creating html tag with
document.createElement seems cleaner and less error-prone way to do that.
Specifically, code above may possibly be subject to XSS attack, because values put into html are not sanitized. Paul's solution is not affected by this problem - note the use of text() method, which escapes html code.