I have this function:
function addButtonLookup() {
var element = document.getElementById("btnToolBar");
var index;
for (var i = 0; i < lookupArray.length; i++) {
index = i;
var btn = document.createElement('input');
btn.type = 'button';
btn.value = '' + lookupArray[i];
btn.name = 'btnLookup' + i;
btn.id = i;
btn.className = 'CommonButtonStyle';
element.appendChild(btn);
btn.onclick = function() {
debugger;
tblExcpression.WriteMathElement(lookupArray[i], lookupArray[i]);
};
}
}
onbutton click the i is undefined
Instead of this:
btn.onclick = function() {
debugger;
tblExcpression.WriteMathElement(lookupArray[i], lookupArray[i]);
};
Try this:
btn.onclick = (function(i) {
return function() {
debugger;
tblExcpression.WriteMathElement(lookupArray[i], lookupArray[i]);
}
})(i);
The issue with the first version is that the i variable is copied from the current scope. However the i variable varies in the current scope (it's part of a for loop), this is why you're getting this weird behavior.
By passing the i variable as a paremeter to a new function (like the second example) the current i variable is copied.
You should take a look at how Closures work in JavaScript.
Related
I am trying to pass the value of a button to a function when it is clicked. Because the buttons were created as a javascript element I'm not sure how to do it.
methods:{
createButtons() {
var i;
var rows =["9","8","7","6","5","4","3","2","1","0","•","="];
var elDiv = document.getElementById("myDIV");
for (i=0; i<12; i++){
var btn = document.createElement("BUTTON");
btn.value = i
btn.style.height = "40px"
btn.textContent = rows[i];
btn.onclick = buttonvalue;
elDiv.appendChild(btn);
}
var pressedbutton = document.getElementById("calculate");
pressedbutton.remove();
},
}
}
function buttonvalue(i){
alert(i);
}
This is an XY problem. Don't create DOM elements manually like this, that's what Vue is for.
But to answer your question, you can do something like this:
const captureI = i;
btn.onclick = () => buttonvalue(captureI);
I copied i into a new local variable because i changes value by the for loop.
Or you can just write the for loop like this instead:
for (let i = 0; i < 12; i++) {
// ... omitted code ...
btn.onclick = () => buttonvalue(i);
}
This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 4 years ago.
Im trying to dinamically insert names into the value of an input element. I try to pass players[i] as an argument for the function addPlayer but it says undefined. When I click on each name it should be passing it to the input field. Where am I failing?
var players = ["john","doe"];
function addPlayer(str) {
console.log(str);
document.getElementsByClassName("answerInput")[0].value += str;
}
for (var i = 0; i < players.length; i++) {
var node = document.createElement("div");
var textnode = document.createTextNode(players[i]);
node.appendChild(textnode);
node.classList.add("newPlayers");
document.getElementById("pushPlayer").appendChild(node);
node.addEventListener('click', function () {
addPlayer(players[i]);
}, false);
};
The problem is the variable i declared with the keyword var
The scope of a variable declared with var is its current execution context, so, the variable i will end the for-loop with the last value.
var players = ["john (click me)", "doe (click me)"];
for (var i = 0; i < players.length; i++) {
var node = document.createElement("div");
var textnode = document.createTextNode(players[i]);
node.appendChild(textnode);
node.classList.add("newPlayers");
document.getElementById("pushPlayer").appendChild(node);
node.addEventListener('click', function() {
console.log(i)
}, false);
};
<div id='pushPlayer'>
</div>
An alternative is declaring that variable with the statement let
Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function.
var players = ["john", "doe"];
function addPlayer(str) {
console.log(str);
document.getElementsByClassName("answerInput")[0].value += str;
}
for (let i = 0; i < players.length; i++) {
var node = document.createElement("div");
var textnode = document.createTextNode(players[i]);
node.appendChild(textnode);
node.classList.add("newPlayers");
document.getElementById("pushPlayer").appendChild(node);
node.addEventListener('click', function() {
addPlayer(players[i]);
}, false);
};
<input class='answerInput' placeholder='Enter answer'>
<div id='pushPlayer'>
</div>
Another alternative is using IIFE to return a function with a specific value/player.
var players = ["john", "doe"];
function addPlayer(str) {
console.log(str);
document.getElementsByClassName("answerInput")[0].value += str;
}
for (var i = 0; i < players.length; i++) {
var node = document.createElement("div");
var textnode = document.createTextNode(players[i]);
node.appendChild(textnode);
node.classList.add("newPlayers");
document.getElementById("pushPlayer").appendChild(node);
node.addEventListener('click', (function(player) {
return function () {
addPlayer(player);
}
})(players[i]), false);
};
<input class='answerInput' placeholder='Enter answer'>
<div id='pushPlayer'>
</div>
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
I have a problem in the following JavaScript function.
I am trying to create buttons dynamically based on details from the results variable.
The button are created and an event is attached but it seems each button has the exact same event attached.
I need the address variable to be different for each event attached to a button and for that button then to be added to replace text in my macField variable.
function (results) {
var r;
var x, i;
var btn;
for (i = 0; i < results.length; i++) {
app.display("Paired to" + results[i].name + results[i].address);
x = document.getElementById("message2");
r = results[i].address;
w = document.getElementById('macField').value;
btn = document.createElement('input');
btn.onclick = function () {
document.getElementById('macField').value = r;
};
btn.setAttribute('type', 'button');
btn.setAttribute('name', 'sal' + [i]);
btn.setAttribute('id', 'Button' + [i]);
btn.setAttribute('value', results[i].name);
appendChild(btn);
}
}
function (error) {
app.display(JSON.stringify(error));
}
Use immediate function. Change this part:
btn.onclick = function () {
document.getElementById('macField').value = r;
};
like this:
(function(r){
btn.onclick = function () {
document.getElementById('macField').value = r;
};
})(r);
Have a look: http://jsfiddle.net/uebD8/1/
create your function seperately like below
function macFieldValue(val)
{
document.getElementById('macField').value = val;
}
and set button onclick attribute like this
btn.setAttribute('onclick', 'macFieldValue('+r+')');
Just modifying this part:
btn = document.createElement('input');
btn.onclick = function () {
document.getElementById('macField').value = r;
};
to
btn = document.createElement('input');
btn.setAttribute('some_id', r);
btn.addEventListener('click',
function (e) {
var addr = e.source.some_id;
document.getElementById('macField').value = addr;
}, false);
You are hitting an unfortunate curse of JS. Your code would have worked if it wasn't within a for-loop. In any case, you should be using addEventListener.
function B(sName) {
this.name = sName;
}
B.prototype = {
instanceCreatButtonCount: 0,
funA: function () { // alert instance's name
alert(this.name);
},
funB: function () { // create a button which clikced can alert this instance's name through funA;
var that = this;
B.prototype.instanceCreatButtonCount++;
var id = "_id" + that.instanceCreatButtonCount;
var str = "<button id='" + id + "' >clike me</button>";
var a = document.getElementById("btns");
a.innerHTML += str;
var btn = document.getElementById(id);
btn.onclick = function () {
that.funA();
};
}
};
var b1 = new B("Jim");
var divB1 = document.getElementById("b1");
divB1.onclick = function () {
b1.funB();
}
var b2 = new B("Dad");
var divB2 = document.getElementById("b2");
divB2.onclick = function () {
b2.funB();
}
After I click divB1, I create a button through b1.funB().
After I click divB2, I create a button througb b2.funB().
Why can only newest button alert name ? I find that other button's onclick function is null.
When you use a.innerHTML += str to append a new element, the entire subtree of a gets removed before the new elements are added again; the removal also unbinds any events you have added before.
It's better to use proper DOM functions in this case, i.e. var btn = document.createElement(), etc. and a.appendChild(btn).
Fiddle provided by #ShadowWizard: http://jsfiddle.net/qR6e8/