What am I doing wrong on this simple Closure - javascript

It maybe sleep deprivation but I cannot understand why this isn't working. I want the onclick to return the value of i from a for loop that created the element and applied the event handler. Have put it in a closure and it is still turning the max number of the iterator.
window.onload = function(){
var arbitrary_amount = 100;
var the_list = document.getElementsByTagName('ul')[0];
for(var i = 0; i < arbitrary_amount; i++){
var natural_index = i + 1;
var list_item = document.createElement('li');
var inner_link = document.createElement('a');
inner_link.setAttribute('href', '#');
inner_link.innerHTML = "Link "+natural_index;
inner_link.onclick = function(){
return function(link_num){
alert('You clicked link '+link_num);
}(i);
};
list_item.appendChild(inner_link);
the_list.appendChild(list_item);
}
};

you are using the closure in the wrong way...i can't give you a guru type answer as to what was actually happening but here is a working (didn't test it) closure:
inner_link.onclick = (function(link_num){
return function(){
alert('You clicked link '+link_num);
};
})(i);

You can try with:
window.onload = function(){
var arbitrary_amount = 100;
var the_list = document.getElementsByTagName('ul')[0];
for(var i = 0; i < arbitrary_amount; i++){
(function(){var natural_index = i + 1;
var list_item = document.createElement('li');
var inner_link = document.createElement('a');
inner_link.setAttribute('href', '#');
inner_link.innerHTML = "Link "+natural_index;
inner_link.onclick = function(){
return function(link_num){
alert('You clicked link '+link_num);
}(i);
};
list_item.appendChild(inner_link);
the_list.appendChild(list_item);})();
}
};

It's because each closure you create for the onclick handler shares the same environment. When the onclick callback function is executed, each one refers to the last value of i.
There are already various solutions out, so I won't repeat it here, but the idea is to create additional closures that do not share the same environment.
It's a common mistake. Checkout this MDC article for more insights.

Your code works correctly for me in Firefox 3.5.5 and Chrome 4.0.249.64.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Just testing</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<div id="test1">
Testing
</div>
<script type="text/javascript">
// self-invoking function
(function makeHR(){
var newHR = document.createElement('hr');
document.getElementById('test1').appendChild(newHR);
})();
function makeLink(j){
var link =document.createElement("a");
link.innerHTML ="<br>Link test " +j;
link.setAttribute('href', '#');
link.onclick = (function(link_num){
return function(){
alert('You clicked link '+link_num);
};
})(j);
document.body.appendChild(link);
}
for (var i=1; i<4; i++) {
makeLink(i);
}
</script>
</body>
</html>

Related

How to make a function that creates toggle buttons for a setTimeout loop?

I am trying to create a function that lets me create an on/off toggle button for setTimeout loops and I am having difficulty figuring out how to get this to work the way I want it. The only way I was able to get the loop to stop was by declaring a variable in the global scope that stores the Id value for the setTimeout loop but which defeats the purpose. This also causes an issue if I try to create a second toggle button for a different loop as all the buttons access the same variable. Help would be appreciated. Thank you
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="mainContainer">
<h1 id='pointA'>Point A</h1>
<p id='pointB'>Point B</p>
</div>
</body>
<script>
function createCustomToolbar(){
var myToolbarCustom = document.createElement('div');
var targetPage= document.querySelector('body');
var myCSS=myToolbarCustom.style;
myToolbarCustom.id='myToolbarCustom';
targetPage.append(myToolbarCustom);
targetPage.style.padding='transform 0.4s ease';
targetPage.style.padding='40px';
myCSS.position='fixed';
myCSS.top=0;
myCSS.left=0;
myCSS.width='100%';
myCSS.height='35px';
myCSS.border=0;
myCSS.background='#e3e3e3'
myCSS.zIndex=9999999;
}
var x;
function toggleCreator(bttnName,callback,targetEle){
var state=false;
var mybttn=document.createElement('button');
mybttn.className='myCustomTog';
mybttn.innerHTML=bttnName;
var bttnTarget=document.getElementById('myToolbarCustom');
bttnTarget.appendChild(mybttn)
mybttn.onclick = function() {
state = !state;
if (state) {
mybttn.innerHTML = bttnName + " ON";
x = callback(targetEle);
} else {
mybttn.innerHTML = bttnName + " OFF";
clearTimeout(x);
}}
}
createCustomToolbar();
toggleCreator("start",testToggle,document.getElementById('pointA'));
toggleCreator("start",testToggle,document.getElementById('pointB'));
var i=0;
function testToggle(myTarget){
x= setTimeout(function(){
myTarget.innerHTML=i;
i++;
testToggle(myTarget);
},1000)
}
</script>
</html>
You're going to have multiple buttons and multiple timeOuts (one for each).
I'd create an empty array to store the timeOuts and use x as an index for this array.
var x = 0;
var timeouts_arr = [];
Inside testToggle...
x++;
myTarget.setAttribute('timeout_index', x);
timeouts_arr[x] = setTimeout(function(){...
inside mybttn.onclick...
state = !state;
if (state) {
mybttn.innerHTML = bttnName + " ON";
callback(targetEle); // don't use the x variable here
} else {
mybttn.innerHTML = bttnName + " OFF";
var timeout_index = mybttn.getAttribute('timeout_index');
clearTimeout(timeout_arr[timeout_index]);
}}

The array won't work with the output of my pictures

I tried to show pictures with an array so I can change it per delay, kinda like a slideshow. The problem is, that only the first picture will load. when the counter of the array changes, the picture won't load.
The paths of the images are all correct
<!DOCTYPE html>`enter code here`
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test2</title>
<script>
var bilder = [];
var zeit = 3000;
var p = 0;
bilder[0] = "../Bilder/Bild1.jpg";
bilder[1] = "../Bilder/Bild2.jpg";
bilder[2] = "../Bilder/Bild3.jpg";
bilder[3] = "../Bilder/Bild4.jpg";
function BildAutoWeiter()
{
document.bild.src = bilder[p];
for( var i=0; i<= bilder.length; i++)
{
if(i <= bilder.length)
{
p++;
}
else
{
i = 0;
}
}
setTimeout("BildAutoWeiter()", zeit);
}
</script>
</head>
<body onload="BildAutoWeiter()">
<div>
<img name ="bild" width="100%" height="50%">
</div>
</body>
</html>
You need to use getElementsByName() to choose elements by their name=''. That returns an array of elements that use that name, so to choose a specific one, use an index [index] starting from 0. Do this instead:
document.getElementsByName('bild')[0].src = bilder[p];
That selects the first element that uses name='bild'
Also, the for statement is a bit useless. You could instead do:
function BildAutoWeiter()
{
document.bild.src = bilder[p];
p++;
setTimeout(BildAutoWeiter, zeit);
}
You don't need to put the function name in quotations and you can't have the parentheses while calling the function in setTimeout.

Why do I get null when I getElementById?

I'm new to JS, and reading Javascript Dom, I'm trying to figure out one of the example in my book!
here is my html code
<!DOCTYPE html>
<html lang ="en">
<head>
<meta charset="utf-8" />
<title>Image Gallery</title>
</head>
<body>
<script type = "text/javascript" src="showPic.js"></script>
<h1>Snapshots</h1>
<ul id = "image">
<li>
Fall
</li>
<li>
Sunshine
</li>
<li>
Green
</li>
<li>
Filter
</li>
</ul>
<img id = "placeholeder" src="images/rise.jpg" alt = "my image gallery"/>
<p id="description"> Choose an image</p>
</body>
</html>
here is my javaScript code
function showPic(whichPic) {
var source = whichPic.getAttribute("href");
var placeholder = document.getElementById("placeholeder");
placeholder.setAttribute("src", source);
description.firstChild.nodeValue = text;
}
var text = whichPic.getAttribute("title");
var description = document.getElementById("description");
function perpareGallery() {
var gallery = document.getElementById("image");
var links = gallery.getElementsByTagName("a");
for(var i = 0 ; i<links.length; i++) {
links[i].onclick = function() {
showPic(this);
return false;
}
}
}
my code didnt getting anything from id = image. and I checked many times dont know what is wrong....
There seems to be no code that actually runs the perpareGallery() function.
After you fix that, it might still not work if you run perpareGallery() before the elements in HTML (like your a elements) are parsed and rendered. So, you might want to do something like:
window.onload = prepareGalery;
Although there are way better ways to set-up events, this will run your function after all HTML is parsed.
Two advices on how to track down your issue:
1) All browsers have a so called javascript console. It shows you all syntax error with your code. For firefox and chromium the keyboard shortcut F12 turns it on/off
If you run your code and open the javascript console you'll see this error:
ReferenceError: whichPic is not defined
In your example it looks as if the line var text = whichPic.getAttribute("title"); belongs to the function showPic(whichPic), but it doesn't.
2) You should format your code to not get lost. Your current code formatted:
function showPic(whichPic) {
var source = whichPic.getAttribute("href");
var placeholder = document.getElementById("placeholeder");
placeholder.setAttribute("src", source);
description.firstChild.nodeValue = text;
}
var text = whichPic.getAttribute("title");
var description = document.getElementById("description");
function perpareGallery() {
var gallery = document.getElementById("image");
var links = gallery.getElementsByTagName("a");
for(var i = 0 ; i<links.length; i++) {
links[i].onclick = function() {
showPic(this);
return false;
}
}
}
If you now take a look to the formatted code, it no longer looks as if whichPic is used inside of showPic(whichPic) but outside of the scope. The error makes perfectly sense.
You might want to move it inside of the function, before you use it.

How to attach eventListener to the child elements in the loop without loosing reference?

I am new to javascript. I am trying to add "click" eventlistener to <div> elements in the cycle, which are nested in the parent <div> element (id="cont").
But the problem is, that if I pass argument cont.children[i] in the "ClickOnMe" function, the reference points to the cont.children[cont.length] after completition of the cycle (which does not exist in reality). Here is the problematic piece of code:
cont.children[i].addEventListener("click", function() {ClickOnMe(cont.children[i]);});
Passing constant 0 instead of i variable is the only way, how to make it "work":
cont.children[i].addEventListener("click", function() {ClickOnMe(cont.children[0]);});
But it fix only specific code below, what is obviously non-sense in the other case.
May somebody tell me, how to correct it?
here is example. Clicking on the text "I am Anna" throws an exception:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Problem</title>
</head>
<script>
var girlFriend;
var cont;
function ClickOnMe(sender){
alert(sender.innerHTML);
}
function init(){
cont = document.getElementById("container");
for (i=0; i < cont.children.length; i++){
var el = cont.children[i];
var elid = el.attributes.getNamedItem("id").value;
switch (elid) {
case "anna":
cont.children[i].addEventListener("click", function() {ClickOnMe(cont.children[i]);}); // looses reference
// cont.children[i].addEventListener("click", function() {ClickOnMe(cont.children[0]);}); // this works
break;
case "betty":
girlFriend = el;
el.addEventListener("click", function() {ClickOnMe(girlFriend);}); // works well
break;
}
}
}
</script>
<body onload="init()">
<div id="container">
<div id="anna">I am Anna </div>
<div id="betty">I am Betty </div>
</div>
</body>
</html>
If all you need is to access the DOM element the handler is bound to, you don't need to access the loop variable and can completely avoid the closure problem. Just use this or event.currentTarget to access the DOM element:
for (i=0; i < cont.children.length; i++){
var el = cont.children[i];
el.addEventListener("click", function() {ClickOnMe(this);});
// or el.addEventListener("click", function(event) {ClickOnMe(event.currentTarget);});
}
For a general solution to the problem, see JavaScript closure inside loops – simple practical example .
Add one function which use immediately-invoked-function-expression(IIFE)
so,one can bind value of i immediately.
function addListner(i) {
(function(i) {
cont.children[i].addEventListener("click", function() {
ClickOnMe(cont.children[i]);
});
})(i)
}
Switch case should be like this one
case "anna":
addListner(i)
break;

Javascript onclick functions do not work

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<script language="javascript" type="text/javascript">
function setFont() {
var i;
for ( i = 0; i < document.all.length; i++) {
document.all[i].style.fontFamily = "Verdana";
document.all[i].style.fontSize = "16";
document.all[i].style.color="black";
}
};
function abc(a) {
alert(a);
ansArray = ['a'];
for (i = 1; i <= a; i++) {
document.write('<input type = "button" value = "a">');
document.write('<input type = "button" value = "b">');
}
var myButton = document.getElementsByTagName("input");
//alert(myButton.length);
myButton[0].onclick = function() {
if (ansArray[0] == 'a') myButton[0].style.backgroundColor = "green";
else myButton[0].style.backgroundColor = "red";
};
myButton[1].onclick = function() {
if (ansArray[0] == 'b') myButton[1].style.backgroundColor = "green";
else myButton[1].style.backgroundColor = "red";
};
};​
setFont();
</script>
</head>
<body onload="Javascript:abc(2)">
hello
</body>
</html>
The onclick functions do not work in IE but work fine in chrome and firefox. I could not find the mistake. Why a normal function does not work. function loads the contents but onclicking the first two buttons for which event handelers are writen does not change the button colour in IE only. Please help me... Thanks in advance
The problem here is that using document.write apparently overwrites JavaScript as well. If you switch your document.write() to document.body.innerHTML += your problem is solved. Your latter two buttons won't work with that code because you are calling button 0 and 1 exclusively, while those second two are 3 and 4.
quick googling suggests that the problem is that you are using document.write after the page has been loaded so it is erasing the dom as well. you should avoid using that in functions that are called after page loading.
source : http://sitr.us/2012/09/04/monkey-patching-document-write.html
I dont have IE so couldn't test it.

Categories

Resources