How do I increment an integer inside a variable, every time that variable is called? Javascript - javascript

How do I increment an integer inside a variable, every time that variable is called? Javascript.
var a=0;
var t=loadXMLDoc("http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist="+x[a].getElementsByTagName("name")[0].childNodes[0].nodeValue+"&api_key=83e386b0ba08735e3dee9b118478e56d&lang=en").getElementsByTagName("bio");
for (i=0;i<20;i++)
{
document.write("<div><button type='button' onclick='document.getElementById("+i+").innerHTML=t[0].getElementsByTagName(\"summary\")[0].childNodes[1].nodeValue;'>Open Bio</button></div>");
}
I'm not sure how I would go about incrementing variable a. I need it to increase by 1 every time variable t is called in the for loop.
When I put all of the code in the for loop I get [object node list] returned so this method is not desired.

If I understood your question correctly, you could define your own getters and setters for the property.
var o = {}
o.__defineSetter__('property', function(value) { this._counter = 0; this._holder = value; })
o.__defineGetter__('property', function() { console.log(this._counter++); return this._holder; })
The counter would be reset every time o.property is assigned a value
o.property = 'Some value'
and then increase every time the property is accessed.
So,
console.log(o.property)
would print
0
Some value
to the console. And if you do it again, it would print
1
Some value

After your edit I think I can see your problem now. You will need to put the loadXMLDoc statement in the loop (since you want to load 20 different XML files), but you can't assign the result of every call to the same variable t - as once the button is clicked, the handler will evaluate t and get only the last value.
Instead, use an array:
var bios = []; // empty array
for (var i=0; i<20; i++) {
var artist = x[i].getElementsByTagName("name")[0].childNodes[0].nodeValue,
doc = loadXMLDoc("http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist="+artist+"&api_key=83e386b0ba08735e3dee9b118478e56d&lang=en"),
bio = doc.getElementsByTagName("bio")[0].getElementsByTagName("summary")[0].childNodes[1].nodeValue;
bios[i] = bio; // store it in the array
document.write("<div><button type='button' onclick='document.getElementById("+i+").innerHTML=bios["+i+"];'>Open Bio</button></div>");
}
Of course, while that will work it's a bunch of bad practises, including
unsecured accessing of DOM nodes/properties. If the xml changes its format, you will get lots of exceptions here. You might be sure now that this never happens, but wrapping artist and bio in try-catch might not be a bad idea.
snychronous Ajax. One can do better than that.
loading 20 documents (and that sequentially!) even if you don't need them. It might be worth to try loading each of them only when the respective button is clicked.
document.write
Inline attribute event handlers
…and creating them even by JS.

Related

Null check for every variable that invokes a function?

I have a single javascript where I have declared all my variables in the
$(document).ready(function(){
//variables
});
The values of these variables are initialized as well and mostly they are HTML elements. The elements are determined using the ids via document.GetElementById(). Some of these elements exists only in a different page which is not loaded in the browser yet. This results in null error when the variables holding the elements are used for a different purpose.
var container_element = document.getElementById('unique-id');
var count = container_element.getElementsByTagName("div").length;
Since the element with "unique-id" is present in another page which is not loaded in the browser, the second line would return an error because container_element is null. To fix this, I changed the code to
var container_element = document.getElementById('unique-id');
if(container_element) {
var count = container_element.getElementsByTagName("div").length;
}
Is this is the only way to handle such a thing? Should I have to do a null check for every function that I invoke via a variable or is there any other solution or standard / best practice?
You need a guard like that any time the element may or may not exist as of when you use getElementById. You can use the if you've shown, or
var container_element = document.getElementById('unique-id');
var count = !container_element ? 0 : container_element.getElementsByTagName("div").length;
or similar.
Another option is to react to the exception:
var container_element = document.getElementById('unique-id');
var count;
try {
count = container_element.getElementsByTagName("div").length;
} catch (e) {
count = 0;
}
I notice you're using jQuery but not using it in that code. Which is too bad, because if you were, jQuery's set-based nature would mean you didn't need the guard:
var container_element = $('#unique-id');
var count = container_element.find("div").length;
Even though container_element is an empty set, you can call methods on it. Most jQuery methods provide intelligent handling of empty sets. For instance, using find on an empty set returns a (new) empty set, as above.
You still have the option to know whether the element exists (more in this question's answers):
if (container_element[0])
// or
if (container_element.length)

Javascript push() replacing element instead of appending

Here's a function I have written to add words to local storage using Javascript. In case you're wondering, this is my attempt at building a search history functionality for a simple dictionary lookup site.
function add2local(entry){
var l = localStorage.length;
if(l==0){
var lu = [entry];
localStorage.setItem("w", JSON.stringify(lu));
}
else {
var lu = JSON.parse(localStorage.getItem("w")); alert(lu);
lu.push(entry); alert(lu);
}}
My understanding is the this function would keep appending its argument to local storage every time it's called. So, the first time I call it, I send it the word pirate. This gets added to the array and subsequently to the local storage as expected. The first alert() (the alert() functions are solely being used for testing) confirms this. The second time, I call it with the word vampire. Now, going by the function's logic, vampire should be appended to the array and thus the second alert() should output "pirate, vampire." And so it does.
But the third time around, say, I call the function with the word foo. This time around, it should output "pirate, vampire, foo" but instead shows "pirate, foo". Every subsequent call simply replaces the second word with the new word instead of appending it. What might I be doing wrong here? Am I misunderstanding how push() works?
The if condition and logic is incorrect; it is irrelevant how many items localStorage has, but it is very important to re-save the modified array.
In any case, I suspect an impl. might look as so:
function add2local(entry){
// load entries - if there are none, we simulate an empty array to load
var lu = JSON.parse(localStorage.getItem("w") || "[]");
// add new entry
lu.push(entry);
// write back - so change is not lost between function calls
localStorage.setItem("w", JSON.stringify(lu));
// return all local entries saved - for use from the caller
return lu;
}
Why check the storage length? You don't care. Fetch the key and if it's null then default to empty array.
function add2local (entry) {
var key = "w"
var value = localStorage.getItem(key)
if (value) {
value = JSON.parse(value)
} else {
value = []
}
value.push(entry)
localStorage.setItem(key, JSON.stringify(value))
}

Confusion around closure/scope

Today while working with some JS I had a bug in my code. I was able to resolve it but I don't really understand why the change I made works. All I can guess is that it comes down to either closure or variable scope.
I was trying to build up a nested hash of arrays like so:
var maxNumberOfPairs = 2;
var container = {};
var pairsHash = {};
$.each(["nurse", "doctor", "janitor", "chef", "surgeon"], function(index, role) {
for(var i = 0; i < maxNumberOfPairs; i++){
var pairIdSubString = "attribute_" + i + "_" + role;
pairsHash["attribute_" + i] = [pairIdSubString + "_night", pairIdSubString + "_day"];
}
container [role] = pairsHash;
});
If you run this you get a nice nested output inside container but when you look at each array in the hash you get a weird behaviour with the string produced.
Each one has the last role in each string like so:
"attribute_0_surgeon_night"
If you log out the variable pairIdSubString it correctly has the role in the string, but as soon as this is added to pairHash it just uses the last element in the $.each array.
I was able to fix it by moving pairsHash inside the $.each but outside the for loop.
Can anyone explain to my why the output was different after moving it inside the each?
Thanks
It actually has to do with reference vs value. When its outside the each you are operating on the same object over and over so every time you set it to the container you are just setting a reference to the same object that is constantly changing. So every reference in container after the loop is the last state of the pairsHash because they all point to the same object.
When you put the pairsHash in the each it is reinitialized every time so they all point to different memory addresses. Not the same one since a new one is created every loop.
To further clarify all objects are just references to a memory address In JavaScript so in order to get new one you need to initialize or to pass by value to a function clone it.

Remembering the last value passed to a JavaScript function called on click

Below is my code fragment:
<div onclick = "myClick('value 1')">
button 1
</div>
<div onclick = "myClick('value 2')">
button 2
</div>
Basically when I for each click on a different div, a different value will be passed to the JavaScript function.
My Question is how can I keep track of the value passed in the previous click?
For example, I click "button 1", and "value 1" will be passed to the function. Later, I click on "button 2", I want to be able to know whether I have clicked "button 1" before and get "value 1".
Just add it to a variable in your script:
var lastClicked;
var myClick = function(value) {
lastClicked = value;
};
You can define somekind of variable, like var lastUsed;
add additional line to your function:
var lastUsed = null;
function myClick(value){
prevClicked = lastUsed; //get the last saved value
...
lastUsed = value; //update the saved value to the new value
...
}
And here you go
You need a variable. Variables are like little boxes in which you can store values. In this case, we can store the value that was last passed to the function myClick.
In Javascript, you can define a variable like this:
var lastClickedValue;
You can "put" a value into that variable. Let's say you want to put your name in there. You would do this:
lastClickedValue = 'sams5817';
Now here's the tricky bit. Variables have "scope". You might want to think about it as their "life-time". When a variable reaches the end of its scope, you cannot read or write to it anymore. It's as if it's never been. Functions define a scope. So any variable you define in a function will disappear at the end of the function. For example:
function myClick(value)
{
var lastClickedValue;
alert('lastClickedValue is = ' + value);
lastClickedValue = value;
}
That looks almost right, doesn't it? We declared a variable, display its last value, and update it with the new value.
However, since the lastClickedValue was declared in the function myClick, once we've reached the end of that function, it's gone. So the next time we call myClick, lastClickedValue will be create all over again. It will be empty. We call that an "uninitialized" variable.
So what's the problem? We're trying to remember a value even after the end of myClick. But we declared lastClickedValue inside myClick, so it stops existing at the end of myClick.
The solution is to make sure that lastClickedValue continues to exist after myClick is done.
So we must delcare lastClickedValue in a different scope. Luckily, there's a larger scope called the "global scope". It exists from the moment your page loads, and until the user moves on to another webpage. So let's do it this way:
var lastClickedValue;
function myClick(value)
{
alert('lastClickedValue is = ' + value);
lastClickedValue = value;
}
It's a very small difference. We moved the declaration of the variable lastClickedValue to be outside the function myClick. Since it's outside, it will keep existing after myClick is done. Which means that each time we call myClick, then lastClickedValue will still be there.
This will let you know what the last value passed to myClick was.
Finally, I'd like to advise you to look for some kind of Javascript tutorials. I wish I knew of some good ones to recommend, but I'm certain you can find a few on the Internet. If you try to write programs before understanding what you're doing, you'll find yourself producing work that is less than what you're capable of. Good luck!
I suppose you need something like this
var clickedButtons = [];
function myClick(value){
...
clickedButtons.push(value);
...
}
I am surprised that no one else mentioned this, but since functions are first class objects in JavaScript, you can also assign attributes and methods to functions. So in order to remember a value between function calls you can do something like I have with this function here:
function toggleHelpDialog() {
if (typeof toggleHelpDialog.status === 'undefined')
toggleHelpDialog.status = true;
else
toggleHelpDialog.status = !toggleHelpDialog.status;
var layer = this.getLayer();
if (toggleHelpDialog.status) layer.add(helpDialog);
else helpDialog.remove();
layer.draw();
}
Here I have added an attribute named 'status' to the toggleHelpDialog function. This value is associated with the function itself and has the same scope as the toggleHelpDialog function. Values stored in the status attribute will persist over multiple calls to the function. Careful though, as it can be accessed by other code and inadvertently changed.
we can leverage javascript static variables
One interesting aspect of the nature of functions as objects is that you can create static
variables. A static variable is a variable in a function‘s local scope whose value persists across
function invocations. Creating a static variable in JavaScript is achieved by adding an instance
property to the function in question. For example, consider the code here that defines a function
doSum that adds two numbers and keeps a running sum:
function doSum(x,y){
if (typeof doSum.static==='undefined'){
doSum.static = x+y;
}else{
doSum.static += x+y;
}
if (doSum.static >= 100){doSum.static = 0;doSum.static += x+y;}
return doSum.static;
}
alert(doSum(5,15))
alert(doSum(10,10))
alert(doSum(10,30))
alert(doSum(20,30))

Why is my jQuery each loop not adding values correctly?

I am trying to get the values of several fields & add them together & in my testing I am having problems. I have this code:
var count;
function calculate() {
// Fix jQuery conflicts
jQuery.noConflict();
count = 0;
jQuery('.calculate').each(function() {
var currentElement = jQuery(this);
var value = currentElement.val();
var count = count + value;
alert(count);
});
}
I enter in the value of "9" in my first field & when the first alert triggers I get "undefined9"; all the other values are currently set to "0"; when it triggers again I always get "undefined0".
Why am I getting the "undefined" bit & why is it only returning the value of the current field & not adding them together?
You are dimensioning the count value inside the loop, essentially setting it to undefined first in each iteration.
You want to remove the var within the loop. This way, it doesn't have scope to the anonymous function and JavaScript will look at the parent function for its declaration.
It may also be a good idea to parseInt(count, 10) the number first, because JavaScript overloads the + operator to mean arithmetic addition and string concatenation, and you wouldn't want count to be "0something".
Finally, count += value is easier to read :)
You're accidentally re-declaring count:
var count = count + value;
should be:
count = count + value;
The count declared in the inner-most scope (that of calculate) will hide the other one (this is called "shadowing" of variables).
The reason you get "undefined9" is because the default value of the newly-declared count variable is undefined; when you add to it, it sees an undefined value on the left, and a number on the right, and decides that string concatenation is the best way to perform the addition, resulting in the string "undefined9".
It has to guess at your intended meaning since + is overloaded to mean both numerical addition and string concatenation; in this case, it guesses wrong.
You're defining count in two different scopes - the calculate function, and then in the function that each is using. You may want to have the second count be named something else, or at least don't redeclare it.

Categories