Testing if value is a function
Asked Answered
P

18

106

I need to test whether the value of a form's onsubmit is a function. The format is typically onsubmit="return valid();". Is there a way to tell if this is a function, and if it's callable? Using typeof just returns that it's a string, which doesn't help me much.

EDIT: Of course, I understand that "return valid();" is a string. I've replaced it down to "valid();", and even "valid()". I want to know if either of those is a function.

EDIT: Here's some code, which may help explain my problem:

$("a.button").parents("form").submit(function() {
    var submit_function = $("a.button").parents("form").attr("onsubmit");
    if ( submit_function && typeof( submit_function.replace(/return /,"") ) == 'function' ) {
        return eval(submit_function.replace(/return /,""));
    } else {
        alert("onSubmit is not a function.\n\nIs the script included?"); return false;
    }
} );

EDIT 2: Here's the new code. It seems that I still have to use an eval, because calling form.submit() doesn't fire existing onsubmits.

var formObj = $("a.button").parents("form");
formObj.submit(function() {
    if ( formObj[0].onsubmit && typeof( formObj.onsubmit ) == 'function' ) {
        return eval(formObj.attr("onsubmit").replace(/return /,""));
    } else {
        alert("onSubmit is not a function.\n\nIs the script included?");
        return false;
    }
} );

Suggestions on possibly how to do this better?

Photomultiplier answered 28/4, 2009 at 14:44 Comment(0)
T
103

I'm replacing a submit button with an anchor link. Since calling form.submit() does not activate onsubmit's, I'm finding it, and eval()ing it myself. But I'd like to check if the function exists before just eval()ing what's there. – gms8994

<script type="text/javascript">
function onsubmitHandler() {
    alert('running onsubmit handler');
    return true;
}
function testOnsubmitAndSubmit(f) {
    if (typeof f.onsubmit === 'function') {
        // onsubmit is executable, test the return value
        if (f.onsubmit()) {
            // onsubmit returns true, submit the form
            f.submit();
        }
    }
}
</script>

<form name="theForm" onsubmit="return onsubmitHandler();">
<a href="#" onclick="
    testOnsubmitAndSubmit(document.forms['theForm']);
    return false;
"></a>
</form>

EDIT : missing parameter f in function testOnsubmitAndSubmit

The above should work regardless of whether you assign the onsubmit HTML attribute or assign it in JavaScript:

document.forms['theForm'].onsubmit = onsubmitHandler;
Tevis answered 28/4, 2009 at 18:48 Comment(2)
where does f in f.onsubmit come from?Chole
f is a form instance. You pass it to the testOnsubmitAndSubmit function as an argument. (I know this question is pretty old, but maybe my answer will save someone some time :) )Jezreel
G
75

Try

if (this.onsubmit instanceof Function) {
    // do stuff;
}
Gallous answered 28/4, 2009 at 14:50 Comment(1)
that's just a sample. You can change it to button.onsubmit instanceof FunctionGallous
H
14

You could simply use the typeof operator along with a ternary operator for short:

onsubmit="return typeof valid =='function' ? valid() : true;"

If it is a function we call it and return it's return value, otherwise just return true

Edit:

I'm not quite sure what you really want to do, but I'll try to explain what might be happening.

When you declare your onsubmit code within your html, it gets turned into a function and thus its callable from the JavaScript "world". That means that those two methods are equivalent:

HTML: <form onsubmit="return valid();" />
JavaScript: myForm.onsubmit = function() { return valid(); };

These two will be both functions and both will be callable. You can test any of those using the typeof operator which should yeld the same result: "function".

Now if you assign a string to the "onsubmit" property via JavaScript, it will remain a string, hence not callable. Notice that if you apply the typeof operator against it, you'll get "string" instead of "function".

I hope this might clarify a few things. Then again, if you want to know if such property (or any identifier for the matter) is a function and callable, the typeof operator should do the trick. Although I'm not sure if it works properly across multiple frames.

Cheers

Harpist answered 28/4, 2009 at 15:17 Comment(1)
I need to test this outside of the onsubmit though.Photomultiplier
E
5

What browser are you using?

alert(typeof document.getElementById('myform').onsubmit);

This gives me "function" in IE7 and FireFox.

Enzyme answered 28/4, 2009 at 14:49 Comment(3)
Even if your onsubmit is "return valid();"?Photomultiplier
Yes - don't forget, you can't have "return" outside a function.Enzyme
form.onsubmit will always be a function as long as it's defined as an HTML attribute. See my answer.Phosphorous
M
4

using a string based variable as example and making use instanceof Function You register the function..assign the variable...check the variable is the name of function...do pre-process... assign the function to new var...then call the function.

function callMe(){
   alert('You rang?');
}

var value = 'callMe';

if (window[value] instanceof Function) { 
    // do pre-process stuff
    // FYI the function has not actually been called yet
    console.log('callable function');
    //now call function
   var fn = window[value];
   fn();
}
Mckeon answered 28/4, 2009 at 14:44 Comment(2)
You don't really need that fn variable, you could just use window[value]();Shuffle
yea I know but I also realize that it is generally easier to understand in a long format and as this is primarily a site for learning your input is valued in any case @LajosMeszarosMckeon
O
4

Checking the call method on the value seems to be a good enough test. e.g., val.call && val()

> a = () => {}
[Function: a]
> function b() {}
undefined
> c = function(){}
  [Function: c]
> d = 2
2
> e = []
  []
> f = {}
{}
> a.call
  [Function: call]
> b.call
  [Function: call]
> c.call
  [Function: call]
> d.call
undefined
> e.call
undefined
> f.call
undefined

Note: Except when it's a class.

Oscillograph answered 4/10, 2021 at 10:40 Comment(1)
This is my preferred method too. Quick and effective. Just came here to see how others are handling it. Think I'll stick with this.Padegs
P
3

Make sure you are calling typeof on the actual function, not a string literal:

function x() { 
    console.log("hi"); 
}

typeof "x"; // returns "string"

typeof x; // returns "function"
Poon answered 28/4, 2009 at 14:48 Comment(0)
A
2

You can try modifying this technique to suit your needs:

 function isFunction() {
   var functionName = window.prompt('Function name: ');
   var isDefined = eval('(typeof ' + functionName + '==\'function\');');
   if (isDefined)
     eval(functionName + '();');
   else
     alert('Function ' + functionName + ' does not exist');
 }
 function anotherFunction() {
   alert('message from another function.');
 }
Antipyretic answered 28/4, 2009 at 14:49 Comment(0)
B
2

If it's a string, you could assume / hope it's always of the form

return SomeFunction(arguments);

parse for the function name, and then see if that function is defined using

if (window[functionName]) { 
    // do stuff
}
Biscuit answered 28/4, 2009 at 14:52 Comment(1)
i was about to add this resolution as well... just remember to register the function at the document/window levelMckeon
E
2

form.onsubmit will always be a function when defined as an attribute of HTML the form element. It's some sort of anonymous function attached to an HTML element, which has the this pointer bound to that FORM element and also has a parameter named event which will contain data about the submit event.

Under these circumstances I don't understand how you got a string as a result of a typeof operation. You should give more details, better some code.

Edit (as a response to your second edit):

I believe the handler attached to the HTML attribute will execute regardless of the above code. Further more, you could try to stop it somehow, but, it appears that FF 3, IE 8, Chrome 2 and Opera 9 are executing the HTML attribute handler in the first place and then the one attached (I didn't tested with jQuery though, but with addEventListener and attachEvent). So... what are you trying to accomplish exactly?

By the way, your code isn't working because your regular expression will extract the string "valid();", which is definitely not a function.

Edmondedmonda answered 28/4, 2009 at 17:32 Comment(0)
A
2

Isn't typeof xxx === 'function' the best and the fastest?

I made an bench in wich you can try it out, compared to instanceof and _underscore

  1. Its just seems to be faster than instanceof (using chrome)
  2. It won't trow an error if the variable is not defined

Here a bench: https://jsbench.me/qnkf076cqb/1

Anemograph answered 12/9, 2020 at 21:51 Comment(1)
No, it isn't the best. See other comments.Cauley
S
2

Beware that es6 class is also a function but not callable

class C {}

typeof C === "function"  // true
C instanceof Function    // true

C()       // error
C.call()  // error
new C()   // okay
new C     // okay
Subjoin answered 23/7, 2021 at 15:14 Comment(0)
D
1

Well, "return valid();" is a string, so that's correct.

If you want to check if it has a function attached instead, you could try this:

formId.onsubmit = function (){ /* */ }

if(typeof formId.onsubmit == "function"){
  alert("it's a function!");
}
Doublehung answered 28/4, 2009 at 14:50 Comment(0)
I
1
  if ( window.onsubmit ) {
     //
  } else {
     alert("Function does not exist.");
  }
Immoderate answered 28/4, 2009 at 14:53 Comment(0)
N
1

I think the source of confusion is the distinction between a node's attribute and the corresponding property.

You're using:

$("a.button").parents("form").attr("onsubmit")

You're directly reading the onsubmit attribute's value (which must be a string). Instead, you should access the onsubmit property of the node:

$("a.button").parents("form").prop("onsubmit")

Here's a quick test:

<form id="form1" action="foo1.htm" onsubmit="return valid()"></form>
<script>
window.onload = function () {
    var form1 = document.getElementById("form1");

    function log(s) {
        document.write("<div>" + s + "</div>");
    }

    function info(v) {
        return "(" + typeof v + ") " + v;
    }

    log("form1 onsubmit property: " + info(form1.onsubmit));
    log("form1 onsubmit attribute: " + info(form1.getAttribute("onsubmit")));
};
</script> 

This yields:

form1 onsubmit property: (function) function onsubmit(event) { return valid(); }
form1 onsubmit attribute: (string) return valid()
Nictitate answered 28/4, 2009 at 18:46 Comment(0)
K
1

You can always use one of the typeOf functions on JavaScript blogs such as Chris West's. Using a definition such as the following for the typeOf() function would work:

function typeOf(o){return {}.toString.call(o).slice(8,-1)}

This function (which is declared in the global namespace, can be used like this:

alert("onsubmit is a " + typeOf(elem.onsubmit));

If it is a function, "Function" will be returned. If it is a string, "String" will be returned. Other possible values are shown here.

Kunkle answered 3/7, 2012 at 17:42 Comment(0)
H
1
// This should be a function, because in certain JavaScript engines (V8, for
// example, try block kills many optimizations).
function isFunction(func) {
    // For some reason, function constructor doesn't accept anonymous functions.
    // Also, this check finds callable objects that aren't function (such as,
    // regular expressions in old WebKit versions), as according to EcmaScript
    // specification, any callable object should have typeof set to function.
    if (typeof func === 'function')
        return true

    // If the function isn't a string, it's probably good idea to return false,
    // as eval cannot process values that aren't strings.
    if (typeof func !== 'string')
        return false

    // So, the value is a string. Try creating a function, in order to detect
    // syntax error.
    try {
        // Create a function with string func, in order to detect whatever it's
        // an actual function. Unlike examples with eval, it should be actually
        // safe to use with any string (provided you don't call returned value).
        Function(func)
        return true
    }
    catch (e) {
        // While usually only SyntaxError could be thrown (unless somebody
        // modified definition of something used in this function, like
        // SyntaxError or Function, it's better to prepare for unexpected.
        if (!(e instanceof SyntaxError)) {
            throw e
        }

        return false
    }
}
Hover answered 31/8, 2013 at 8:28 Comment(0)
V
-4

A simple check like this will let you know if it exists/defined:

if (this.onsubmit)
{
  // do stuff;
}
Vallation answered 28/4, 2009 at 14:47 Comment(4)
This won't work. The if will assert to TRUE when onsubmit is a non-empty string as well.Doublehung
A horrible way of testing if something is a function - as indicated, and to be clearer: anything "truthy" would cause this code to "do stuff" and that might not be what is expected. If this.onsubmit were set to the value of 5 or "hello" or anything else in JavaScript that evaluates to true, you are going to get unexpected behavior.Chelsiechelsy
The subject of the question asks how to check if something exists. That's all I was suggesting - no more, no less.Vallation
Not really - the original posted states that he has the "need to test whether the value of a form's onsubmit is a function." Your condition isn't whether or not this.onsubmit is a funciton, your test simply asks whether or not this.onsubmit is "truthy"Chelsiechelsy

© 2022 - 2024 — McMap. All rights reserved.