Javascript onclick functions do not work - javascript

<!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.

Related

Pure Javascript- Change class of onclick element generated by function

I'm new in coding and have been around a problem for a few days but found no solution.
I'm trying to build a canvas with a grid made out of squares and, when a square is clicked, it changes its background color.
I'm learning pure javascript and my problem is that I can't find a way to refer to the clicked element and change only its class, because my grid is generated by a function. I am doing it this way because I want to be able to change the canvas size in future.
Any suggestions on how to proceed?
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Square Canvas Game</title>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Merriweather:ital,wght#0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
</head>
<body onload="javascript:setInitialCanvas()">
<header>
<h1>Square artist</h1>
</header>
<div id="gameInterfaceContainer">
<button id="clearCanvas" onclick="clearAllSquares()">Clear All</button>
<div id="canvas">
</div>
</div>
<script src="script.js"></script>
</body>
</html>
JS
function setInitialCanvas(){
for (let i =0; i<400; i++){
let initialSquare = document.createElement("div");
document.getElementById("canvas").appendChild(initialSquare);
initialSquare.className = "canvasSquare clearSquare";
initialSquare.onclick = changeSquareColor(this);
initialSquare.style.backgroundColor = "white";
initialSquare.id = "square" + i;
}
}
function changeSquareColor(this){
let squareColor = ["white","blue","black","green","red","yellow","gray","brown"];
let colorIndex = 0;
while (squareColor[colorIndex] != this.style.backgroundColor){
colorIndex += 1;
}
if(this.style.backgroundColor == squareColor[squareColor.length]){
this.style.backgroundColor = squareColor[0];
}else
colorIndex += 1;
this.style.backgroundColor = squareColor[colorIndex];
}
Thank you!
*Edit: Adding codepen (that doesn't work) https://codepen.io/ccue92/pen/qBNKLQY
The reason it's not working is you're not binding the changeSquareColor function to onclick event, you are binding the result of it.
You need to change onclick binding like this:
initialSquare.onclick = function(event) {
changeSquareColor(event.target);
}
And do not use "this" as a variable name, because it's a reserved word for the current scope:
function changeSquareColor(square) {
let squareColor = ["white","blue","black","green","red","yellow","gray","brown"];
let colorIndex = 0;
while (squareColor[colorIndex] != square.style.backgroundColor){
colorIndex += 1;
}
if(square.style.backgroundColor == squareColor[squareColor.length]){
square.style.backgroundColor = squareColor[0];
} else {
colorIndex += 1;
}
square.style.backgroundColor = squareColor[colorIndex];
}

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;

atr onmouseover to Raphaël.js vars created in a for loop

Hope anyone could help.
I've got a huge amount of paths from svg file (showing only a few here) which I store in an array to dynamically create them on Raphael paper.
var paths = "M539.99,181v95.141h-0.12L509.521,181H539.99zdivideM539.99,276.511v84.85h-30.41L539.99,276.511zdivideM539.99,85.021V181h-30.47L539.99,85.021"; // string with paths from svg file. Much much bigger in real code
var pathsArray = paths.split("divide"); // putting all paths in an array
Everything works until I try to assign an attribute to a path in a onmouseover function inside a for loop. Nothing happens. And no error messages in the console.
var currentPath = window['section' + i];
currentPath.node.onmouseover = function() {
this.style.cursor = 'pointer';
currentPath.attr("fill", "#ccc"); // <-- THIS PART DOESNT WORK
}
I also tried it this way:
window['section' + i].attr("fill", "#ccc");
which gives me an error message:
TypeError: 'undefined' is not an object (evaluating 'window['section' + i].attr')
Here is the full code:
<!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>
<script type="text/javascript" src="jquery-1.8.2.js"></script>
<script type="text/javascript" src="raphael.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
</head>
<body>
<script type="text/javascript">
var paths = "M539.99,181v95.141h-0.12L509.521,181H539.99zdivideM539.99,276.511v84.85h-30.41L539.99,276.511zdivideM539.99,85.021V181h-30.47L539.99,85.021"; // string with paths from svg file. Much much bigger in real code
var pathsArray = paths.split("divide"); // putting all paths in an array
var paper = Raphael(10, 50, 1000, 1000);
for (var i=0;i<pathsArray.length;i++)
{
var currentPath = window['section' + i];
currentPath = paper.path(pathsArray[i]);
currentPath.attr("fill", "#f00");
currentPath.attr("stroke", "#fff");
currentPath.node.onmouseover = function() {
this.style.cursor = 'pointer';
currentPath.attr("fill", "#ccc"); // <-- THIS PART DOESNT WORK
}
}
</script>
</body>
</html>
Try using this keyword, also according to documentation there is a built in mouseover event. So the following should also work:
currentPath.mouseover(function() {
this.node.style.cursor = 'pointer'; // using 'node' property to get access to DOM element
this.attr("fill", "#ccc"); // 'this' should refer to Raphael element
});
Even though currentPath was inside the handler function, it was always taking the value of the last 'currentPath' in loop.

Multiple Onclick images

SO I am trying to create a screen that will display about 50 toggle buttons to display in a building on a monitor.
I want to create a bunch of images to use as the toggle so they are easy to see. This is the effect I want.
http://www.w3schools.com/dhtml/tryit.asp?filename=trydhtml_intro
except picture I want several light bulbs to turn on and off as I want.
Right now this is the code I have.
I am able to click the images to change as I want, they just wont go back, any ideas?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Untitled Document</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<script type="text/javascript">
function changeImg(img, newimg) {
img.src = newimg;
}
</script>
<body>
<img onclick="changeImg(this, 'staten_uw.jpg')" src="staten_moored.jpg">
<img onclick="changeImg(this, 'block_uw.jpg')" src="block_moored.jpg">
</body>
</html>
They don't go back because the code always changes it to the _uw images. If you want them to toggle, the function needs to check what image is currently displayed. Something like this:
function changeImg(img) {
if ( img.src.indexOf("_uw") > 0 ) {
img.src = img.src.replace("_uw","_moored");
}
else {
img.src = img.src.replace("_moored","_uw");
}
}
will work if you have 50x2 different images, named "img1_uw.jpg", "img1_moored.jpg", "img2_uw.jpg", "img2_moored.jpg", etc.
If you only have 2 images, but want 50 buttons that each toggle between them, it's easier:
function changeImg(img) {
if ( img.src == "staten_uw.jpg" ) {
img.src = "staten_moored.jpg";
}
else {
img.src = "staten_uw.jpg";
}
}
Other answers have shown this quite simply as well.
Either way, the HTML should change to something like this:
<img onclick="changeImg(this)" src="block_moored.jpg">
You can do this:
window.onload = function() {
images = document.getElementsByTagName("img");
for (i in images) {
images[i].addEventListener("click", function(e) {
var newsrc = this.togglesrc;
this.togglesrc = this.src;
this.src = newsrc;
});
}
};
and then use
<img togglesrc="staten_uw.jpg" src="staten_moored.jpg" />
In your case, you should use only one img tag:
<img onclick="changeImg(this)" src="staten_moored.jpg">
and check logic in your js:
function changeImg(img) {
img.src = (img.src == 'staten_moored.jpg') ? 'block_moored.jpg' : 'staten_moored.jpg';
}

What am I doing wrong on this simple Closure

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>

Categories

Resources