JavaScript " is not a function" error when passing function as argument - javascript

I have this piece of code in html header:
<script src="../scripts/scripts.js"></script>
<script>
$(document).ready(function() {
window.LastConnection=null;
AjaXUpdateDetails(SetDetailsToHtmlPage);
});
</script>
in scripts.js I have:
var SetDetailsToHtmlPage=function (details_array){
window.LastConnection;
if (window.LastConnection!==null) {
if (window.LastConnection!==details_array.last_connection) {
$("#EStatus").val(details_array.status);
}
}
};
and
function AjaXUpdateDetails(interfaceUpdateFunc){
var ajaxRequest;
if ( (ajaxRequest=getAjaxObject())===false ) return;
ajaxRequest.onreadystatechange = function() {
if(ajaxRequest.readyState == 4) {
var data_array =jQuery.parseJSON(ajaxRequest.responseText);
interfaceUpdateFunc(data_array);
}
};
var queryString = "?id=1";
ajaxRequest.open("GET", "../scripts/AjaXgetDetails.php" + queryString, true);
ajaxRequest.send(null);
}
I read a lot about passing functions as arguments and thought I understood the idea but I keep getting error:
TypeError: interfaceUpdateFunc is not a function
interfaceUpdateFunc(data_array);
Where do I make a mistake?
thanks and regards
Tom

Try checking it before you pass it to AjaXUpdateDetails:
$(document).ready(function() {
window.LastConnection=null;
// verify it is defined here
console.log(SetDetailsToHtmlPage, typeof SetDetailsToHtmlPage)
AjaXUpdateDetails(SetDetailsToHtmlPage);
});
I suspect that will tell you the function is undefined at this point (scope issue). You can probably fix it by changing:
var SetDetailsToHtmlPage=function (details_array) {
to
function SetDetailsToHtmlPage(details_array) {

Here are couple of investigative changes that you can try:
1) The Function expression var SetDetailsToHtmlPage=function (details_array) is not in a function so it has global scope. Because it is an expression then the variable will be "hoisted" which means it is declared before it is assigned to the function. So try declaring it as a function: function SetDetailsToHtmlPage(details_array). This will cause the function declaration including the function to get "hoisted" in the same scope as the document ready event.
2) If that has no effect then try using jQuery document event .load() as opposed to .ready() to see if that has an effect.
$(document).load(function() {
window.LastConnection=null;
AjaXUpdateDetails(SetDetailsToHtmlPage);
});

interfaceUpdateFunc is a param. You could not call by that way.
if (typeof(interfaceUpdateFunc) == "function") {
interfaceUpdateFunc.apply(this, [data_array]);
}
You can check object is a function by this way:
function isFunction(func) {
return Object.prototype.toString.call(func) == '[object Function]';
}
And
if (isFunction(interfaceUpdateFunc)) {
interfaceUpdateFunc.apply(this, [data_array]);
}

Related

JS Revealing Pattern event undefined issue

I am using the modular design pattern for JS and I keep running into issues when using arguments bound functions. I have a particular function that I would like to bind to different events to keep from having to write the function for each bound event. The only difference in the function, or the argument, is the table that will be updated. The problem is that when I build a function with the arguments I need and pass those arguments to bound events, I get an undefined error, in the console, on load. Keep in mind, I want to stick with this design pattern for the security it offers.
Here is my JS:
var Users = (function(){
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
$addRoleForm.submit(ajaxUpdate(event, $rolesTableBody));
function ajaxUpdate(event, tableName) {
event.preventDefault();
event.stopPropagation();
var url = this.action;
var data = $(this).serialize();
var $this = $(this);
$.ajax({
type: 'POST',
url: url,
dataType: 'json',
data: data,
success: function(data) {
if(data.st === 0){
$messageContainer.html('<p class="alert alert-danger">' + data.msg + '</p>');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
} else {
$messageContainer.html('<p class="alert alert-success">' + data.msg + '</p>');
tableName.fadeOut().html('').html(data.build).fadeIn();
$this.find('input').val('');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
}
},
error: function(xhr, status, error){
console.log(xhr.responseText);
}
});
}
})();
Here is the error I get in the console, on load:
Uncaught TypeError: Cannot read property 'preventDefault' of undefined
I have tried to bind the event like this: $addRoleForm.on('submit', ajaxUpdate(event, $rolesTableBody)); and receive the same results.
Any ideas how to fix this?
You're seeing that issue, because the way you have it written now, ajaxUpdateexecutes, returns undefined and THEN passes undefined to the event listener, so you're basically doing this: $addRoleForm.submit(undefined).
2 Choices here:
1) You can wrap it in an anonymous function:
$addRoleForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someValue);
});
$someOtherForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someOtherValue);
});
2) You can set the first argument in-advance using bind:
$addRoleForm.submit(ajaxUpdate.bind($addRoleForm, someValue));
$someOtherForm.submit(ajaxUpdate.bind($someOtherForm, someOtherValue));
Using this way, you're binding the value of this to be $addRoleForm, setting the first argument to always be someValue, so it's the same as:
ajaxUpdate(someValue, event) {
//value of "this" will be $addRoleForm;
}
To pass the event, and the custom argument, you should be using an anonymous function call
$addRoleForm.submit(function(event) {
ajaxUpdate(event, $rolesTableBody));
});
This is by far the easiest and most readable way to do this.
What you're doing right now equates to this
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
var resultFromCallingFunction = ajaxUpdate(event, $rolesTableBody); // undefined
$addRoleForm.submit(resultFromCallingFunction);
Where you're calling the ajaxUpdate function, as that's what the parentheses do, and pass the returned result back to the submit callback, which in your case is undefined, the default value a function returns when nothing else is specified.
You could reference the function, like this
$addRoleForm.submit(ajaxUpdate);
but then you can't pass the second argument
The question refers to the Revealing Module pattern. Benefit of using this design is readability. Going with the anon function may work, but defeats the overall purpose of the module pattern itself.
A good way to structure your module to help maintain your scope is to setup helper functions first, then call a return at the end.
Example use case with events:
var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init
}
}().runMe();
Helps to "modularize" your workflow. You are also writing your revealing pattern as an IIFE. This can cause debugging headaches in the future. Editing the IIFE to instead invoke via the return is easier to maintain and for other devs to work with and learn initially. Also, it allows you to extend outside of your IFFE into another Module, example:
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.runMe();
anotherFunc();
}
return {
addClothes: init
}
}().addClothes();
I hope this helps to give you a better understanding of how/when/why to use the JS revealing pattern. Quick note: You can make your modules into IIFE, that's not a problem. You just limit the context of the scope you can work with. Another way of doing things would be to wrap the var User and var Clothes into a main module, and then make that an IIFE. This helps in preventing polluting your global namespace.
Example with what I wrote above:
// MAIN APPLICATION
var GettinDressed = (function() {
// MODULE ONE
///////////////////////////
Var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init,
style: someFunc
}
}();
// MODULE TWO
//////////////////////////
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.style();
anotherFunc();
}
return {
dressUp: init
}
}();
// Define order of instantiation
User.runMe();
Clothes.dressUp();
}());

probably moronic js syntax error. Object is null

var fbToggle = document.getElementById("fbToggle");
and later in the script
fbToggle.addEventListener("click", toggle("fbContainer"));
Console tells me that fbToggle is NULL
This is in the document though.
<input type="checkbox" id="fbToggle">
I wasnt using eventListener before, so maybe there is a special order of declaration i'm missing ?
EDIT :
entire js :
function toggle(target) {
var obj = document.getElementById(target);
display = obj.style.display;
if (display == "none") {display = "block"}
else {display = "none"}
}
function init() {
var fbToggle = document.getElementById("fbToggle");
var twitToggle = document.getElementById("twitToggle");
var pinToggle = document.getElementById("pinToggle");
console.log(fbToggle); // NULL
fbToggle.addEventListener("click", toggle("fbContainer"));
twitToggle.addEventListener("click", toggle("twitContainer"));
pinToggle.addEventListener("click", toggle("pinContainer"));
}
window.onload = init();
HTML is way too long.but JS is in head, called from external file. Also i'm not in quirk mode.
It is not clear where "later in the script" is. If it is in different scope definitely it is not going to work. Suggesting you to keep everything in a global object if possible so that you can access from different places in the script.
window.globals = {};
window.globals.fbToggle = document.getElementById("fbToggle");
window.globals.fbToggle.addEventListener("click", function () {
toggle("fbContainer")
});
function toggle(container) {
alert(container);
}
http://jsfiddle.net/ST938/
Another point is addEventListener expects a function or function idenitifier, NOT a function call.
addEventListener("click", toggle("fbContainer")); // wrong
addEventListener("click", toggle); // correct
So if you want to pass a parameter
window.globals.fbToggle.addEventListener("click", function () {
toggle("fbContainer")
});
function toggle(container) {
alert(container);
}
In JavaScript, putting brackets after a function name causes it to be called. If you want to reference a function without calling it you must not put brackets after the name:
window.onload = init(); // this calls init() immediately
window.onload = init; // this correctly stores init in window.onload
The same applies to toggle(). If you need to pre-specify some of the arguments you can wrap it in an anonymous function:
fbToggle.addEventListener("click", function() { toggle("fbContainer"); });
or you can use bind:
fbToggle.addEventListener("click", toggle.bind(null, "fbContainer"));

De-Anonymizing an Anonymous Function in jQuery

This is probably quite a simple problem, but it's causing me to scratch my head, so I'm posting it here.
I have some jQuery in the following form:
if (jQuery('.SearchRegions:checked').length == 0) {
jQuery('.SearchRegions').each(function(){
//code
});
} else {
jQuery('.SearchRegions:checked').each(function(){
//the same code
});
}
Obviously it seems ridiculous to repeat a big block of code inside each of these functions. But when I tried to name and move the function, it all seemed to break down - perhaps because of issues with scope and/or jQuery(this) inside the function no longer referring to the same object?
Can anyone help me by posting a general idea of what my code should look like? (Or any other optimisings or recastings to make it work would be much appreciated!)
You can definitely just define a function and use it by name:
function someHandler(event) {
// code code code
}
jQuery('.SearchRegions').each(someHandler);
Note that when you refer to the function by name, you don't include "()".
Assuming that closures are indeed the problem, you can parameterize your "anonymous" function to pass those values in to it. For example:
function eachRegion(values, $container, foo) {
// common code which uses scope variables `values`, `$container`, and `foo`
}
// elsewhere, in code defining `values`, `$container`, and `foo`...
if (jQuery('.SearchRegions:checked').length == 0) {
jQuery('.SearchRegions').each(function(){
eachRegion(values, $container, foo);
});
} else {
jQuery('.SearchRegions:checked').each(function(){
eachRegion(values, $container, foo);
});
}
You could define your function as a variable and use that in your each method call.
var yourEachFunction = function(){$("ul").append("<li>" + $(this).val() + "</li>");}
if (jQuery('.SearchRegions:checked').length == 0) {
jQuery('.SearchRegions').each(yourEachFunction );
} else {
jQuery('.SearchRegions:checked').each(yourEachFunction );
}
Example of this working on jsfiddle.

Send a variable to a variable of a function?

Let's say I have a function and one of the parameters is for the name of the target variable.. Would it be possible for me to send a variable to the function like this:
function otherfunction(input){
...
}
function test {target) {
var x = 1;
target(x);
}
test(otherfunction);
The problem I have is that I'm making a greasemonkey script and one of the variable I need can't be returned from the function due to a limitation.. So this would be the alternative. I just don't know how to get it to work.. Any help would be much appreciated!!
Your example (almost) works:
function otherfunction(input){
alert(input);
}
function test(target) {
if(typeof target !== 'function') {
alert('target is not a function!');
return;
}
target(1); //invokes the passed-in function, passing in 1
}
test(otherfunction); //alerts 1
//You can also do it with an anonymous function too:
test(function(arg) {
alert(arg * 5);
}); //alerts 5
jsFiddle example

Run a json string function

I have a json string like this:
json = "{'run': 'function() { console.log('running...'); }'}"
How do I run that function inside of the json string?
You're going to have to use the eval() (doc) function. A lot of people have a lot of feelings about this function. JSON is best for transporting data, not functions (see JSON). The functions ought to lay in the script on the page.
Also there's a syntax error in your posted code (function is wrapped in single quotes ('), and so is console.log's first parameter).
But...
json = "{\"run\":\"function() { console.log('running...'); }\"}"; //Fixed, thanks
obj = JSON.parse(json);
eval(obj.run); //Logs "running..."
Update:
Oh, and I was mistaken. Eval doesn't seem to like anonymous functions. With the revised code, it will parse json into an object with a run property that is a String, with value "function() { console.log('running...'); }". But when you eval(obj.run);, you will get a SyntaxError declaring an unexpected (. Presumably, this is the ( in function ().
So, I can think of two ways of dealing with this:
Remove the anonymous function in your actual JSON string (so, make your PHP forget about function () {), and eval it. This means it will be called as soon as you eval it.
What I think you want, is to be able to evaluate it to an anonymous function, that will be called when you want. So, you could write a wrapper function (you would need to follow option 1 for this as well):
function returnEval(str) {
return function () { eval(str); }
}
This would allow you to call it. So:
obj = JSON.parse(json);
obj.run = returnEval(obj.run);
obj.run(); //Logs "running..."
Hope this helps!
JSON is not really intended for that, but here seems to be a good example.
This works for me in Firefox:
var json = "{'run': 'function() { console.log(\\'running...\\'); }'}";
eval('var j = ' + json);
eval('var r = ' + j.run);
r();
Try this, it works:
var JS = { "function" : "alert( new Date().getTime() );" };
new Function ( "", JS["function"] )();
for nested functions you also can use something like this:
jQuery.each( JS, function( method, value ) {
new Function ( "a, b", "if ( a == 'function' ) { new Function ( '', b )(); }" )( method, value );
} );
I know that thread is old but i want to share with you guys. You can make string a json and parse it easily with these functions.
function makeString(val){
return JSON.stringify(val, function (key, value) {if (typeof value == 'function') {return value.toString();}return value;});
}
function parseIt(val){
return JSON.parse(string, function (key, value) {if (value.toString().search("function")>-1) {eval("var func = " + value);return func;}return value;});
}
Without using eval('function()') you could to create a new function using new Function(strName). The below code was tested using FF, Chrome, IE.
<html>
<body>
<button onclick="test()">Try it</button>
</body>
</html>
<script type="text/javascript">
function test() {
try {
var myJSONObject = {"success" : true, "jsFunc" : "myFunction()"};
var fnName = myJSONObject.jsFunc;
var fn = new Function(fnName);
fn();
} catch (err) {
console.log("error:"+err.message);
}
}
function myFunction() {
console.log('Executing myFunction()');
}
</script>

Categories

Resources