Only the last data in my for loop assigns - javascript

Hey I have a simple loop like this:
for(var i in nodes) {
var d = document.createElement('div');
d.className = 'box';
d.id = 'node' + i;
document.getElementById('node').appendChild(d);
document.getElementById('node'+ i).innerHTML = nodes[i].name;
document.getElementById('node'+ i).addEventListener('mousedown', function() {
var info = nodes[i]; display_parent(info);
}, false);
}
function display_parent(data){
console.log(data);
}
The problem is all the divs hold the same information aka the last one in the loop, i tried to assign the data to a local variable to info but it still does not work.
Any ideas how I can fix that?

It is due to closure in 'mousedown' event handler. You have to use something like this:
document.getElementById('node'+i).addEventListener('mousedown',
(function(node) {
return (function() {
display_parent(node);
});
}(nodes[i])), false);

Related

Defining javascript function while referencing local variable

Is it possible to use the current value of a variable when defining a javascript function?
In the following code I'd like to add a click function to an array of divs, each with a different value for i, ie. the first div should call setCurrentStyleIndex(0). At the moment they all call whatever the value of i is at the time of the call.
Sorry if this is dumb question.
Step3.populateStyleMenu = function() {
var stylePopup = $("#stylePopup");
for(var i = 0; i < Step3.fontStyles.length; i++) {
var div = $('<div>'+Step3.fontStyles[i][0]+'</div>');
div.css({
'font-weight': Step3.fontStyles[i][1],
'font-style': Step3.fontStyles[i][2],
})
div.data('style', Step3.fontStyles[i]);
div.addClass('personalizePopupItem personalizeStyleItem');
div.click(function() {
setCurrentStyleIndex(i);
});
stylePopup.append(div);
}
}
try to create a closure (not tested):
(function (i) {
div.click(function() {
setCurrentStyleIndex(i);
});
})(i);
Yup, you can do the below:
div.click((function() {
var inx = i;
return function() {
setCurrentStyleIndex(inx);
}
})());

javascript api simplified

Was using fourquare api to get venue, had previously had a clickable list written out from api but cut it down to just one venue name written to screen. Then decided it'd be best to just send it over to php. So when I did what I thought was logical cutting of the code, it stopped working completely.
My program has this, working:
$(document).ready(function doEverything(element) {
$.getJSON("https://api.foursquare.com/v2/venues/search?ll=" + lat + "," + lng + "&client_id=L2VWBKPOW45D5X3FJ3P4MJB5TGVJ4ST2J005RIVAFIWG44ND%20&client_secret=ZKDAOLHASCA31VUOGMBTAS3RFYUOMXL4IFFYPRURIDQA3QMA%20&v=20111107", function(data) {
one = data.response.venues[0].name;
var list = [];
list[0] = [one];
function Make() {
for (var i = 0; i < 1; i++) {
var div = document.createElement("div");
div.style.margin = "-435px 100px 0px 110px";
div.innerHTML = list[i];
!
function() {
var index = 0;
div.onclick = function() {
doSomething(this);
};
}();
document.body.appendChild(div);
}
}
function doSomething(element) {
var value = element.innerHTML;
switch (value) {
case one:
break;
}
}
Make();
});
});
Then I decided I wanted to pass a variable over to php using this:
theVar = 10; //just to make things simple.
urlString = "cookiestesttwo.php?var=" +theVar;
window.location = urlString;
So I tried to simplify my api code to this, and it stopped working:
$(document).ready() {
$.getJSON("https://api.foursquare.com/v2/venues/search?ll=" + lat + "," + lng + "&client_id=L2VWBKPOW45D5X3FJ3P4MJB5TGVJ4ST2J005RIVAFIWG44ND%20&client_secret=ZKDAOLHASCA31VUOGMBTAS3RFYUOMXL4IFFYPRURIDQA3QMA%20&v=20111107", function(data) {
one = data.response.venues[0].name;
document.write(one)
theVar = one
urlString = "cookiestesttwo.php?var=" + theVar;
window.location = urlString;)
};
};​
$(document).ready() { is not proper syntax and does throw errors.
Furthermore there was another syntax error at the end of the function. you reversed } and )
$(document).ready(function() {
$.getJSON("https://api.foursquare.com/v2/venues/search?ll=" + lat + "," + lng + "&client_id=L2VWBKPOW45D5X3FJ3P4MJB5TGVJ4ST2J005RIVAFIWG44ND%20&client_secret=ZKDAOLHASCA31VUOGMBTAS3RFYUOMXL4IFFYPRURIDQA3QMA%20&v=20111107", function(data) {
one = data.response.venues[0].name; // if one is local to this function then use VAR otherwise you'll leak it globally.
document.write(one);
theVar = one; // same here for the VAR keyword.
urlString = "cookiestesttwo.php?var=" + theVar; // and here as well. NO GLOBAL LEAKS!
window.location = urlString;
});
});
I threw a few more hints in the comments.
Your problem might be that you use document.write() when the DOM is already complete. You are not supposed to do that. Create an element document.createElement( "div" ) and set the innerText() and then append it to the dom. Much like you did before the refactor.
EDIT
I understand that it wasn't the document.write() but just do clarify what I was talking about I wrote a little refactor. I also threw out the theVar = one since that is redundant. Also make sure to declare your variables in the right scope. Therefore I added a var in front of the one.
$(document).ready(function() {
$.getJSON("https://api.foursquare.com/v2/venues/search?ll="+lat+","+lng+"&client_id=L2VWBKPOW45D5X3FJ3P4MJB5TGVJ4ST2J005RIVAFIWG44ND%20&client_secret=ZKDAOLHASCA31VUOGMBTAS3RFYUOMXL4IFFYPRURIDQA3QMA%20&v=20111107",
function(data){
var one = data.response.venues[0].name;
var div = document.createElement( "div" );
div.innerText( one );
document.appendChild( div );
window.location = "cookiestesttwo.php?var=" + one;
});
});
But if you change the location of the window. There is no point to document.write() or appending a new div since you leave the site anyways.

how to access object's member through a method which filled by another method?

I know the title is a little bit confusion, here is the details:
Say I have a custom object defined in javascript, and there is a public member defined in it:
function Test()
{
this.testArray = [];
}
And I have two methods for this object, one is read out some xml file and filled into the array:
Test.prototype.readXML = function()
{
var self = this;
$.get('assest/xml/mydata.xml', function(d){
$(d).find("item").each(function(){
var item = new Item;
item.ID = ($(this).attr("ID"));
item.body = ($(this).find("body").text());
});
self.testArray.push(item);
});
}
And another function, which will display the content into the HTML page.
Test.prototype.appendInfo = function()
{
var i;
for (i=0; i<testArray.length;i++)
{
$('#testdisplay').append(testArray[i].ID +"<br />");
$('#testdisplay').append(testArray[i].body = "<br /");
}
}
However, the display function continue gives me error that the testArray is not defined. I'm not sure where is the problem, since I put the display function behind the reading function. I expect that the data will be stored in the array and could be accessed anytime I need them.
Hope some one will kindly help me about this! Thank you!
}
}
So I notice two problems with your code.
First when you do your ajax call you need to pass a deferred back to the user. Since ajax calls are async it may not finish right away.
So your readXML function should do this. It should return the jquery get.
Test.prototype.readXML = function() {
var self = this;
return $.get('assest/xml/mydata.xml', function(d){
$(d).find("item").each(function(){
var item = new Item;
item.ID = ($(this).attr("ID"));
item.body = ($(this).find("body").text());
});
self.testArray.push(item);
});
}
Next you your second function append was just missing some context.
Test.prototype.appendInfo = function() {
var i;
for (i=0; i<this.testArray.length;i++) {
$('#testdisplay').append(this.testArray[i].ID +"<br />");
$('#testdisplay').append(this.testArray[i].body = "<br /");
}
}
So your code should look like this.
var mytest = new Test();
mytest.readXML().done(function(){
mytest.appendInfo();
}).fail(function(){
// put some fallback code here
});
Updated:
Added additional this's.
There is no testArray in your appendInfo() function, that's why it says it's not defined. You should use this.testArray instead.
Every time you want to use a variable declared inside your scope, but outside the function you are using, you must use this.yourVariable

In JQGrid add onclick event on img

I'm very new to JQuery, and I'm having some trouble
function Clients(guid)
{
var that = this;
this.guid = guid;
this.container = $("#Clients_" + that.guid);
this.LoadClients = function () {
var ids = that.container.find("#clients-tbl").getDataIDs();
for (var i = 0; i < ids.length; i++) {
var row = that.container.find("#clients-tbl").getRowData(ids[i]);
var imgView = "<img src='../../Content/Images/vcard.png' style='cursor:pointer;' alt='Open case' onclick=OnClickImage(" + ids[i] + "); />";
that.container.find("#clients-tbl").setRowData(ids[i], { CasesButtons: imgView });
}
}
this.CreateClientsGrid = function () {
var clientsGrid = that.container.find("#widget-clients-tbl").jqGrid({
.....
ondblClickRow:function(rowid)
{
---
}
loadComplete: function () {
that.LoadClients();
}
}
this.OnClickImage=function(idClient){
....
}
this.Init = function () {
that.CreateClientsGrid();
};
this.Init();
}
The problem is with onclick, because OnClickImage is not global function.
How can I use OnClickImage function?
You can bind to the click event in different ways. For example you can follow the way from the answer. By the way, it works much more quickly as getRowData and setRowData. Moreover you should save the result of that.container.find("#clients-tbl") operation in a variable outside of the loop and use use the variable inside the loop. JavaScript is dynamic language and every operation even ids.length will be done every time.
One more way would to use onCellSelect event without click event binding. See the answer which describe the approach and gives the corresponding demo.

JavaScript closures and variable scope

I am having trouble with JS closures:
// arg: an array of strings. each string is a mentioned user.
// fills in the list of mentioned users. Click on a mentioned user's name causes the page to load that user's info.
function fillInMentioned(mentions) {
var mentionList = document.getElementById("mention-list");
mentionList.innerHTML = "";
for (var i = 0; i < mentions.length; i++) {
var newAnchor = document.createElement("a");
// cause the page to load info for this screen name
newAnchor.onclick = function () { loadUsernameInfo(mentions[i]) };
// give this anchor the necessary content
newAnchor.innerHTML = mentions[i];
var newListItem = document.createElement("li");
newListItem.appendChild(newAnchor);
mentionList.appendChild(newListItem);
}
document.getElementById("mentions").setAttribute("class", ""); // unhide. hacky hack hack.
}
Unfortunately, clicking on one of these anchor tags results in a call like this:
loadUserNameInfo(undefined);
Why is this? My goal is an anchor like this:
<a onclick="loadUserNameInfo(someguy)">someguy</a>
How can I produce this?
Update This works:
newAnchor.onclick = function () { loadUsernameInfo(this.innerHTML) };
newAnchor.innerHTML = mentions[i];
The "i" reference inside the closure for the onclick handlers is trapping a live reference to "i". It gets updated for every loop, which affects all the closures created so far as well. When your while loop ends, "i" is just past the end of the mentions array, so mentions[i] == undefined for all of them.
Do this:
newAnchor.onclick = (function(idx) {
return function () { loadUsernameInfo(mentions[idx]) };
})(i);
to force the "i" to lock into a value idx inside the closure.
Your iterator i is stored as a reference, not as a value and so, as it is changed outside the closure, all the references to it are changing.
try this
function fillInMentioned(mentions) {
var mentionList = document.getElementById("mention-list");
mentionList.innerHTML = "";
for (var i = 0; i < mentions.length; i++) {
var newAnchor = document.createElement("a");
// Set the index as a property of the object
newAnchor.idx = i;
newAnchor.onclick = function () {
// Now use the property of the current object
loadUsernameInfo(mentions[this.idx])
};
// give this anchor the necessary content
newAnchor.innerHTML = mentions[i];
var newListItem = document.createElement("li");
newListItem.appendChild(newAnchor);
mentionList.appendChild(newListItem);
}
document.getElementById("mentions").setAttribute("class", "");
}

Categories