How to define javascript event handlers dynamically with a function - javascript

I'm somewhat new to Javascript and I can't seem to figure out exactly what's going on with my functions.
Background: my script is supposed to let you assign a class of either "toggler" or "toggled" to an element to have them automatically linked, so checking/unchecking the toggler will show/hide the toggled (I know there are many libraries that can do this but unfortunately I can't use them in this case)
The script executes on pageload and searches through the elements for ones with the class "toggler" and then assigns its onclick handler accordingly. Here's the bit of code I'm having problems with:
function makeToggle () {
for (i=0;i<toggler.length;i++) {
toggler[i].onclick=function(){toggleSection(this,i)};
}
}
function toggleSection(obj,index) {
if (obj.checked==true) {
toggled[index].style.display="inline-block";
} else {
toggled[index].style.display="none";
}
}
"This" is passed correctly and resolves to whatever checkbox it's applied to, but "index" is always set to the length of the toggler array instead of being incremented. For example, the first and second togglers' onclick should be:
onclick="toggleSection(this,0)"
onclick="toggleSection(this,1)"
What they are actually set for (assuming I have 5 elements defined as a toggler) is:
onclick="toggleSection(this,5)"
onclick="toggleSection(this,5)"
From what I've read I think it's a scoping problem, or the way I'm calling the function, but nothing I've found makes much sense

you can't use that variable i like that while you are creating a function. If you do it so the value of i will be the last value of i. In your problem i will be length of toggler always.
so you should pass variable (i) as paramater to your onclick function,
try like this,
function makeToggle () {
for (i=0;i<toggler.length;i++) {
toggler[i].addEventListener("click", (function(d) { return function(){
toggleSection(this,d);
}; })(i), true);
}
}
or in your style,
function makeToggle () {
for (i=0;i<toggler.length;i++) {
toggler[i].onclick = (function(d) { return function(){toggleSection(this,d)}})(i);
}
}

You are seeing the problem detailed in this blog post.
The reason for this is quite complicated. The anonymous functions we define as event handlers 'inherit' the variable i from the scope of attachEventsToListItems, and not the for-loop. However, by the time the event handlers are executed, the for-loop has completed its iterations and the value of i in this function has become 4. The problem here is that the functions we define as event handlers don't create a new scope for i until they are executed.
To fix the problem, you need a closure:
for (i=0;i<toggler.length;i++) {
toggler[i].onclick= (function (index) {
return function() {
toggleSection(this,index);
};
}) (i);
}
You can see it in actor here: http://jsfiddle.net/Vb2t2/

Related

addEventListener not working correctly

So I have a div which has an EventListener on it which works fine. But I want the EventListener to only be triggered at certain points, which is why I added an if statement to it.
I was under the impression that you can change global variables from functions the way my code is below yet this doesn't seem to work at all. I have tried changing the variable within the function by way of window.clickDisable = false; but that does not work either. The relevant parts of the code are below, does anybody know why this is not working? Thanks.
var clickDisable = true;
if (clickDisable == false) {
document.getElementById("fight")
var fightMenu = fight.addEventListener("click", fightMenuFunction)
}
function fightMenuFunction () {
}
setTimeout(introAnimation, 7000)
function introAnimation() {
clickDisable = false;
}
There are a handful of problems with your code:
I think the best approach is to put your if statement within the body of the function. You want the body of your function to run only if the timer has elapsed, rather than the event to be bound only if the timer has elapsed.
Also, your document.getElementById was not being used. You were instead benefiting from the fact that element ids are automatically interpreted as global variables, which you can do, as I have done below, but is probably not a best practice.
var clickDisable = true;
function fightMenuFunction () {
if(!clickDisable){
console.log('This is the function running.')
}
}
fight.addEventListener("click", fightMenuFunction)
window.setTimeout(introAnimation, 7000)
function introAnimation() {
clickDisable = false;
}
<button id="fight">Fight</button>

Passing value into function and variable scope in JQuery

Link to code example:
http://jsfiddle.net/99Shr/
This code works for a given click handler, and $(this) takes on the particular class.
I am attempting to take the code that is inside the click function and put it into it's own function. The reason I want to do this is because I would like to replace quantity-- with quantity++ depending on which click handler is called. The issue I am running into is that the variables when called in the function are undefined since $(this) is window.
I am well aware that I may be doing this wrong to achieve what I want and am open to learning a better way to achieve it.
function price(change) {
return change;
}
$('.cart-item-decrease').click(function(){
price('quantity--');
});
or
$('.cart-item-increase').click(function(){
price('quantity++');
});
You can customise the event handler registration so that additional data gets sent to your function:
function myClickHandler(e)
{
// ...
quantity += e.data.increment;
// ...
}
$('.cart-item-increase').on('click', null, {
increment: 1
}, myClickHandler);
$('.cart-item-decrease').on('click', null, {
increment: -1
}, myClickHandler);
Here, the increment property gets sent to myClickHandler as e.data.increment.

Recognising variables while assigning a function to a variable in javascript

In my jQuery scripts, when the user closes a menu with an animation, I have to call a function after the closing animation is finished. I want to assign this function dynamically by calling a function openStrip() with a parameter. My code looks like:
var FUNCTION_JUST_AFTER_MENU_CLOSE = function(){};
function openStrip(stripId){
FUNCTION_JUST_AFTER_MENU_CLOSE = function(){
createStrip(stripId);
});
}
if I call openStrip("aStripId"), I expect FUNCTION_JUST_AFTER_MENU_CLOSE to be:
// #1
function(){
createStrip("aStripId");
}
whereas my current code gives:
//#2
function(){
createStrip(stripId);
}
i.e, the parameter passed to the function openStrip() is lost while assigning the function() to the variable FUNCTION_JUST_AFTER_MENU_CLOSE.
How can I avoid this.
EDIT: I discovered that my code is actually working. The problem was elsewhere. I got confused because when I looked at Chrome's debugger, it was showing me the function definition as is (#2 in above). But when it actually went down executing that function later in the code, it did evaluate the values of the passed argument, and endedup executing #1.
Thanks for the answer though. I am marking it correct because that is perhaps a better way of assigning the function.
The best way is to return a function, from openStrip like this
function openStrip(stripId) {
return function() {
createStrip(stripId);
};
}
For example,
function openStrip(stripId) {
return function() {
console.log(stripId);
};
}
openStrip("aStripId")();
# aStripId
openStrip("bStripId")();
# bStripId
You can even assign the function objects returned to different variables and use them later on
var aStrip = openStrip("aStripId");
aStrip();
# aStripId
aStrip();
# aStripId

JQuery named functions with scope

I need to execute a single function on a set of adjacent DIVS on my page. I thought it would work best like this:
function namedFunction(){
setTimeout(3000);
//do animations and such
$(this).next().call(namedFunction);
}
$("#firstdiv").call(namedFunction());
but I can't find a way to arbitrarily execute a function in a set scope (the firstdiv object), and moreover I'm not even sure if the scope will be passed correctly. Is there a way to do this, or a better way to do this?
I guess you want this:
function namedFunction () {
this.animate({ ... }, function () {
namedFunction.call( $( this ).next() );
});
}
and then:
namedFunction.call( $('#firstdiv') );
The setTimeout() function doesn't pause the current execution, it queues a future execution of a function that you supply. To do something when the page loads use a document ready handler.
I think you want something like this:
$(document).ready(function() {
var $divs = $("div.commonClass"),
i = 0;
function processNextDiv() {
namedFunction($divs.eq(i));
i = (i + 1) % $divs.length;
setTimeout(processNextDiv, 3000);
}
processNextDiv();
});
This first selects all the divs (I'd suggest doing so by giving them a common class, but you can do that any way you like), then sets up a timeout based loop that calls namedFunction() passing it the current div to be processed. So then you could have:
function namedFunction($div) {
$div.animate(...); // process current div
}
Or you could put the animation or other div processing code directly in that processNextDiv() function, depending on whether you want to be able to call namedFunction() from other places.
Demo: http://jsfiddle.net/hWLTb/
Maybe this?
$("#firstdiv").nextAll("div").andSelf().each(function(){
// this = DIV
});

Passing jQuery .click() a function as a variable

I'm working with a tabbed interface and have the following jQuery function set up to handle the click events of my tabs.
$(document).ready(function () {
$('a#foo').click(function() {
//content, various calls
return false;
});
});
The above is an example of one of my tabs, the others are also within the same document ready block. What I needed to do was make it so the currently selected tab could not be re-clicked and that in some other cases I could manually disable tabs if needed. I achieved this via the following:
$('a#play').unbind('click');
This works fine, and it certainly disables the tabs but the problem then becomes rebinding the click action that was once there. I achieved this via the bind function:
$('a#foo').bind('click', function() {
//the same content and calls as before
return false;
});
This also works fine, but it has become exceedingly cluttered as I have added tabs to my UI. The immediate solution appears to be to create the function as a variable and then pass it into the initial click creation and into the binding event. Like so:
var Foo = new function() {
//same content and calls as before
return false;
}
$('a#foo').click(Foo());
$('a#foo').bind(Foo());
This, for one reason or another, seems to be causing browser crashing issues. Is it not possible to pass a function as a var in this case or am I just doing it wrong? Alternatively, is there a better way to achieve the results I'm looking for? Thanks.
$('a#foo').click(Foo());
$('a#foo').bind(Foo());
The Foo gives you the function, but adding ()'s after it means you are calling the function instead of passing the function itself. Since you're calling the function, false ends up getting passed to click and bind, obviously not doing anything. Some of your other problems might result from the fact that you simulating switching to that tab twice (calling the event handler twice).
var Foo = function() {
//same content and calls as before
return false;
}
$('a#foo').click(Foo);
$('a#foo').bind(Foo);
^^ should do what you want.
Alternatively, is there a better way to achieve the results I'm looking for?
Currently all we really know about your design is that you are calling using a click event handler to switch tabs. That part is awesome, but we'll need more info to give you the deeper answer you really want. If you post the code inside Foo we should be able to help a bit more. :D
EDIT: credit to SLaks♦ for noticing the new in the function declaration that I missed. I'll add a little more detail to his explanation:
When you write var foo = new
function(...) { ... }, you're making a
function literal, then calling it as a
constructor.
It's equivalent to
var SomeClass = function(...) { ... };
var foo = new SomeClass;
without the SomeClass dummy variable.
The function() {} is an anonymous function as you would expect. new in javascript is a little more confusing. When you call a function and precede it with new, you are using that function to instantiate an instance of a class defined in the function. In JS, unlike most other languages, the entire definition of a class is in one constructor function, from which you set all the instance variables, like so:
Foo = function() {
this.a = "lala";
this.b = 5;
}
To make instance methods of the 'class', you use the prototype attribute. However I just realized I've gotten super off-topic. Read more on that here and here. :D
You need to remove new from the function definition and stop calling the function when using it.
When you write var foo = new function(...) { ... }, you're making a function literal, then calling it as a constructor.
It's equivalent to
var SomeClass = function(...) { ... };
var foo = new SomeClass;
without the SomeClass dummy variable.
You need to simply assign the function literal to the variable.
When you write .click(foo()), you're calling foo, and passing the result to click.
Unless foo returns a function, that's not what you want to do.
You need to pass foo itself by removing the parentheses.
So firstly, click accepts a function, but you call without the () as click runs the function when ready. By adding the () you call it straight up.
Secondly, bind takes a string (what event you are binding to) AND a function (as above)...
Use the following:
function Foo() {
//same content and calls as before
return false;
}
$('a#foo').click(Foo);
$('a#foo').bind('click', Foo);
Hope that helps :)
Try:
var foo = function() // not "new function", as this creates an object!
{
return false;
}
$("a#foo").click(foo); // not "Foo()", as you can't call an object!
As for a better way to achieve the result you're looking for, you could have a class on every tab, such as .tab. That way, you can just do:
$("a.tab").click(function() { return false; });
... without having to fluff around with a lot of ids.
Take a different approach, and do not unbind().
I assume the tabs are all in a common container. If so, just use the delegate()(docs) method to place a handler on the container.
Here's a generic code example:
$('#container').delegate('.tab:not(.selected)', 'click', function() {
$(this).addClass('selected')
.siblings('selected').removeClass('selected');
// rest of the tab code
});
This will only trigger clicks on .tab elements that do not have the .selected class. You'll need to modify for your specific code.
Adding the parenthesis calls the function, but if you wanted to make it cool and stuff, you could make it so that Foo returned the function to be bound.
function Foo(){
return function(){
//your onclick event handler here.
};
}
$('a#bar').bind(Foo())
This makes use of one on javascript's function programming aspects, closures, which is cool, but not as efficient as some of the other answers. You should do some research about closures, as they can be used to make some cool stuff.
http://www.javascriptkit.com/javatutors/closures.shtml

Categories