Debugging JavaScript Errors

form.submit is not a function

form.submit is not a function

HTML Forms have some really strange JavaScript behavior. We’ll often want to add JavaScript to enhance form controls and actions, but sometimes weird things happen, like the submit function disappearing.

Breaking down the Message

TypeError: form.submit is not a function

TypeError is a subset of JavaScript Error that is thrown when code attempts to do something that does not exist on the target object.

This message indicates that our code expects to have an object with a submit function. But if the property exists, it was not a function. In this case, we had a reference to a HTMLFormElement, which should have a submit function on its prototype.

This is a blocking error, and script execution will stop when this error occurs.

Understanding the Root Cause

Let’s say you have a simple contact form on your website, like this:


<form action="/contact" method="POST" id="myForm">
  <input type="text" name="name" placeholder="Name" />
  <input type="submit" name="submit" value="Submit" />
</form>

All is well until we try to attach some JavaScript to this form. If we need to inject some asynchronous action before the submit fires, like an AJAX lookup or send an analytics event, the code might look something like:


var form = document.querySelector("#myForm");
form.addEventListener("submit", function (evt) {
  //... do something cool.
  form.submit();
});

Boom. TypeError: form.submit is not a function

Wait, what? How is form.submit not a function? MDN says its a function, WHAT IS EVEN GOING ON? Then we see, hidden in the text is this really important and interesting line:

If a form control has a name or id of submit it will mask the form’s submit method.

Any named form control can be accessed as a property on the form object. In the example above form.name would reference the Name HTMLInputElement. But we also gave our submit button a name too, so rather than being the submit() function we want, it gives us a reference to the HTMLInputElement for submit.

HTMLFormElement is just trying to be helpful, but this is really weird. What happens if we do other important properties, like class or id?


<form class="form-control" id="myForm">
  <input name="id" />
  <input name="className" />
</form>
<script>
  var form = document.querySelector("#myForm");
  form.id; // => <input name="id" />
  form.className // => <input name="className" />
</script>

That feels like a bug waiting to happen. Even more weird, if we set the properties, like form.id = "foo";, that still works, even though we cannot get the value because it has been hidden by the input control.

How to Fix It

This is super weird. How do we avoid these problems? How should we be submitting forms and accessing properties?

1. Access the Prototype and Attributes Directly

Despite the simplicity of using form.submit or form.id, we should be more explicit about what we want to do. submit is a function on the prototype, so we should call it there:


var form = document.querySelector('form.my-form');
HTMLFormElement.prototype.submit.call(form);
Calling form submit on the prototype

Similarly, if we want to access attributes on the form element like id or action, we should use getAttribute():


var form = document.querySelector('form.my-form');
var id = form.getAttribute('id');
var action = form.getAttribute('action')
Getting form attributes

2. Monitor Your Environment

You’ll need to make sure that the issue is really fixed and that the problem stops happening. Mistakes happen, APIs change, and users are unpredictable. Just because your application works today, doesn’t mean an error won’t be discovered tomorrow. Tracking when exceptional events happen to your users gives you the context to understand and fix bugs faster. Check out TrackJS Error Monitoring for the fastest and easiest way to get started.

Other JavaScript Errors

JavaScript Error rollup report in TrackJS

About TrackJS

The easiest way to track JavaScript errors. TrackJS monitors real errors from your website and reports all the context you need to recreate the problem and fix the bug. No complicated dashboards or custom queries, just the data you need to build better web applications.