Not sure I quite understand variable scope in Javascript - javascript

Sorry if this seems a bit easy. I'm still relatively new to Javascript.
I am generating a list of checkboxes. On the onClick of a checkbox, i want to make it pop up its associated text. ie. Checkbox named "one" should then display "one". In my example it only displays "two".
However the click() callback method only ever calls the text of the last added checkbox. Does the 'v' variable in here not get assigned per checkbox? It seems like the'v' is behaving like a global variable.
this.view = document.createElement("div");
var tbody = document.createElement("tbody");
var popupValues = {"A", "B"};
for (var i=0;i<this.popupValues.length;i++) {
var v = popupValues[i];
var tr = document.createElement('tr');
var tdCheck = document.createElement('td');
var ChkBx = document.createElement('input')
ChkBx.type = 'checkbox';
tdCheck.appendChild(ChkBx);
var self = this;
$(ChkBx).live('change', function(){
if($(this).is(':checked')){
alert('checked' + v);
} else {
alert('un-checked' + v);
}
});
var td = document.createElement("td");
td.appendChild(document.createTextNode('' + v));
tr.appendChild(tdCheck);
tr.appendChild(td);
tbody.appendChild(tr);
}
table.appendChild(tbody);
document.appendChild(table)
Here is jsfiddle :
http://jsfiddle.net/n5GZW/2/
Anyone know what I am doing wrong?
UPDATE: updated JSFiddle

"Does the 'v' variable in here not get assigned per checkbox?"
Well, it's assigned, but not declared for each checkbox.
In Javascript variables only have function scope. Even if you try to create a new variable in each iteration of the loop, it's only a single variable declared at the function level, shared by all iterations. The declaration is hoisted to the function level, only the assignment happens inside the loop.
You can use an immediatey executed function expression to create another scope inside the loop, where you can create a new variable for each iteration:
for (var i=0;i<this.popupValues.length;i++) {
(function(v){
// code of the loop goes in here
// v is a new variable for each iteration
}(popupValues[i]));
}

you can do
var table = document.createElement("table");
var tbody = document.createElement("tbody");
var popupValues = [
"one", "two"
];
for (var i = 0; i < popupValues.length; i++) {
var v = popupValues[i];
var tr = document.createElement('tr');
var tdCheck = document.createElement('td');
var ChkBx = document.createElement('input');
ChkBx.type = 'checkbox';
ChkBx.value=v;
tdCheck.appendChild(ChkBx);
var td = document.createElement("td");
td.appendChild(document.createTextNode('' + v));
tr.appendChild(tdCheck);
tr.appendChild(td);
tbody.appendChild(tr);
var self = this;
$(ChkBx).click('change', function () {
if ($(this).is(':checked')) {
alert('check ' + $(this).val());
} else {
alert('un-checked');
}
});
}
table.appendChild(tbody);
document.body.appendChild(table)
http://jsfiddle.net/n5GZW/4/
add ChkBx.value=v; to get value like $(this).val() on click

You could've searched in SO for event binding in for loop.
Here is one solution:
Try this:
this.view = document.createElement("div");
var tbody = document.createElement("tbody");
var popupValues = {"A", "B"};
for (var i=0;i<this.popupValues.length;i++) {
var v = popupValues[i];
var tr = document.createElement('tr');
var tdCheck = document.createElement('td');
var ChkBx = document.createElement('input')
ChkBx.type = 'checkbox';
tdCheck.appendChild(ChkBx);
var self = this;
(function(val) {
$(ChkBx).on('change', function(){
if($(this).is(':checked')){
alert('checked' + val);
} else {
alert('un-checked' + val);
}
});
})(v);
var td = document.createElement("td");
td.appendChild(document.createTextNode('' + v));
tr.appendChild(tdCheck);
tr.appendChild(td);
tbody.appendChild(tr);
}
table.appendChild(tbody);
document.appendChild(table);

Related

Send row data when clicking button?

I have the next information object
var data = [
{
name: 'John Doe',
transactions: [{...},{...},{...}]
},
{...}
];
Im inserting this data into a table like this:
function addRowsData(data) {
var table = document.getElementById('main-table');
var rowCount = table.rows.length;
var tBody = document.createElement('tbody');
table.appendChild(tBody);
for (var i = 0; i < data.length; i++) {
var info = data[i];
var tr = tBody.insertRow();
for(var key in info){
if(key == 'transactions') continue;
var td = document.createElement('td');
td.innerHTML = info[key];
tr.appendChild(td);
}
var tdButton = document.createElement('td');
var button = document.createElement('button');
var clientId = info.id;
button.setAttribute('type', 'button');
button.setAttribute('class', '"btn btn-primary');
button.setAttribute('id', clientId);
button.innerHTML = 'More'
tdButton.appendChild(button);
tr.appendChild(tdButton)
button.addEventListener('click', function() {
openModal(info.transactions);
});
}
}
function openModal(transactions) {
console.log(transactions);
var modal = document.getElementById('my-modal');
var close = document.getElementsByClassName('close')[0];
close.addEventListener('click', closeModal, false);
modal.style.display = 'block';
}
Each row has a button and when is clicked opens a modal, on this modal I want to show the data from the transactions array, I tried doing it like this openModal(info.transactions); but this always shows the data from the last element. Is there a way to accomplish this, that when I click on the row button, I can access to the transactions array?
In your code, you reference info in the event listener. However, the variable info changes throughout the for loop, so at the end of the for loop, info will be the last index. To fix this, maybe instead of passing info in, you can pass in eval(data[i]), so it will input the actual data instead of a variable which changes.

Remove a duplicate row in table when being added from an array?

I have something like this:
getELE("btnAddStudent").addEventListener("click", function(){
var ID = getELE("txtID").value;
var Name = getELE("txtName").value;
var Score = getELE("txtScore).value;
var St = new Student(ID, Name, Score);
List.Add(St);
var table = getELE("tbodyStudent");
for (var i = 0; i < List.arrSt.length; i++) {
var tr = document.createElement("tr");
for (var key of ['ID', 'Name', 'Score'])
{
var td = document.createElement("td");
td.innerHTML = List.arrSt[i][key];
tr.appendChild(td);
}
table.appendChild(tr);
}
});
The problem is whenever I add a new student, the table will add a whole list of students instead of adding just the new student to it making it have duplicate students.
How do I add just the new student instead of the whole list?
I have tried to tweak this into my "for" loop but still doesn't work.
Your code seems that you add whole list again to your table.
Here's updated code. Please try this.
getELE("btnAddStudent").addEventListener("click", function(){
var ID = getELE("txtID").value;
var Name = getELE("txtName").value;
var Score = getELE("txtScore).value;
var St = new Student(ID, Name, Score);
var table = getELE("tbodyStudent");
var tr = document.createElement("tr");
for (var key of ['ID', 'Name', 'Score'])
{
var td = document.createElement("td");
td.innerHTML = St[key];
tr.appendChild(td);
}
table.appendChild(tr);
List.Add(St);
});
The solution for this is add the tr when you are adding a new student.
var table = getELE("tbodyStudent");
var tr = document.createElement("tr");
for (var key of ['ID', 'Name', 'Score'])
{
var td = document.createElement("td");
// add here the new Student data
td.innerHTML = newStudentData[key];
tr.appendChild(td);
}
table.appendChild(tr);
I agree with above solution.
It is a good solution Adding a row when you add a new student.

Javascript Loop Button always takes last value

I have a function that crawls infos from tmdb. i want to work with that response. i do for example search for a movie and want to put the results(titles and the poster-images) into a table with a button.
That works fine.
For that i have a loop that creates a table with entries every loop. In 1 entry i have a button, that is supposed to call another function and give it the value(form the poster-path the movie has) that is set in the specific loop. Works partially...Problem is: the value given to the new fuction is always the last value (value of the last loop). I dont know how to fix that :/
I already tried an "Immediately-Invoked Function Expression"(found in this forum) which looks like this
for (var i = 0; i < 3; i++) {
(function(index) {
console.log('iterator: ' + index);
})(i);
}
Same Problems still :/
thats my script ("bbcode" and "imglink" were set before this)
if (this.status === 200) {
var jsonResponse = JSON.parse(this.responseText);
totalresults = jsonResponse.total_results;
if (totalresults === 0){ noresult(); }
if (totalresults === 1){ oneresult(); }
else {
var x = document.createElement("TABLE");
x.setAttribute("id", "myTable");
x.setAttribute("border", "1");
document.getElementById("log").appendChild(x);
for (var j = 0; j < totaresults; j++) {
(function(i) {
posterpath = jsonResponse.results[i].poster_path;
newbbcode = bbcode.replace(imglink, "http://image.tmdb.org/t/p/w400"+ posterpath);
var y = document.createElement("TR");
y.setAttribute("id", "myTr" + i);
document.getElementById("myTable").appendChild(y);
var z = document.createElement("TD");
var t = document.createTextNode(jsonResponse.results[i].title);
z.appendChild(t);
document.getElementById("myTr" + i).appendChild(z);
var z = document.createElement("TD");
var t = document.createElement("IMG");
t.setAttribute("src", "http://image.tmdb.org/t/p/w92"+jsonResponse.results[i].poster_path);
z.appendChild(t);
document.getElementById("myTr" + i).appendChild(z);
var z = document.createElement("TD");
var t = document.createElement("INPUT");
t.setAttribute("type","button");
t.setAttribute("value","pick this");
t.addEventListener("click", function(){workwithnewbbcode(newbbcode)} );
z.appendChild(t);
document.getElementById("myTr" + i).appendChild(z);
})(j);
}
}
Maybe someone has a very simple (noob friendly^^) idea to do that with javascript.
Thank you guys!
EDIT: Thanks to Jaromanda X for the solution!
instead of
newbbcode = bbcode.replace(imglink, "http://image.tmdb.org/t/p/w400"+ posterpath);
just add var
var newbbcode = bbcode.replace(imglink, "http://image.tmdb.org/t/p/w400"+ posterpath);
OR
if (this.status === 200) {
var jsonResponse = JSON.parse(this.responseText);
totalresults = jsonResponse.total_results;
if (totalresults === 0) {
noresult();
} else if (totalresults === 1) {
oneresult();
} else {
var table = document.createElement("TABLE");
table.id = "myTable";
table.border = "1";
jsonResponse.results.forEach(function(result) {
var posterpath = result.poster_path;
var newbbcode = bbcode.replace(imglink, "http://image.tmdb.org/t/p/w400" + posterpath);
var tr = document.createElement("TR");
var td1 = document.createElement("TD");
td1.appendChild(document.createTextNode(result.title));
tr.appendChild(td1);
var td2 = document.createElement("TD");
var img = document.createElement("IMG");
img.src = "http://image.tmdb.org/t/p/w92" + posterpath;
td2.appendChild(img);
tr.appendChild(td2);
var td3 = document.createElement("TD");
var input = document.createElement("INPUT");
input.type = "button";
input.value ="pick this";
input.addEventListener("click", function() {
workwithnewbbcode(newbbcode);
});
td3.appendChild(input);
tr.appendChild(td3);
table.appendChild(tr);
});
document.getElementById("log").appendChild(table);
}
}
Consider the following example:
for (var i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},1000);
}
You will always get 10 in the output because by the time the nested function is called, the variable i already reached the end of the loop.
In your case, your response object arrives in an XHR callback. This has the same flow as setTimeout. You can create a function wrapper to keep the iterator variable in the call stack:
var myfunc=function(x){ //a function that returns a function
setTimeout(function(){
console.log(x);
},1000);
}
for (var i=0;i<10;i++) myfunc(i);

adding EventListener in a loop with index as argument at each iteration

When creating a table with JavaScript,
I'm trying to set an event listener in a loop.
the event listener in each iteration should call the same function but with the current index as an argument.
I found here that I can use closures with a function array but do I have to use an array? I don't understand why the below code is not working?
function createTable(id, headers, keys, data, url) {
var table = document.createElement("TABLE");
var header = table.createTHead();
var tr = header.insertRow(0);
var i, j, td;
var headersLength = headers.length;
for (i = 0; i < headersLength; i++) {
th = document.createElement('th');
th.innerText = headers[i];
tr.appendChild(th);
var tbodyId = "t_body";
var sortFunction = createSortFunction(tbodyId, i);
th.addEventListener('click', function() {
sortFunction;
});
}
function createSortFunction(tbodyId, i) {
return function() { alert(i); };
}
You don't need to wrap sortFunction in a function because it is holding a function which you can pass directly to addEventListener
th.addEventListener('click', sortFunction);

How to access dynamically created button?

btnUpdate = document.createElement("BUTTON");
var t = document.createTextNode("Update");
btnUpdate.id = 'update0';
btnUpdate.appendChild(t);
tabCell.appendChild(btnUpdate);
I have a simple line of code where I have created a button with my javascript. How do I access this button through the same javascript file? I want to add onClick feature to it.
document.getElementById("update0").onclick = edit_row(0);
I tried doing so by adding the above line of code, but now it won't display the table but straight away jumps to the edit_row() function.
Edit:
function showCustomer() {
var obj, dbParam, xmlhttp, myObj, x, txt = "",tabCell;
var btnUpdate;
obj = { "table":"Successful", "limit":20 };
dbParam = JSON.stringify(obj);
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
myObj = JSON.parse(xmlhttp.responseText);
console.log(myObj);
var col = [];
for (var i = 0; i < myObj.length; i++) {
for (var key in myObj[i]) {
if (col.indexOf(key) === -1) {
col.push(key);
}
}
}
key="Update";
col.push(key);
console.log(col);
// CREATE DYNAMIC TABLE.
var table = document.createElement("table");
// CREATE HTML TABLE HEADER ROW USING THE EXTRACTED HEADERS ABOVE.
var tr = table.insertRow(-1); // TABLE ROW.
for (var i = 0; i < col.length; i++) {
var th = document.createElement("th"); // TABLE HEADER.
th.innerHTML = col[i];
tr.appendChild(th);
}
// ADD JSON DATA TO THE TABLE AS ROWS.
for (var i = 0; i < myObj.length; i++) {
tr = table.insertRow(-1);
tabCell = null;
for (var j = 0; j < col.length-1; j++) {
tabCell = tr.insertCell(-1);
tabCell.innerHTML = myObj[i][col[j]];
}
tabCell = tr.insertCell(-1);
btnUpdate = document.createElement("BUTTON");
var t = document.createTextNode("Update");
btnUpdate.id = 'update'+i;
btnUpdate.appendChild(t);
tabCell.appendChild(btnUpdate);
}
tr = table.insertRow(-1);
tabCell = null;
for (var j = 0; j < col.length-1; j++) {
tabCell = tr.insertCell(-1);
tabCell.innerHTML = " ";
}
tabCell = tr.insertCell(-1);
var btn = document.createElement("BUTTON");
var t = document.createTextNode("Add Row");
btn.appendChild(t);
tabCell.appendChild(btn);
document.getElementById("update0").addEventListener = function (){
edit_row(0);
};
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table);
}
};
xmlhttp.open("POST", "http://localhost:8090/Vaccine", true);
xmlhttp.setRequestHeader("Content-type", "application/JSON");
xmlhttp.send("x=" + dbParam);
}
function edit_row(no)
{
alert("HELLO");
}
With this line :
document.getElementById("update0").onclick = edit_row(0);
You are not "attaching" the click event to the edit_row function. You're setting the onclick property with the result of the edit_row(0) invokation.
Also, don't use the onclick property.
Use the addEventListener function instead.
document.getElementById("update0").addEventListener("click", function () {
edit_row(0);
});
If you need a reason : by overwriting the onclick property, you could be disabling any other click event listener on your elements. By using addEventListener(), you can have several events listener on the same element/event couple.
And you can do this right after you created the button. You don't need to get it by its id later.
Your code would look like this :
btnUpdate = document.createElement("BUTTON");
var t = document.createTextNode("Update");
btnUpdate.id = 'update0';
btnUpdate.appendChild(t);
btnUpdate.addEventListener("click", function () {
edit_row(0);
});
You have to do that in callback of on click event. If you inline, it executes directly when javascript reading your code.
document.getElementById("update0").onclick = function (){
edit_row(0);
};
How do I access this button through the same javascript file?
The same way you've been accessing it all along.
It is stored in the btnUpdate variable. Use that.
but now it won't display the table but straight away jumps to the edit_row() function.
That is because you are calling edit_row and setting its return value as the click handler.
Since you want to pass arguments to it, the easiest thing to do is to create a new function.
function edit_row_first_argument_0 () {
edit_row(0);
}
button.addEventListener("click", edit_row_first_argument_0);
(You can use an anonymous function expression, I use the verbose approach above for clarity).
Try this:
btnUpdate = document.createElement("BUTTON");
var t = document.createTextNode("Update");
btnUpdate.id = 'update0';
btnUpdate.appendChild(t);
tabCell.appendChild(btnUpdate);
btnUpdate.addEventListener("click", (e) => {
// this linked to btnUpdate
// Here make whatever you want
// You can call edit_row now
edit_row(0)
})
It seems that your button is not in the DOM yet, so you are not able to find it with document. You can use the variable btnUpdate if it is in the same file like btnUpdate.onclick = function() {}, or using addEventListenerlike btnUpdate.addEventListener('click', function() {}).
Also, it seems you are executing the edit_row(0) function. You need to put it inside a function like
btnUpdate.addEventListener('click', function() {
edit_row(0);
})
You call the function when you have () at the end so
document.getElementById("update0").onclick = edit_row(0);
will immediately call edit_row
Why not do this instead:
btnUpdate = document.createElement("BUTTON");
var t = document.createTextNode("Update");
btnUpdate.id = 'update0';
btnUpdate.onclick=function() {
edit_row(this.id.replace("update","")); // or use a data-attribute
}
btnUpdate.appendChild(t);
tabCell.appendChild(btnUpdate);
or use event delegation:
Native JS equivalent to jquery delegation

Categories