Javascript Window Function Pass "this" as Parameter - javascript

I am looping thru a JSON object and I want to set clicks on fields based on a function named in the JSON Object. The function called will by window[] will be parameterised instead of hardcoded.
for (var key in JSONOBJ) {
func = window["minRule"](this);
$('#'+ key).click(func);
}
The function being called is
function minRule(elem){
alert(elem.name);
}
and I want it applied to
<input type='text' name='FIELDINJSONOBJECT' id='FIELDINJSONOBJECT'/>
However when I refresh the page the function is being called for some reason and hasnt binded to the click. Also the alert(elem.name) is returning undefined. How do I pass this as the parameter to a function called with window[$FUNCTIONAME]
Regards,
Tom

I think what you are looking for is
$.each(JSONOBJ, function(key, value){
$('#'+ key).click(function(){
window["minRule"](value);
});
})
In your case you are invoking the function minRule in the loop with parameter this where it points to the current functions scope and the value returned by it (in this case undefined as the click handler), that is the reason it is not working

Related

Passing an array.push function in javascript

I have a problem passing an array.push function in javascript having a code like this:
const array = [];
addToArray("works", (e) => array.push(e));
addToArray("notWorks", array.push);
doConsoleLog("hello", console.log);
function addToArray(element, pushFn) {
pushFn(element);
}
function doConsoleLog(message, log) {
log(message);
}
Just curious how's that first call of addToArray works, but second causes TypeError: Cannot convert undefined or null to object. How is it possible? Type of array.push is a function. It is not possible to pass functions directly like in the case of console.log above, which works pretty well?
push is a method of array. When invoked, it uses this as a reference to the object it is being called on. If you pass it around like a normal function, you will lose that reference, the method will use the value of this defined at the moment of the call (inside addToArray, this is probably document.window), and will therefore fail.
In order to pass around a method, you need to bind it to the object you want it to operate on, in this case array. This is true for any object method in JavaScript. Function.prototype.bind() exists exactly for this purpose: it "binds" a function (in this case your method) to a given value of this, in order to work correctly.
const array = [];
addToArray("now it works", array.push.bind(array));
// or
addToArray("now it works", Array.prototype.push.bind(array));
function addToArray(element, pushFn) {
pushFn(element);
}
Once you pass a function like you did in the second case, it executes itself immediately, and array.push doesn't really mean something.
A callback function should be executed later on in the outer function.
Unlike the second example, in the first one you don't execute the function immediately, but only when you call it inside the outer function.
Your code have a lot of mistakes. What exactly should do this code? if you want to push new items in array use spread operator
const array = [...[], "works", "notWorks"]
const array = [];
addToArray("works", (e) => array.push(e));
addToArray("notWorks", array.push);
doConsoleLog("hello", console.log);
function addToArray(element, pushFn) {
pushFn(element); //function must return something
}
function doConsoleLog(message, log) {
log(message); //function must return something
}

Variable is undefined/out of scope

This is a scope issue, correct?
EDIT: http://jsfiddle.net/RPwLK/7/
On line 38 of the following function, where displayBossMessage() is called by itself, the variable "boss" seems to be out of scope. It should contain the name (string) of the current boss, and when the function is called, it should put it back in the "boss" argument.
But boss seems to always be undefined. I've tried creating a variable right before the jQuery listener is created to see if it would be within scope, but it seems as if it isn't.
Or maybe I'm just having a slow day. ;p
function displayBossMessage(boss,message,options,timer){
boss = bosses[boss];
//clear any possible lingering text/buttons
$(boss.messagebox).text('');
$(boss.optionsbox).text('');
displayMessage_CurrentBoss = boss;
//if no options provided, set a default "continue" button
if(options == ''){
options = {
'default' : {
'text' : 'Continue',
'func' : function(){}
}
}
}
$('#container div').hide();
$(boss.div).fadeIn(1500);
writeMessage(message,$(boss.messagebox),0);
setTimeout(function(){
$(boss.optionsbox).fadeIn(1000);
},3000);
//"listen" for a choice
var i = 0;
for(option in options){
$(boss.optionsbox).html($(boss.optionsbox).html() + '<button name="'+ i +'">'+ options[option].text +'</button> ');
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(){
options[option].func();
//close message screen or show defined response
if(typeof options[option].response != 'undefined'){
displayBossMessage(boss,options[option].response,'',true);
}else{
$(boss.div).hide();
$('#container div').fadeIn(1500);
}
});
}
if(timer){
//if they are afk/don't click after a minute, do it for them
setTimeout(function(){
$(boss.div+' button[name="0"]').click();
},60000);
}
}
Hope I'm not being completely oblivious and missing something so simple.
*Edit: Bosses variable (is global) *
(updated jsfiddle revision link to #11 which includes both solutions)
Looks like this could be a working fiddle: http://jsfiddle.net/RPwLK/11/
A minor problem: you have an extra ' on line 30 with the (second) alert call - the string literal was not closed correctly (or rather another was being opened). After that I was able to investigate and come up with the following conclusion (2 problems)...
The first problem was with the variable override here:
function displayBossMessage(boss,message,options,timer){
boss = bosses[boss]; // this line, boss was a string, now it will be an object
And the later usage in the same function here:
if(typeof options[option].response != 'undefined'){
displayBossMessage(boss,options[option].response,'',true); // first arg is now an object
The solution is to create a reference to the original boss when it was a string like:
function displayBossMessage(boss,message,options,timer){
var origBoss = boss; // let's remember what it was in its original string form
boss = bosses[boss];
And use it like so:
if(typeof options[option].response != 'undefined'){
displayBossMessage(origBoss,options[option].response,'',true); // now we're dealing with a string ref
The second problem is the reference to option within the for loop. It was always referencing the last value since the $(document).on('click'... is always delayed (asynchronous). There are a number of ways to solve this. I chose to use bind and pass in an argument with a reference to the value of option for each specific iteration.
Notice that in the original option is in the async function but not in a closure (for is not a closure):
for(option in options){
//...
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(){
options[option].func(); // here option is always the last item in options
So introduce an argument conveniently called option in the callback function for the click handler:
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(option){ // here
options[option].func(); // and now option is whatever the argument value is
And don't forget to pass it in the function declaration via bind:
$(document).on('click', (boss.div)+' button[name="'+i+'"]', function(option){
options[option].func();
// ...
}.bind(this,option)); // here we're passing whatever option is for the specific iteration as the first argument of the callback function
Note, this is just the scope, and each subsequent parameter for bind (see MDN) after the first corresponds to the arguments in the function definition.

Why isn't this jQuery firing on click? (w/ radio buttons)

function showHideSoldTo() {
if ($("#radio-text-sold-to").prop("checked")) {
$("#select-sold-to").hide();
$("#text-sold-to").show();
} else if ($("#radio-select-sold-to").prop("checked")) {
$("#text-sold-to").hide();
$("#select-sold-to").show();
}
}
$("#radio-text-sold-to").click(showHideSoldTo());
$("#radio-select-sold-to").click(showHideSoldTo());
All this is inside a document ready wrapper.
Remove the () from the function names in the click call. So..
$("#radio-text-sold-to").click(showHideSoldTo);
...
Remove the () from the function names in the click call
just try like
$("#radio-text-sold-to").click(showHideSoldTo);
$("#radio-select-sold-to").click(showHideSoldTo);
The problem is with the value you pass to the .click() method it's supposed to be a function value but instead you are invoking the function and as a result passing the return value of that function (which is equal to undefined)
the fix is simple you need to remove the () in these two lines
$("#radio-text-sold-to").click(showHideSoldTo());
$("#radio-select-sold-to").click(showHideSoldTo());
So they become
$("#radio-text-sold-to").click(showHideSoldTo);
$("#radio-select-sold-to").click(showHideSoldTo);
in javascript any identifier identifies a value. That value might be a simple value such as an integer or it might be a more complex object and as is the case with the value you pass as a callback or event handler it might be a function value.
These two lines of code are basically the same
function myFoo() {}
var myFoo = function() {}
and in the latter it's explicit that the right hand side is assigned to the left hand side. Ie that the function value is assigned to an identifier.

Using the each function to pass results to another function

I guess this might be a noob question but when using the each iterator, is it possible to send the results to a predefined function? I have only seen examples where the function is defined in the parantheses. Here is what I would like to do:
function setCheckboxes(key, value) {
....some code....
}
context.find('input[type="checkbox"]').each(setCheckboxes(key, value));
Is this possible? Or do I have to implement it this way:
context.find('input[type="checkbox"]').each(function(key, value) {
....some code....
});
You can just give the function reference as the callback for each.
context.find('input[type="checkbox"]').each(setCheckboxes);
In your callback setCheckboxes key will be the index and value will be the element (DOM element).
This is no different from writing an anonymous function, where you get the same 2 arguments, in this case you are just giving a reference to a function that expects the same 2 arguments. So the context inside the callback i.e setCheckboxes will be the DOM element.
You should be able to do:
function setCheckboxes(key, value) {
....some code....
}
context.find('input[type="checkbox"]').each(function(key, value){
setCheckboxes(key, value);
});

can not use Closure right

I wrote JS function and it must bind the buttons it generates depending on values in the array.
But it gives me the last value. I read that i had to use closure, I did, and I'm still not able to bind them right!
I'm still a beginner
I read about closure, I got the idea but still did not know what I'm missing
function addNewServices(newServicesArray){
var j=0; var x;
for (i in newServicesArray){
var html='';
html='<div style="width: 33%; float: leftt">'+newServicesArray[j].servicename+'</div>';
$("#main-menu").append(html);
$('#btn-'+newServicesArray[j].servicename).bind('click', function (){bindThis(j)});
j++;
}
var bindThis = function( j ) {
return function() {
alert(j); // gives 2 always
alert( newServicesArray[j].servicename );
};
};
}
you don't have to bind click in a loop... you can get the clicked refrence by $(this) in a function..
making it as simple as i can..
function addNewServices(newServicesArray){
var j=0;
for (i in newServicesArray){
var html='';
html='<div style="width: 33%; float: left">'+newServicesArray[j].servicename+'</div>';
$("#main-menu").append(html);
}
}
$(function(){
$(document).on('click','a[id^="btn-"]',function (){
var $this = $(this);
alert($this.attr('value'));
});
});
Because you have
function (){bindThis(j)}
Which gets called later when the value of j is 2.
You only need
bindThis(j)
which gets called with the different values
Closure is just the way function accesses variable from outer scope. The key word here is variable — variable may change, and if you access it afterwards (on click), you will access the later version of it.
So anyhow you need to store that association of j with jth button. Thanks to jQuery, bind method already have a facility just for this: its second parameter, eventData, is some user data that will be passed to event handler function.
So, changing this:
(..).bind('click',function (){bindThis(j)});
to this:
(..).bind('click', j, bindThis);
...should* work. Note that we don't need to create any wrapper-function. We simply pass bindThis function itself to bind, and tell bind that it will pass j to it when calling it.
(*) — not tested yet

Categories