Adding one handler with different parameters - javascript

I am creating elements in list tag dynamically and I want to add different event handler for all elements in list. How can I achieve this?
var menuLinks = document.getElementsByClassName('test');
for(var j = 0; j < menuLinks.length; j++)
{
var menuLink = menuLinks[j];
menuLink.onclick = function(e) {
e.preventDefault();
console.log(menuLink.innerHTML);
};
}
I added the elements in class name test but no matter which ever element I click it always gives me last element.

Your problem is that you are creating a closure inside a loop. See JavaScript closure inside loops – simple practical example for an explanation and generic solution.
In short: JavaScript doesn't have block scope. Since there is only a single menuLink variable, every handler refers to that one variable. The variable can of course only have one value, which is the one that is set in the last iteration.
However, in your case there is a simpler solution: You can use this inside the event handler to refer to the element. You don't need to rely on the loop variable:
menuLinks[j].onclick = function(e) {
e.preventDefault();
console.log(this.innerHTML);
};
Learn more about this in event handlers.

try this,
var menuLinks = document.getElementsByClassName('test');
for(var j = 0; j < menuLinks.length; j++)
{
var menuLink = menuLinks[j];
menuLink.onclick = function(e) {
e.preventDefault();
console.log(e.target.innerHTML);
};
}

Related

Not able attach click event for a button using Javascript oop

I'm working on attaching a click event on a button which is having class "nextPage", but its not working. Let me show you the code.
function myContent() {
}
myContent.prototype.clickNext = function() {
alert("clicked");
};
var objMyContent = new myContent();
var el = document.getElementsByClassName('nextPage');
el.onclick=objMyContent.clickNext();
Please take a look into it. Please let me know where I did mistake.
You need to reference the function, not execute it, when assigning the click handler.
Instead of:
el.onclick = objMyContent.clickNext();
Use this:
el.onclick = objMyContent.clickNext;
The first piece of code executes clickNext, and assigns it's return value to el.onclick.
The second line assigns a reference to the clickNext function to el.onclick, instead. (Which is what you want)
Also, getElementsByClassName returns an HTMLCollection (Which is basically an array of HTML elements).
You'll need to assign the click handler to each found element in that collection:
for(var i = 0; i < el.length; i++){
el[i].onclick = objMyContent.clickNext;
}
iterate the array and the assign the onclick event
var objMyContent = new myContent();
var el = document.getElementsByClassName('nextPage'); for(var i=0;i<el.length;i++) el[i].onclick=objMyContent.clickNext;

Why doesn't this javascript bind the correct parameters with the event?

In this code I am supposed to bind a rollover effect to each <area> tag in a <map> element.
function initLinks(webrt) {
var areas = document.querySelectorAll("map#streetmap > area");
var links = new Array(areas.length);
for (var i=0; i<links.length; i++) {
links[i] = new Image(786,272);
links[i].src = webrt+"templates/default/sketches/links"+(i+1)+".png";
areas[i].onmouseover=function(){switchLinkImg(webrt+"templates/default/sketches/links"+(i+1)+".png");};
areas[i].onmouseout=function(){switchLinkImg(webrt+"templates/default/sketches/links.png");};
}
}
Strangely, each <area> onmouseover event tries to load the non-existing image: /templates/default/sketches/links6.png. Why does it keep this variable i which has incremented to 6 as a global variable rather than take the string I am passing to the function?
How do I fix this?
Note: No jQuery!
i often find it cleaner to use array methods when using the index because you don't need extra wrappers and everything reads a little cleaner (imho):
function initLinks(webrt) {
[].forEach.call(document.querySelectorAll("map#streetmap > area"),
function(elm, index){
var img = new Image(786,272);
img.src = webrt+"templates/default/sketches/links"+(index+1)+".png";
elm.onmouseover=function(){switchLinkImg(webrt+"templates/default/sketches/links"+(index+1)+".png");};
elm.onmouseout=function(){switchLinkImg(webrt+"templates/default/sketches/links.png");};
});
}
the variable count is way down, and we avoid extra ram-hogging closures by not creating a extra new function in each iteration of the "loop".
to be sure, both ways work, but the newer array methods can also allow the procedure to be recycled by ripping it out of the forEach() call, and giving it a name.
Try using the following code:
function initLinks(webrt) {
var areas = document.querySelectorAll("map#streetmap > area");
var links = new Array(areas.length);
for (var i=0; i<links.length; i++) {
(function(index) {
links[index] = new Image(786,272);
links[index].src = webrt+"templates/default/sketches/links"+(index+1)+".png";
areas[index].onmouseover=function(){switchLinkImg(webrt+"templates/default/sketches/links"+(index+1)+".png");};
areas[index].onmouseout=function(){switchLinkImg(webrt+"templates/default/sketches/links.png");};
})(i);
}
}
You should wrap the i variable into a closure. Otherwise it gets incremented.

generating dynamic onclick events with javascript

I am dynamically generating a series of onclick events where an alert() is associated with loop number of the pretended content. My problem is that currently the alerts outputs the 'i' value of the last loop rather than the i'th loop associated with the pretended content. Any thoughts?
JavaScript:
for (i = 1; i < 4; i++) {
prepend_content = 'foo';
$('#dynamic_div').prepend(prepend_content);
}
Many thanks.
Try concatenating it like you do before:
for (i = 1; i < 4; i++) {
prepend_content = 'foo';
$('#dynamic_div').prepend(prepend_content);
}
You might want to declare i and prepend_content (with var) in case you already haven't, to make sure they don't leak into the global scope.
At the same time, I wouldn't suggest using or adding HTML with inline event handlers. Try creating the element like this:
prepend_content = $("<a>").attr({
href: "#",
id: "img1_link_" + i
}).text("foo").on("click", (function (i) {
return function () {
alert(i);
};
})(i));
DEMO: http://jsfiddle.net/ujv4y/
The extra use of the immediately invoked function for the click handler is to make a closure that captures the value of i in the loop.
You can create a function using currying for the alert (for more complex stuff):
function(i) {
return function(){alert(i);}
}

Assign a Function Argument with a Loop

I have an array of list items in a piece of Javascript code. I would like to assign an onclick event handler to each one. Each handler would be the same function, but with a different input argument. Right now I have:
function contentfill(i) {
box = document.getElementById("text");
box.style.background="rgba(0,0,0,0.8)";
var content = new Array();
contentdivs = document.querySelectorAll("#contentfill>div");
box.innerHTML = contentdivs[i].innerHTML;
}
li[3].onclick = function() {contentfill(0);};
li[4].onclick = function() {contentfill(1);};
li[5].onclick = function() {contentfill(2);};
This works well enough, but I would like to achieve the same thing with a loop, for example:
for(i=3;i<=5;i++) {
j=i-3;
li[i].onclick = function() {contentfill(j);};
}
This, however, does not work. Since j seems to be defined as 2 at the end of the loop, each time I click, it only seems to call contentfill(2).
For an alternative approach, consider having each of the elements aware of what argument it should be using.
for (var i = 0; i < 3; i++) {
var el = li[i + 3];
el.dataset.contentIndex = i;
el.addEventListener('click', contentfill);
}
Then contentfill would have to extract the argument from .dataset instead of taking an argument, of course. (This is the same mechanism as jQuery's $.data.)
I tend to prefer this since (a) it doesn't generate tons of tiny wrappers, (b) it allows me to later examine and possibly change the "arguments", and (c) it lets me predefine them in the document using data- attributes. Effectively changes them from function arguments into behavior.
The value of i - 3 should be bound to the click handler function; a closure can provide this functionality:
li[i].onclick = (function(j) {
return function() {
contentfill(j);
}
)(i - 3));
Btw, it's better practice to use addEventListener or attachEvent to register click handlers.

Transfer HTML elements from one list to another with JavaScript

I have a problem with the following JavaScript function. I have two UL and I need that when the user clicks on a LI, this element transfers to the other UL.
I've managed to move them onClick from on list to the other, the problem appears when I try to move again a LI that was previously in the other UL, when that happens it just doesn't work...
function testList() {
usersA = document.getElementById("users-a");
usersB = document.getElementById("users-b");
for (var i=0; i < usersA.getElementsByTagName("li").length; i++) {
usersA.getElementsByTagName("li")[i].onclick = function() {
transfer = this.cloneNode(true);
usersB.appendChild(transfer);
usersA.removeChild(this);
return false;
}
}
for (var i=0; i < usersB.getElementsByTagName("li").length; i++) {
usersB.getElementsByTagName("li")[i].onclick = function() {
transfer = this.cloneNode(true);
usersA.appendChild(transfer);
usersB.removeChild(this);
return false;
}
}
}
I know that my logic sucks but it's all I could come up with. Any ideas why it works the first time I transfer a LI but when I try to move back to its original UL it doesn't work?
You're not "moving" elements, you're creating a copy and deleting the original. Although this seems like a "move" from the user's point of view the new elements that you create do not have click handlers assigned. From MDN: "Cloning a node copies all of its attributes and their values but does not copy event listeners."
According to MDN, .appendChild() will remove the child from its current parent so you shouldn't need the two-step clone/remove that you are currently doing. I haven't tested it, but perhaps using just .appendChild() it will keep the handlers? If so you need to remove that handler and assign a new one to allow for which list it now belongs to.
Or, rewrite your handlers so that they check which list is the current parent (have a look at .parentNode) and move to the other list as appropriate.
Bearing in mind that click events "bubble up" starting from the target/source element and up through the parent heirarchy you're probably better off setting your click handlers on the parent <ul> elements and then testing which <li> was clicked. That way you don't have to worry about setting new click handlers on new child <li> elements.
function testList() {
var usersA = document.getElementById("users-a"),
usersB = document.getElementById("users-b");
usersA.onclick = function(e) {
// allow for the IE and non-IE way of handling the event object
if (!e) e = window.event;
var el = e.target || e.srcElement;
if (el.tagName === "li") {
usersB.appendChild(el);
}
}
usersB.onclick = function(e) {
// allow for the IE and non-IE way of handling the event object
if (!e) e = window.event;
var el = e.target || e.srcElement;
if (el.tagName === "li") {
usersA.appendChild(el);
}
}
}
Also, if you're using .getElementsByTagName() call it once in the loop initialisation and assign the result to a variable, then use the variable - don't keep calling the function again and again to test the length, or to access individual elements inside your loop:
for (var i=0, lis = usersA.getElementsByTagName("li"); i < lis.length; i++) {
lis[i].onclick = function() {
// etc
}
}
The problem is that even after you've moved an element from list A to list B, it still keeps its old onclick handler, which still says "remove me from list A and add me to list B". You need to change its onclick handler to say "remove me from list B and add me to list A". Here's one way to fix that:
var usersA = document.getElementById("users-a");
var usersB = document.getElementById("users-b");
var onclickA;
var onclickB = function() {
usersA.appendChild(this);
this.onclick = onclickA;
return false;
};
onclickA = function() {
usersB.appendChild(this);
this.onclick = onclickB;
return false;
};
for (var i=0; i < usersA.getElementsByTagName("li").length; i++)
usersA.getElementsByTagName("li")[i].onclick = onclickA;
for (var i=0; i < usersB.getElementsByTagName("li").length; i++)
usersB.getElementsByTagName("li")[i].onclick = onclickB;
(I also got rid of the cloneNode stuff, for exactly the reasons that nnnnnn gives.)

Categories

Resources