How to submit a focused web form on Enter key

July 10, 2011 by Vlad

So, what is "focused form"? Imagine two forms on a single web page. Like Sign In and Register New Account forms that we have on the Download page of the Web Rule site. Each form has several controls and a button. If user types something in one of the text boxes, the form that contains that box is said to be "focused". If user begins typing inside of a box in the other form, the first form looses its focus and the second form becomes "focused".


The objective here is to submit the "focused" form when the user hits Enter. Try those two forms on our site to see that this functionality is quite convenient and intuitive. Type "blah blah" into the Registered Email field of the Sign In form and hit Enter. You should see the modal dialog displaying warnings related to the Sign In form. Now, without refreshing the page, type "blah blah" into the Email Address of the Register form and hit Enter. You should see the same dialog, but this time it displays warnings related to the Register form. Again, pretty convenient for the user.


To demonstrate how it's done, we need a simple html page with two forms on it. When I say "form" I don't necessarily mean the FORM tag. Any collection of controls can be called "a form". You need the FORM tag only if your page posts its data back to the server using the POST verb. There are other ways to submit data to the server. But in our case I'm going to create a page with two FORM tags, each containing a text box, a drop down and a button:


<html>
<head>
<title>Test page</title>
<script type="text/javascript">
</script>
</head>
<body>
<form id="formFirst" action="Test.htm" method="post">
<input type="text" id="txtOne" />
<select id="ddlOne">
<option value="1">One</option>
<option value="2">Two</option>
</select>
<input type="button" id="btnOne" value="One" />
</form>
<br />
<form id="formSecond" action="Test.htm" method="post">
<input type="text" id="txtTwo" />
<select id="ddlTwo">
<option value="1">One</option>
<option value="2">Two</option>
</select>
<input type="button" id="btnTwo" value="Two" />
</form>
</body>
</html>

Notice the empty SCRIPT tag in the header. I'm going to add some functions there. First, we need two functions, one for each form on the page. These functions will retrieve and submit one of the forms. They must be parameterless (I'll explain why later). Basically, they both define what happens when the form's button is clicked or the Enter is hit while the form is current. You can do whatever you'd like there. But, for the purpose of this small article, I'm going to use them to submit the forms:


function SubmitOne()
{
var form = document.forms[0];
alert("Submitting " + form.id);
form.submit();
};

function SubmitTwo()
{
var form = document.forms[1];
alert("Submitting " + form.id);
form.submit();
};

Each function gets a FORM, displays its client ID and submits it. Because I'm using the POST verbs, the server should throw an exception since this is a static HTML file and there is nothing on the server to handle our post. I'm going to include both functions into that empty SCRIPT tag. You can download the complete page from the References section at the bottom of this article.


I'm also going to use a couple of utility functions in this example. The first one is called GetKey. It returns the number of key that was pressed:


function GetKey(keyEvent)
{
var k = null;
if (keyEvent)
{
if (keyEvent.keyCode) k = keyEvent.keyCode;
else if (keyEvent.charCode) k = keyEvent.charCode;
else if (typeof (keyEvent.which) != "undefined") k = keyEvent.which;
return k;
}
else return window.event.keyCode;
};

The second utility function is called GetChildrenByTagName. It gets a container and a tag name as parameters and returns an array of child elements of the container by that tag name. I'm not using the standard DOM function element.getElementsByTagName here because it returns pretty much useless DispHTMLElementsCollection.


function GetChildrenByTagName(el, tagName)
{
var children = [], node;
if (el.childNodes)
{
for (var i = 0; i < el.childNodes.length; i++)
{
node = el.childNodes[i];
if (node.tagName && node.tagName == tagName) children.push(node);
if (node.childNodes && node.childNodes.length > 0)
{
var c = GetChildrenByTagName(node, tagName);
for (var j in c) children.push(c[j]);
}
}
}
return children;
};

Now that we defined all utilities, we need a way to initiate everything when the page loads. Let's do it in BODY's OnLoad event handler:


<body onload="Loaded()">

The Loaded function looks like this:


function Loaded()
{
document.getElementById("btnOne").onclick = function (e) { SubmitOne(); };
document.getElementById("btnTwo").onclick = function (e) { SubmitTwo(); };
};

It gets each button on the page and assigns the Submit function as button's OnClick handler. This has nothing to do with "Submit on Enter" thing but we still need to submit the form if its button was clicked. I will add a couple of lines to this function a bit later.


All of the above was just a small preparation for the "thing". Below is the function SubmitOnEnter. I'm just going to post it in its entirety and then explain the whole thing, line by line:


function SubmitOnEnter(form, delegate)
{
var one = GetChildrenByTagName(form, "INPUT");
var two = GetChildrenByTagName(form, "SELECT");

if (two.length > 0) one = one.concat(two);
if (one.length == 0) return;

var t;
for (var i in one)
{
t = one[i];

if (!t.tagName) continue;

if (t.tagName == "INPUT" &&
(t.type == "text" || t.type == "password") && typeof (t.onkeypress) != "function")
t.onkeypress = function (e) { subscribe(delegate); return true; };

else if (t.tagName == "INPUT" &&
(t.type == "checkbox" || t.type == "radio") && typeof (t.onclick) != "function")
t.onclick = function (e) { subscribe(delegate); return true; };

else if (t.tagName == "SELECT" && typeof (t.onchange) != "function")
t.onchange = function (e) { subscribe(delegate); return true; };
}

function subscribe(del)
{
document.onkeypress = function (e)
{
e = e || window.event;
var t = e.target || e.srcElement;

if (GetKey(e) == 13 && t.tagName && t.tagName != "TEXTAREA")
{
del();
document.onkeypress = null;
return false;
}
else return true;
}
};
};

This function gets all form's children with INPUT and SELECT tags as two arrays and concatenates them into one. It doesn't extract TEXTAREA elements because Enter in those elements means a new line, not "submit". The function then iterates through the array, filtering out all elements but radio buttons and drop downs, as well as text, password and check boxes. Each iteration assigns the internal "subscribe" function as event handler of each control. For text and password boxes the even is OnKeyPress, for radio buttons and checkboxes the event is OnClick, for drop downs it's OnChange. The assignment only happens if the correspondent handler is not already of "function" type, meaning that it checks if the control is not already "busy" doing something else on that event.


When the specified event of each control gets fired (meaning that the user is filling out this form by typing in some text or password, selecting items from the drop down or checking the radio button) its handler calls the internal "subscribe" function. The subscribe function tells the document to check each key to see if its number 13 (the Enter key) and if the target of the event wasn't a text area control (we need to make sure that the current hit on Enter wasn't because the user inserted a new line in text area.) If it's 13th key and not a new line, the function invokes the delegate received as a parameter of the parent function (this is why we needed those SubmitOne and SubmitTwo functions to be parameterless) and resets the document. That's all. Very simple.


Now, to complete the page, I only need to add calls to SubmitOnEnter for both forms to the Loaded function:


function Loaded()
{
document.getElementById("btnOne").onclick = function (e) { SubmitOne(); };
document.getElementById("btnTwo").onclick = function (e) { SubmitTwo(); };

SubmitOnEnter(document.forms[0], SubmitOne);
SubmitOnEnter(document.forms[1], SubmitTwo);
};

An important note: this code pretty much high jacks document's OnKeyPress event. This is not very nice. I'd even say, it's a programming by brutal force. This works only if you control the entire code and know for sure that no one else might use document's OnKeyPress now or in the future. Otherwise, you need to employ a custom code or any common framework such as jQuery, Scriptaculous or MS Ajax to properly attach and detach the code in "subscribe" function to the document's OnKeyPress event.

HTML JavaScript Next »
Comments:
Name (optional):
Comment (URLs are allowed and must start with http:// or https://; all tags will be encoded):
Remaining character count:
SPAMMER? Comments on this site are not included in page source. The stuff that you might post here WILL NOT be indexed by search engines.