Trying to setup binding to a callback with different values - javascript

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! :)

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);

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.

How functions get invoked in this code?

I can't understand the order of execution takes place here:
<script>
$(function(){
function f(id){
document.body.innerHTML += "<p>executing handler for div " + id + "</p>";
}
document.getElementById("div1").onclick = function(){
f("1");
}
document.getElementById("div2").onclick = function(){
f("2");
}
document.getElementById("div3").onclick = function(){
f("3");
}
});
</script>
What I want to know is how the 'function' and 'f' are called? Is it like when someone click on 'div' then function gets invoked? If it is so, why the function is on the right side of "=" operator?
When someone clicks on div1 the onclick method triggers function f with a passed value of 1. Ditto when div2/3 are clicked on, f is called with those values.
All f does is change the content of the page to show a message.
I'm not sure why this is using document.body.innerHTML though, I would normally expect to see a div that shows a message, such as document.getElementById('message').innerHTML.
I have a feeling (without checking) that document.body.innerHTML will change the whole content of the page to the value that f outputs. I doubt that is the desired result.
Explained in comments, line by line:
<script>
// this is a jQuery shorthand for $(document).ready. That means, that this function is executed automatically, when the DOM is ready
$(function(){
// declaration of a function that will be executed when it's called from somewhere. 'id' is an argument that can be passed
function f(id){
document.body.innerHTML += "<p>executing handler for div " + id + "</p>";
}
// 'onclick' is an event handler. When you click the div container with the id 'div1', then the function, set after '=', gets executed
document.getElementById("div1").onclick = function(){
// call the function that you declared above with the argument "1"
f("1");
}
document.getElementById("div2").onclick = function(){
f("2");
}
document.getElementById("div3").onclick = function(){
f("3");
}
});
</script>
If I'm understanding you correctly based on your question of "why the function is on the right side of = operator?", your question really related to = function(){ in the following code.
document.getElementById("div1").onclick = function(){
f("1");
}
What this code is doing is assigning an anonymous function to the onclick property of the div1 element.
When a user clicks on the div1 element, this anonymous function is executed. Within the anonymous function, a call is made to the function f passing the string "1".
The reason the anonymous function is needed is because if you were to exclude this and simply have this:
document.getElementById("div1").onclick = f("1");
Rather than calling the f function when the element is clicked, you would immediately call the f function and set the returned value (undefined) to the onclick property. By wrapping it in an anonymous function, you get the desired effect of calling f with the given parameter when the element is clicked.
According to what you have asked
$(function(){
});
gets executed on load of a page
if you want to call function f() you need to call as
$(function(){
f();
});
$(function(){...} is the jQuery function for document.ready. This function is executed as soon as all of the DOM is ready. It is a feature of jQuery. You don't call it explicitly - jQuery handles that for you.
The f() function is attached to the click handlers (onclick) that are defined for the three div elements. When they are clicked, they trigger the f() function.
The function is on the right side of an assignment because what the code is actually saying is replace the default onclick function with the one defined.

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/

Categories