I'm trying to assign a click handler to a JQuery object, defined in a variable :
some.object.array[8].action = function(data){console.log(data);}
anotherobject = {..}
now inside some loop, I need to assign this function to the click handler:
and want to pass the whole 'anotherobject' object
for (var i = 0; i < foo.length; i++) {
$('<div/>').click(some.object.array[i].action);
}
But how can I pass the parameter?
If I encapsulate it inside some anonymous function, I'm losing my scope...:
for (var i = 0; i < foo.length; i++) {
$('<div/>').click(function() {
some.object.array[i].action(anotherobject)
});
}
because i has changed...
How are we supposed to do this?
There are just too many ways to do this:
for (var i = 0; i < foo.length; i++) {
(function(i) {
$('<div/>').click(function() {
some.object.array[i].action(anotherobject);
});
})(i);
}
Or
for (var i = 0; i < foo.length; i++) {
$('<div/>').data("i", i).click(function() {
var i = $(this).data("i");
some.object.array[i].action(anotherobject);
});
});
}
Or
function getClickHandler(callback, parameter) {
return function() { callback(parameter); };
};
for (var i = 0; i < foo.length; i++) {
$('<div/>').click(getClickHandler(some.object.array[i].action, anotherobject));
}
If you want your action function to maintain the div as this and still accept the jQuery event object, you can use bind like this example:
function action(another, event){
console.log(this, arguments);
}
$(function(){
for (var i = 0; i<10; i++) {
var anotherObject = "another"+i;
var div = $('<div>'+i+'</div>');
// force 'this' to be 'div.get(0)'
// and 'arg0' to be 'anotherObject'
div.click(action.bind(div.get(0),anotherObject));
$("body").append(div);
}
})
Example: https://jsfiddle.net/Lwe5b9cx/ You'll need to open the console to see the output, which should look like this:
for(var i = 0; i < foo.length; i++)
{
$('<div/>').click(
(
return function(callback){
callback(anotherobject)
}
)(some.object.array[i].action)
);
}
Related
I am trying to dynamically add onclick function to "li" tagged elements.
But the event does not fires.
Here is my code:
var arrSideNavButtons = [];
var sideNavLi = document.getElementsByClassName('side-nav')[0].getElementsByTagName('li');
var arrayOfSceneAudios = [scene1Audio, scene2Audio,...];
for (var i = 0; i < sideNavLi.length; i++) {
sideNavLi[i].onclick = function() {
arrayOfSceneAudios[i].play();
}
arrSideNavButtons.push(sideNavLi[i]);
}
Is it possible to code it this way?
If yes, what is my mistake?
Thanks a lot.
Wrap your onclick handler in a closure, else it only get assigned to the last elem in the loop:
for (var i = 0; i < sideNavLi.length; i++) {
(function(i) {
sideNavLi[i].onclick = function() {
arrayOfSceneAudios[i].play();
}
arrSideNavButtons.push(sideNavLi[i]);
})(i)
}
I think it's better to reuse one single function, instead of creating a new one at each iteration:
var arrSideNavButtons = [],
sideNavLi = document.getElementsByClassName('side-nav')[0].getElementsByTagName('li'),
arrayOfSceneAudios = [scene1Audio, scene2Audio,...],
handler = function() {
this.sceneAudio.play();
};
for (var i = 0; i < sideNavLi.length; i++) {
sideNavLi[i].sceneAudio = arrayOfSceneAudios[i];
sideNavLi[i].onclick = handler;
arrSideNavButtons.push(sideNavLi[i]);
}
I was doing this code but it will take time because it will be h1 up until h24 so i decided to use a for loop but i don't know how..
this is my original code
function hover(h1,h2,h3,h4){
document.getElementById(h1).style.backgroundColor="orange";
document.getElementById(h2).style.backgroundColor="orange";
document.getElementById(h3).style.backgroundColor="orange";
document.getElementById(h4).style.backgroundColor="orange";
}
and i want to replace it something like this
function hover(
for(i = 1; i<=24; i++) {
document.write("h"+i+",");
}
)
but there is an error.. Please help me out.. Thank you
function hover() {
for(var i = 1; i < 25; i++) {
document.getElementById("h" + i).style.backgroundColor="orange";
}
}
If you could need more control, you could set the upper limit as a parameter, e.g.
function hover(limit) {
for(var i = 1; i <= limit; i++) {
document.getElementById("h" + i).style.backgroundColor="orange";
}
}
A call to hover(10); would change the background colour of h1 through h10.
There are several things wrong with the code you posted. I think I understand the problem you are trying to solve though. Try something like this:
function hover(eleId){
document.getElementById(eleId).style.backgroundColor="orange";
}
for(i=1; i<=24; i++){
hover("h"+i.toString());
}
Also note h1, h2, h3 all look like HTML tags. Check out getElementsByTagName.
You need to use Javascript's arguments object
function hover() {
var args = Array.prototype.slice.call(arguments);
var length = args.length;
for (var i = 0; i < length; i++) {
document.getElementById(args[i]).onmouseover =
function () { this.style.backgroundColor = "orange"; }
document.getElementById(args[i]).onmouseout =
function () { this.style.backgroundColor = "transparent"; }
}
}
jsFiddle Demo
HTML:
<div id="h1">A</div>
<div id="Hello">B</div>
<div id="box">C</div>
<div id="World">D</div>
JS Call:
hover("h1", "Hello", "box", "World");
Here is my suggestion :
<div id="h1">test</div>
<div id="h2">test</div>
<div id="h3">test</div>
<div id="h4">test</div>
script, notice the dummy variable
<script>
function hover(dummy){
for (var i=0; i<arguments.length; i++) {
var element = document.getElementById(arguments[i]);
element.onmouseover = function() {
this.style.backgroundColor="orange";
}
element.onmouseout = function() {
this.style.backgroundColor="white";
}
}
}
//hover('h1','h2','h3','h4');
for (var i=1;i<=24;i++) {
hover('h'+i);
}
</script>
I have the following code that is in need of a closure:
var numItems = document.getElementsByClassName('l').length;
for (var i = 0; i < numItems; i++) {
document.getElementsByClassName('l')[i].onclick = function (e){
preview(this.href, i);
};
}
What happens is that whenever an item is clicked, preview always the same number for i
I suspect what I need to do is
function indexClosure(i) {
return function(e) {
preview(this.href, i);
}
}
And assign the onclick's like this:
document.getElementsByClassName('l')[i].onclick = indexClosure(i);
But then this would no longer refer to my link... how is this problem solved?
Use closure to capture the counter of the cycle:
var numItems = document.getElementsByClassName('l').length;
for (var i = 0; i < numItems; i++) {
(function(i){
document.getElementsByClassName('l')[i].onclick = function (e){
preview(this.href, i);
};
}(i))
}
doesn't onclick pass in (sender, eventArgs) allowing you to access this through sender?
I am looping through a list of links. I can correctly get the title attribute, and want it displayed onclick. When the page is loaded and when I click on a link, all of the link titles are alerted one by one. What am I doing wrong?
function prepareShowElement () {
var nav = document.getElementById('nav');
var links = nav.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
links[i].onclick = alert(links[i].title);
}
}
What you were doing was actually running the alert function.
enclosing the whole thing in an anonymous function will only run it when it is clicked
for (var i = 0; i < links.length; i++) {
links[i].onclick = function () {
alert(this.title);
}
}
You are assigning the onclick to the return value of alert(links[i].title); which doesn't make any sense, since onclick is supposed to be a function.
What you want instead is somethig like onclick = function(){ alert('Hi'); };
But
Since you are using a variable i in that loop you need to create a local copy of it
onclick = function(){ alert(links[i].title); }; would just use the outer scope i and all your links would alert the same message.
To fix this you need to write a function that localizes i and returns a new function specific to each link's own onclick:
onclick = (function(i){ return function(e){ alert(links[i].title); }; })(i);
Final result:
function prepareShowElement () {
var nav = document.getElementById('nav');
var links = nav.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
links[i].onclick = (function(i){ return function(e){ alert(links[i].title); }; })(i);
}
}
You can use jquery. To display title of the link on click.
$("#nav a").click(function() {
var title = $(this).attr('title');
alert(title);
});
links.forEach(function(link) {
link.onclick = function(event) {
alert(link.title);
};
}
Also note that your original solution suffered from this problem:
JavaScript closure inside loops – simple practical example
By passing in our iteration variable into a closure, we get to keep it. If we wrote the above using a for-loop, it would look like this:
// machinery needed to get the same effect as above
for (var i = 0; i < links.length; i++) {
(function(link){
link.onclick = function(event) {
alert(link.title);
}
})(links[i])
}
or
// machinery needed to get the same effect as above (version 2)
for (var i = 0; i < links.length; i++) {
(function(i){
links[i].onclick = function(event) {
alert(links[i].title);
}
})(i)
}
You need change .onclick for a eventlistener same:
function prepareShowElement () {
var nav = document.getElementById('nav');
var links = nav.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
links[i].addEventListener('click',function() {
alert(links[i].title);
},false);
}
}
How does JavaScript closure work in this case and to be more specific: what does the (i) at the end do?
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Also I'm trying to implement it in my code, and it seems I don't get it right
for (var i=0; i < len; i++) {
var formID = document.forms["form-" + i];
$(formID).bind("submit", validate);
$(formID).bind("change", function(i){
var divI = '#ind-' + i;
$(divI).css("background-color","green");
})(i);
}
This is a pattern used to create local scope around a variable. If this wasn't used then every call to console.log(i) would log the value of i (10) after the for loop finished.
for(var i = 0; i < 10; i++) {
// create new function
(function(e) {
// log each counter after 1 second.
setTimeout(function() {
console.log(e);
}, 1000);
// execute it with the counter
})(i);
}
The above is the same as this.
function foobar(e) {
setTimeout(function() {
console.log(e);
}, 1000);
}
for (var i = 0; i < 10; i++) {
(
foobar
)(i);
}
The real problem here is creating functions in a loop. don't do it :)
Your code
for (var i=0; i < len; i++) {
var formID = document.forms["form-" + i];
$(formID).bind("submit", validate);
// create a full closure around the block of code
(function() {
$(formID).bind("change", function(i){
var divI = '#ind-' + i;
$(divI).css("background-color","green");
})//(i); Don't call (i) here because your just trying to execute the
// jQuery element as a function. You can't do this, you need to wrap
// an entire function around it.
})(i);
}
But that is wrong, you want to delegate this job to something else.
function makeGreen(form, i) {
$(form).change(function() {
$("#ind-"+i).css("background-color", "green");
});
}
for (var i=0; i < len; i++) {
var formID = document.forms["form-" + i];
$(formID).bind("submit", validate);
// call a helper function which binds the change handler to the correct i
makeGreen(formID, i);
}
If you want to get a bit clever you can get rid of these anonymous functions
function makeGreen() {
var divId = $(this).data("div-id");
$(divId).css("background-color", "green");
}
for (var i=0; i < len; i++) {
$(document.forms["form-" + i])
.bind("submit", validate)
// store i on the form element
.data("div-id", "#ind-" + i)
// use a single event handler that gets the divId out of the form.
.change(makeGreen);
}
Edit
( // contain the function we create.
function(parameterA) {
window.alert(parameterA);
}
) // this now points to a function
("alertMessage"); // call it as a function.
Is the same as
( // contain the window.alert function
window.alert
) // it now points to a function
("alertMessage"); // call it as a function
Although not a direct answer to the closure question, here is my take on the issue.
I would re-write the logic to avoid the need for a closure (as it seems overcomplicated for the requirements)
The fact that there is a pattern in the naming of the forms makes things really easy
$('form[id^="form-"]').submit(validate)
.change(function(){
var divI = '#ind-' + this.id.replace('form-','');
$(divI).css("background-color","green");
});
demo http://jsfiddle.net/gaby/q8WxV/