Passing value into function and variable scope in JQuery - javascript

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.

Related

Function stored to a variable not running when called

I'm working on a plugin for Trumbowyg where I'm trying to store a function in a variable so it can be called later but also be over-writable without altering the included javascript file.
The problem is, the function is not being called when I try to call it.
Here is my relevant code:
init: function (trumbowyg) {
var plugins = trumbowyg.o.plugins;
...
if(!plugins.giphycrumbs.close_modal) {
plugins.giphycrumbs.close_modal = function() {
console.log('close modal');
$(plugins.giphycrumbs.modal_selector).modal('hide');
}
}
$(document).on('click', '.add_giphy', function() {
trumbowyg.execCmd('insertImage', $(this).attr('src'), false, true);
plugins.giphycrumbs.close_modal;
});
// If the plugin is a button
trumbowyg.addBtnDef('giphycrumbs', {
//this function is handled exactly the same way except it actually works
fn: plugins.giphycrumbs.open_modal
});
}
In my code above, you can see I am checking if plugins.giphycrumbs.close_modal is NOT set, and if thats true, I set it to a function which is supposed to close a modal.
In my click handler for .add_giphy, the insertImage code works, but plugins.giphycrumbs.close_modal is never executed (I don't get the console.log message embedded in the function)
If I do console.log(plugins.giphycrumbs.close_modal) the expected function is put into the console.
Why is the close_modal function not executed in my code?
Answer
Try adding parentheses to close_modal inside your click handler.
Explanation
It seems to me like you are not invoking (calling) this function.
In your click handler, there's this line plugins.giphycrumbs.close_modal;
In javascript, this is a reference to a property on the giphycrumbs object. Though it happens to be a function, it will not be invoked as such unless you use parentheses after it (and optionally give it some arguments).
Hope that helps! 👍

Javascript : Replace Event Listener

I am listening to an event and want to call different methods. For example, I am listening to animation end event and the code is something like this:
this.inAnimationCallback = function() {
console.log('In');
_this.elem.className = _this.settings.className;
};
this.outAnimationCallback = function() {
console.log('Out');
_this.elem.parentNode.removeChild(_this.elem);
};
this.elem.addEventListener(animationEvent, this.inAnimationCallback);
setTimeout(function() {
_this.elem.addEventListener(animationEvent, _this.outAnimationCallback);
// Call some animation here.
}, 3000);
What happens here is that instead of replacing the method attached to the event, JS adds the method and when animation ends, both methods are called. Console looks like this:
(2) In
Out
I'm writing this answer for those like me, who is just started learning JS. And this thread came up first in google to "js replace event listener"..
Although, I am not disagreeing with the answers to use removeEventListener(), but mozilla warns that this function is not always successful. So use it with care. not willing to go that road i have found two other ways to do it.
Use something like GlobalEventHandlers which is simple as target.onclick = functionRef;. Mozilla even warns:
Only one onclick handler can be assigned to an object at a time.
Within listener function add external function call to action function, and then replace reference to another external action function. For example this code will call firstAction(), then seconAction(), then first again...:
const buttonOne = document.getElementById('buttonOne');
buttonOne.addEventListener('click', listenerFunction);
let doAction = firstAction; //assigning doAction to firstAction
function listenerFunction() {
doAction(); //external function call
}
function firstAction() {
doAction = secondAction; //assigning doAction to secondAction
console.log('first action clicked');
}
function secondAction() {
doAction = firstAction; //assigning doAction to firstAction
console.log('second action clicked');
}
<button type="button" id="buttonOne" name="button">button1</button>
I wrote this answer to broaden solution scope: would have saved at least 6 hours of my time. If I had this in the first place...
You can just remove the event listener before adding the new one :
setTimeout(function() {
_this.elem.removeEventListener(animationEvent, _this.inAnimationCallback);
_this.elem.addEventListener(animationEvent, _this.outAnimationCallback);
// Call some animation here.
}, 3000);

Trying to setup binding to a callback with different values

I am trying to build a jquery function that builds a series of buttons and binds them to the callback with different argument values. The issue I am having is that the callback is always called with the argument value of the last buttons[] value "cancel" no matter which button is clicked.
var buttons = ["OK|ok", "Cancel|cancel"];
buildButtons(buttons, function(response){
alert(response);
});
function buildButtons(buttons){
for (i = 0; i < buttons.length; i++){
var btnTitle = buttons[i].split("|")[0];
var btnName = buttons[i].split("|")[1].toString();
var btnElem = $('<button/>', {
type: 'button',
id: 'btn_' + btnName,
html: btnTitle,
})
$(btnElem).on('click', function () {
//..some other code
fnResponse(btnName); //<<------- Issue Should return 'ok' or 'cancel'
});
}
}
The problem is that the callback is always called with the value [btnName] of the last element.. no matter which button is clicked.
You need to change context for each literal. You can do it with jQuery each:
function buildButtons(buttons, fnResponse){
$.each(buttons, function(index, button)) {
// wrapped into diffent function, context changed
var btnTitle = button.split("|")[0];
var btnName = button.split("|")[1].toString();
var btnElem = $('<button/>', {
type: 'button',
id: 'btn_' + btnName,
html: btnTitle,
});
$(btnElem).on('click', function () {
//..some other code
fnResponse(btnName); //<<------- Issue Should return 'ok' or 'cancel'
});
});
}
Another solution: https://gist.github.com/4650951
Some simple example why you need another function: http://jsfiddle.net/nYmtV/
I've recreated the problem in here: http://jsfiddle.net/makque/zgHGc/
I have also included a log element so we could see what's exactly happening inside the code.
If you would try to run the code we can see that the for loop was executed and you can see that in our log element only the creation and addition of the button to the #menu was executed. And the event handler's code [inside] was skipped...
This therefore proves the definition for event handlers to be true:
A JavaScript can be executed when an event occurs, like when a user clicks on an HTML element.
To execute code when a user clicks on an element, add JavaScript code to an HTML event attribute:
onclick=JavaScript
To prove this, when we click the button that's the only time that code will run (as proven from our log)...
In connection with the problem:
Local variables or any variable for that matter (i.e., inside a for loop will retain it's last given value)
that means...for example, during
call 0=i: if var a = i; a=0
at call 1=i: the value of var a = i; a=1
consequently, when u run an eventhandler code using a local variable of the loop, when the loop has already been executed, it will give you it's current value, which in our case is CANCEL...
So how can we solve this, already?? &#&$!
During code execution of the for loop, all lines of code that met the condition are executed, this means the for these lines of codes:
1 $(btnElem).on('click', function () {
2 ...
3 alert(btnName);
4 });
Lines 1 and 4 were executed, while 2 & 3 were not, since they didn't meet the condition: on click
Running lines 1 and 4 meant that the button element has already registered that it has an Eventhandler onclick, and is just waiting to be executed.
In summary,
CAUSE: Using the wrong variables
SOLUTION: Use variable in context
$(btnElem).on('click', function () {
fnResponse($(this).html()); //passes the button's title
});
At last, we can have a code like these: http://jsfiddle.net/makque/mq9cJ/
Hope I helped! :)

How to define javascript event handlers dynamically with a function

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/

Why is function() needed sometimes in JavaScript?

HTML
<button id='hello'>Click Me!</button>
JavaScript (wrong)
$('#hello').click(alert('Hello, World!'));
JavaScript (correct)
$('#hello').click(function() {
alert('Hello, World!');
}
I'm wondering why the first JS code triggers on the event load instead of click. Can anyone tell me why function() { [code] } is needed for the script to work properly?
In this example, I used jQuery events, but this is not specific to it, for example, I need to use it with setTimeout, too.
The click function expects another function as a parameter.
In the first case you would be passing the result of calling alert('hello world');, which is null.
The second is just a shorthand for:
$('#hello').click(callback);
function callback(){
alert('hello world');
}
Because .click() is a handler. The first argument is a function to assign. But if you actually pass the function with arguments then it will call the function (in this case alert) and then pass it's return value.
Writing $('#hello).click( function() { } )` is basically a short hand for writing:
var myfunction = function() {
// code
};
$('#hello').click( myfunction );
As you can see in the long hand way, it's passed as a reference to the function instead of the function's return value.
Your first example says "evaluate
alert('Hello, World!')
right now, and pass the result as an argument to click. "
The second says "Define a function which will do the alert when I call it, and pass that whole function as an argument to click.
The function() { ... } syntax is how you declare an anonymous function in Javascript. jQuery uses lots of these to specify that some action will be performed later, like when an event occurs. You can think of it as delaying the execution of your function until necessary. Without this syntax, whatever code you place there is evaluated immediately, which is not what you want for an event handler.
You might think, "why isn't JavaScript smart enough to know the difference?" Consider this:
function returnCallback(linkId, data) {
return function(e) {
alert('Clicked on ' + linkId + '. Here is some data: ' + data);
// Maybe do some stuff with e, the event parameter
}
}
$('#some-link').click(returnCallback('some-link', 'some-data'));
$('#other-link').click(returnCallback('other-link', 'different-data'));
This is a contrived example, but it illustrates the power of anonymous functions and closures. This works since returnCallback returns a function.
In the first instance, "JavaScript wrong", you're actually calling alert('Hello, World!') at the point that the script is loaded. Now, the reason you pass the .click function a function is because it can call it at any point. Essentially, you're packing code together to be run (or not run at all) at any point when you put it in a function.
$('#hello').click(alert('Hello, World!')); is attempting to run alert('...') and pass its return value to the .click() function which will not work as expected.
This is because JavaScript evaluates everything and during this process your alert is invoked. You can use anonymous function or you can also use your own custom function as implemented below:
<script language="javascript" type="text/javascript">
$("#mybutton").click(clickFired);
function clickFired() {
alert('click fired');
}
</script>
The parameter required for the .click() function is a Function. Therefore $("#hello").click(function { [code] }); is required. Because there's nothing to return by alert().
The click function here assigns a value to the event handler.
With the first ("wrong") code you're assigning a value of alert('Hello, World!') which is itself a function call, so it's going to be immediately evaluated and hence appear at load.
With the second ("correct") code you're now assigning a new anonymous function which is not executed itself, just instantiated at load. Hence this will work as expected later.
somefunction(alert('hello! world'));
this would mean you want to pass to somefunction the return value of alert("hello! world").
jquery click expects a callback that it should fire upon click on the element. so you put it in a function which does not execute unless someone (here jquery) calls it explicitly.

Categories