Displaying table using Javascript breaks inside for loop - javascript

I have a code to populate a table using Javascript as:
var myResponse = document.getElementById("jsonPlaceHolder");
myResponse.innerHTML += "<table border=1> <tr> <td> User Id </td> <td> Question </td> <td> Link Question </td> </tr>";
for (var i = 0; i < 25; i++) {
myResponse.innerHTML += "<tr>"
myResponse.innerHTML += "<td>" + jsonObj[i]["user_id"] + "</td>";
myResponse.innerHTML += "<td>" + jsonObj[i]["text"] + "</td>";
myResponse.innerHTML += "</tr>"
}
myResponse.innerHTML += "</table>";
Problem with this code is when I run this table is not continued inside for loop. If I add
myResponse.innerHTML += "<table><tr>"
inside my for loop, table is created. Isn't this bit odd?,
since i am using += to add to current innerHTML of the DOM element.

One of the most misunderstood thing about innerHTML stems from the way the API is designed. It overloads the + and = operators to perform DOM insertion. This tricks programmers into thinking that it is merely doing string operations when in fact innerHTML behaves more like a function rather than a variable. It would be less confusing to people if innerHTML was designed like this:
element.innerHTML('some html here');
unfortunately it's too late to change the API so we must instead understand that it is really an API instead of merely an attribute/variable.
When you modify innerHTML it triggers a call to the browser's HTML compiler. It's the same compiler that compiles your html file/document. There's nothing special about the HTML compiler that innerHTML calls. Therefore, whatever you can do to a html file you can pass to innerHTML (the one exception being that embedded javascript don't get executed - probably for security reasons).
This makes sense from the point of view of a browser developer. Why include two separate HTML compilers in the browser? Especially considering the fact that HTML compilers are huge, complex beasts.
The down side to this is that incomplete HTML will be handled the same way it is handled for html documents. In the case of elements not inside a table most browsers will simply strip it away (as you've observed for yourself). That is essentially what you're trying to do - create invalid/incomplete HTML.
The solution is to provide innerHTML with complete HTML:
var htmlString = "<table border=1> <tr> <td> User Id </td> <td> Question </td> <td> Link Question </td> </tr>";
for (var i = 0; i < 25; i++) {
htmlString += "<tr>"
htmlString += "<td>" + jsonObj[i]["user_id"] + "</td>";
htmlString += "<td>" + jsonObj[i]["text"] + "</td>";
htmlString += "</tr>"
}
htmlString += "</table>"
myResponse.innerHTML += htmlString;

Use the DOM API to manipulate the DOM:
var myResponse = document.getElementById("jsonPlaceHolder");
var table = document.createElement('table'),
headings = ["User ID", "Question", "Link Question"];
table.style.border = "1";
var r = table.insertRow(-1);
for (var i = 0; i < 3; i++) {
(function(){
return r.insertCell(-1);
})().innerHTML = heading[i];
}
for (var i = 0; i < 25; i++) {
r = table.insertRow(-1);
var userid = r.insertCell(-1),
text = r.insertCell(-1);
userid.innerHTML = jsonObj[i]["user_id"];
text.innerHTML = jsonObj[i]["text"];
}
myResponse.appendChild(table);

Related

textarea clears when using innerhtml [duplicate]

I have a problem concerning multiple file uploads in javascript. I am trying to create my own multiple file upload by dynamically adding inputs. This is all easy as pie, but the problem is that whenever I add a new , my previous input-fields of the type "file" get reset.
If I remove the last lines of code where I alter the innerHTML of my parent div, the values of my do not get reset. Does anyone know how this problem can be solved? The javascript code can be found below. Thanks in advance.
if(document.getElementById("upload_queue").innerHTML.indexOf(_item) == -1)
{
var _row = "<tr id='queue_row_" + items_in_queue + "'>";
_row += "<td>";
_row += "<div class='remove_uploaded_image' onclick='remove_from_queue(" + items_in_queue + ")'></div>";
_row += "</td>";
_row += "<td>";
_row += _item;
_row += "</td>";
_row += "</tr>";
document.getElementById("upload_queue").innerHTML += _row;
document.getElementById("upload_image_" + items_in_queue).style.display = "none";
items_in_queue++;
document.getElementById("uploader_holder").innerHTML +=
'<input id="upload_image_' + items_in_queue +
'" name="upload_image_' + items_in_queue + '" accept="image/jpeg" type="file"' +
'onchange="add_to_upload_queue()" style="display: inline;" />';
}
Yeah... you're going to want to use appendChild instead of modifying the inner HTML:
var myInput = document.createElement("INPUT");
// do stuff to my input
var myContainer = document.getElementById("uploader_holder");
myContainer.appendChild(myInput);
That's the general gist of what you have to do - let me know if you need somethign more specific, but it looks like you've got a good hold on JS already... You're going to want to do that in almost all cases rather than setting inner HTML... So, building your TR as well... you'll have to append the TD to the TR, you'll have to append the TD with your input, you'll have to append your targeted table with the TR, etc.

Create javascript function with JQuery

I'm getting JSon data from my server and building a table with this data. The last column of the table is a button that will grab the fields from the selected row to populate another fields, but it is returning: ReferenceError: response.DATA is not defined.
response = JSON.parse(response);
$('.myDiv').empty();
// Header
var table = '<table class="table table-striped"><thead><tr><th>First Name</th><th>Last Name</th><th>City</th><th>State</th><th>ZIP</th><th>Action</th></tr></thead><tbody>';
var i;
for(i=0; i<response.ROWCOUNT; i++){
table += '<tr>';
table += '<td>' + response.DATA.PROVIDERFIRSTNAME[i] + '</td>';
table += '<td>' + response.DATA.PROVIDERLASTNAME[i] + '</td>';
table += '<td>' + response.DATA.PROVIDERCITY[i] + '</td>';
table += '<td>' + response.DATA.PROVIDERSTATE[i] + '</td>';
table += '<td>' + response.DATA.PROVIDERPOSTALCODE[i] + '</td>';
table += '<td><input type="button" class="btn btn-primary" value="select" onClick="setData(response.DATA, i);" /></td>';
table += '</tr>';
}
table += '</tbody></table>';
$('.myDiv').append(table);
My setData function:
function setData(data, pos){
console.debug(data.PROVIDERFIRTNAME[pos]);
}
You're using an inline handler, and an inline handler can only reference global variables. Attach the listener properly using Javascript instead, so that it can reference the variables (most importantly, response and i) properly. Also make sure i is block scoped with let, rather than function-scoped.
You can also consider using template literals to make the code a lot more readable:
// Header
const $table = $('<table class="table table-striped"><thead><tr><th>First Name</th><th>Last Name</th><th>City</th><th>State</th><th>ZIP</th><th>Action</th></tr></thead><tbody></tbody>');
// make sure to use "let i" here
for (let i = 0; i < response.ROWCOUNT; i++) {
const $row = $(`
<tr>
<td>${response.DATA.PROVIDERFIRSTNAME[i]}</td>
<td>${response.DATA.PROVIDERLASTNAME[i]}</td>
<td>${response.DATA.PROVIDERCITY[i]}</td>
<td>${response.DATA.PROVIDERSTATE[i]}</td>
<td>${response.DATA.PROVIDERPOSTALCODE[i]}</td>
<td>${response.DATA.PROVIDERPOSTALCODE[i]}</td>
<td><input type="button" class="btn btn-primary" value="select"/></td>
</tr>
`);
$row.find('input').on('click', () => {
console.debug(response.DATA.PROVIDERFIRSTNAME[i]);
});
$table.find('tbody').append($row);
}
$('.myDiv').append(table);
Spelling matters - make sure to use PROVIDERFIRSTNAME instead of PROVIDERFIRTNAME.
Inline handlers are pretty universally considered to be poor practice - best to avoid them whenever possible.

Adding button that removes object from array inside a table

Created a form and empty table, trough DOM i should create a new object from class Movie and insert it in empty table with a button to remove it.
Don't know how i would create the button with a function to remove the movies[i] in the table. Sorry I am still learning and don't know how to express my problems.
window.onload = function(){
let frmMovies = document.getElementById("frmMovies");
let txtTitle = document.getElementById("txtTitle");
let txtYear = document.getElementById("txtYear");
let txtGender = document.getElementById("txtGender");
frmMovies.addEventListener("submit", function (event) {
let newMovie = new Movies(txtTitle.value, txtYear.value, txtGender.value);
movies.push(newMovie);
refreshTable();
/*let btn = document.getElementsByClassName("btn");
btn.addEventListener("click", function (event) {
movies.splice(x, 1);
event.preventDefault();
})*/
event.preventDefault();
})
}
function refreshTable() {
let movieTable = document.getElementById("movieTable");
let txt = "";
txt = "<tr><th>TÍTULO</th><th>ANO</th><th>GÉNERO</th><th>!</th></tr>";
for(let i = 0 ; i < movies.length; i++){
txt += "<tr>";
txt += "<td>" + movies[i].title + "</td>";
txt += "<td>" + movies[i].year + "</td>";
txt += "<td>" + movies[i].gender + "</td>";
txt += "<td>" + "<button class='btn'>" + "Remove" + "</button>" + "</td>";
txt += "</tr>";
}
movieTable.innerHTML = txt;
}
You could add an onclick attribute to your Remove buttons when you build it. Upon clicking on the button, it will call a function passing current button element i.e. this.
let movies = [];
// etc
function refreshTable() {
let movieTable = document.getElementById("movieTable");
let txt = "";
txt = "<tr><th>TÍTULO</th><th>ANO</th><th>GÉNERO</th><th>!</th></tr>";
for(let i = 0 ; i < movies.length; i++){
txt += "<tr>";
txt += "<td>" + movies[i].title + "</td>";
txt += "<td>" + movies[i].year + "</td>";
txt += "<td>" + movies[i].gender + "</td>";
txt += "<td>" + "<button type='button' class='btn' onclick='deleteRow(this)'>" + "Remove" + "</button>" + "</td>";
txt += "</tr>";
}
movieTable.innerHTML = txt;
}
Create your new function which will delete the current row using .removeChild().
function deleteRow(button) {
let row = button.parentElement.parentElement;
document.getElementById("movieTable").removeChild(row);
}
This is untested of course but you could do something like this.
Event Delegation is the most efficient way of using one element to listen for an event for multiple child/descendant elements. Simply find an ancestor (tbody) element that the group of target elements (buttons) have in common.
Demo
// Reference the <tbody> by its tagName
var tbody = document.querySelector('tbody');
// Register the click event on <tbody> calls removeRow when clicked
tbody.addEventListener('click', removeRow, false);
// Pass through the Event Object
function removeRow(event) {
/* Event Object property Event.target always knows which
|| element was actually clicked. Use event.target to compare
|| or assert a true/false check in order to target the rest of
|| the DOM in reference to event.target.
|| Find the closest <tr> from event.target
|| `this` is the function owner <tbody> which removes the <tr>
*/
if (event.target.className === 'btn') {
var row = event.target.closest('tr');
this.removeChild(row);
}
return false;
}
<table>
<tbody>
<tr>
<td>Reservoir Dogs</td>
<td>1992</td>
<td>Male?</td>
<td><button class='btn'>Remove</button></td>
</tr>
<tr>
<td>The Usual Suspects</td>
<td>1995</td>
<td>Male?</td>
<td><button class='btn'>Remove</button></td>
</tr>
<tr>
<td>Pulp Fiction</td>
<td>1994</td>
<td>Male?</td>
<td><button class='btn'>Remove</button></td>
</tr>
<tr>
<td>Sin City</td>
<td>2005</td>
<td>Male?</td>
<td><button class='btn'>Remove</button></td>
</tr>
<tr>
<td>Old Boy</td>
<td>2003</td>
<td>Male?</td>
<td><button class='btn'>Remove</button></td>
</tr>
</tbody>
</table>
Since you're new to this, I would recommend really focusing on understanding the difference between an actual DOM element and the HTML tags that are used to create them.
The DOM is the actual mechanism that determines what is displayed on the page. HTML is just a way of serializing a DOM into text, so you can easily send it over networks to other computers. A web browser parses HTML and recreates the DOM on the client's computer.
The DOM consists of JavaScript objects that you can manipulate with Javascript. When you set movieTable.innerHTML, what you're doing is telling the browser: to do the following:
Parse the assigned string as HTML.
Delete all of the children of movieTable.
Add the new DOM elements parsed from the assigned string as children of movieTable.
This is wasteful because you're recreating the entire table every time you do it, even though you may only be adding or removing a single element. It also can be frustrating to maintain, since JS does not play well with long string literals. This is one of the reasons why stuff like this is often handled by templating libraries.
I'll keep it simple though, and show you how you can do this with just the standard DOM methods:
let frmMovies = document.getElementById("frmMovies");
let txtTitle = document.getElementById("txtTitle");
let txtYear = document.getElementById("txtYear");
let txtGender = document.getElementById("txtGender");
// You don't need to keep getting this every time.
// Just get it once and reuse the reference.
let movieTable = document.getElementById("movieTable");
frmMovies.addEventListener("submit", function (event) {
// You can do this anytime, as long as you do it before your
// handler returns. I usually do it right away.
event.preventDefault();
// First, let's create a new table row.
let movieRow = document.createElement("tr");
// Create the title cell.
let titleCell = document.createElement("td");
titleCell.innerText = txtTitle.value;
// Create the year cell.
let yearCell = document.createElement("td");
yearCell.innerText = txtYear.value;
// Create the gender cell.
let genderCell = document.createElement("td");
genderCell.innerText = txtGender.value;
// Create the remove button and a cell to put it in.
let buttonCell = document.createElement("td");
let removeButton = document.createElement("button");
removeButton.innerText = 'Remove';
// Attach the click handler for the remove button.
removeButton.addEventListener("click", function() {
// This handler will form a *closure*, which will store the
// reference to the movieRow, enabling you remove it by simply
// calling `remove` on it.
movieRow.remove();
});
// Now, let's put it all together:
// Add the remove button to its cell.
buttonCell.appendChild(removeButton);
// Add the cells to the table row.
movieRow.appendChild(titleCell);
movieRow.appendChild(yearCell);
movieRow.appendChild(genderCell);
movieRow.appendChild(buttonCell);
// Add the table row to the table.
movieTable.appendChild(movieRow)
});
MDN has some pretty solid documentation of standard HTML DOM that you'll probably want to check out:
https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model

Confusing behaviour on passing object as parameter to a method called from Table created in Javascript

I'm have a table with buttons in each row. On every button click I want to pass that row's data to a method. I've done the following
HTML
<body>
<div>
<button onclick="load()" >Click</button>
<table id="roleTable">
<thead>
<tr>
<td>EnterpriseId</td>
<td>Role</td>
<td>UserId</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</body>
Javascript
function load() {
var data = JSON.parse(sessionStorage.getItem("loginInfo"));
var html = "";
for (var i = 0; i < data.length; i++) {
html += "<tr><td>" + data[i].ENTERPRISE_ID + "</td>";
html += "<td><button type='button' onclick='loadSegment(data[" + i + "])'>...</button></td>";
html += "<td>" + data[i].USER_ID + "</td></tr>";
}
document.getElementById('roleTable').getElementsByTagName("TBODY")[0].innerHTML = html;
}
function loadSegment(o) {
alert('reading obj');
alert(JSON.stringify(o));
}
The problem is when I click on the button with given code, nothing happens.
But if I move the code of load() function out, the loadSegment method gets called and I'm able to use the object as I want.
Working JS
//function load() { // Commented the function call
var data = JSON.parse(sessionStorage.getItem("loginInfo"));
var html = "";
for (var i = 0; i < data.length; i++) {
html += "<tr><td>" + data[i].ENTERPRISE_ID + "</td>";
html += "<td><button type='button' onclick='loadSegment(data[" + i + "])'>...</button></td>";
html += "<td>" + data[i].USER_ID + "</td></tr>";
}
document.getElementById('roleTable').getElementsByTagName("TBODY")[0].innerHTML = html;
//}
function loadSegment(o) {
alert('reading obj');
alert(JSON.stringify(o));
}
I don't understand why through the second code the method is reachable but not from the first code. If I don't pass object as parameter in the first code then it is able to reach to the loadSegment() function.
Can anybody please help me understand what is the exact issue her. And how can I achieve the function call from the first JS code.
Update
JSON string
[{"USER_ID":"UID00007","ENTERPRISE_ID":"admin1","ROLE":"ADMIN"},
{"USER_ID":"UID00008","ENTERPRISE_ID":"admin2","ROLE":"TESTER"},
{"USER_ID":"UID00009","ENTERPRISE_ID":"admin3","ROLE":"REVIEWER"},
{"USER_ID":"UID00010","ENTERPRISE_ID":"admin4","ROLE":"PMO"},
{"USER_ID":"UID00011","ENTERPRISE_ID":"admin5","ROLE":"MANAGER"},
{"USER_ID":"UID00012","ENTERPRISE_ID":"admin6","ROLE":"DEVELOPER"},
{"USER_ID":"UID00013","ENTERPRISE_ID":"admin7","ROLE":"DBA"}]
In the first example, the data variable exists only within the scope of the load function:
function load() {
var data = JSON.parse(sessionStorage.getItem("loginInfo"));
//...
}
And then you try to reference it outside of that function:
onclick='loadSegment(data[0])'
It doesn't exist at this point, so you can't reference it. By contrast, in the second example you create the data variable in global scope, so it exists on the window object and can be referenced anywhere.
To encapsulate the data variable within that function, you'll need to supply the HTML you're creating with all of the information it needs to call the loadSegment function, not just an index reference. Maybe serialize the whole value into the function call? Maybe put values on data-* attributes? Maybe move all the code into the function except the data variable and keep that on window scope? You have options, depending on what you're trying to achieve overall.
Side note: You might not want to create something called load on the window object (or any other DOM object). If not actual problems, that could cause confusion with other code.
Changing the functionName will help
function loadfun()
{
var data = JSON.parse(sessionStorage.getItem("loginInfo"));
var html = "";
for (var i = 0; i < data.length; i++) {
html += "<tr><td>" + data[i].ENTERPRISE_ID + "</td>";
html += "<td><button type='button' onclick='loadSegment(data[" + i + "])'>...</button></td>";
html += "<td>" + data[i].USER_ID + "</td></tr>";
}
document.getElementById('roleTable').getElementsByTagName("TBODY")[0].innerHTML = html;
}
function loadSegment(o) {
alert('reading obj');
alert(JSON.stringify(o));
}

jquery mobile button refresh not working programmatically

I have a table that is added dynamically to the dom row by row. every row has a button in the last'' of the row. With the option to remove.
I am having problems getting the id of the button or any attribute. However the styles are applied.
function makeLeague(){
var tableStart = "<table style='min-width:100%;' id='league-table-custom' class='league-table ui-responsive' data-mode='reflow'><th>TEAM</th><th>PLD</th><th>W</th><th>D</th><th>L</th><th>GF</th><th>GA</th><th>GD</th><TH>PTS</TH><th></th>";
var tableEnd = "</table>";
var tableMid = '' ;
var secondtab = $('#demo1');
leagueSize = league.length;
for(k=0; k<league.length; k++){
tableMid += "<tr>";
for(i=0; i< 9; i++){
tableMid += "<td> " + league[k][i] + "</td>";
}
tableMid += "<td><input type='button' class='remove' value='"+k+"' id="+k+"></td></tr>";
}
secondtab.html(tableStart + tableMid + tableEnd).trigger('create');
$('#demo1').trigger('create'); //tried many combinations/and on own
$('.remove').button();
$('.remove').trigger('create');
}
$('.remove').on('click',function(){
console.log ($(this).attr.id);});
Does anyone have any suggestions where I am going wrong here. I read the jquery mobile docs and they state that the styled button get wrapped in a div. Would this effect my access? Or can I construct the function in a better way that will eliminate any issues.
I got this working I had to traverse the dom a little bit more. When getting the value I typed $('#pageone').on('click', 'table .remove',function(){ console.log ($(this).attr('id')); }); and it works. Not sure that is normal behavior and hope it works on all browsers. thanks for any input.
Instead of $('.remove').on('click',function(){
console.log ($(this).attr.id);});

Categories

Resources