Say you have the following submittable <form>
:
<form id="myForm" action="http://google.com">
<label for="search">Search For:</label>
<input type="text" id="search" name="q">
<button type="submit">Find</button>
</form>
And you want to show the form in a jQuery UI dialog. You can do that with the code below:
$( "form" ).dialog({
open: function() {
// On open, hide the original submit button
$( this ).find( "[type=submit]" ).hide();
},
buttons: [
{
text: "Find",
click: $.noop,
type: "submit"
},
{
text: "Close",
click: function() {
$( this ).dialog( "close" );
}
}
]
});
Which looks like this:
The Problem
There’s one problem with this - the “Find” button doesn’t work. Because of the generated structure of a jQuery UI dialog, the buttons are not within the <form>
itself. Therefore, the buttons are not associated with the <form>
. You can see the structure below:
Note that the <button type="submit">
is not within the <form>
.
Solution
There are a few ways we can work around this. One is avoid the buttons option and leave the original submit button in place. This works, but you lose the formatted display.
Explicit Click Handler
Another solution is to add a click
handler to the button that submits the form:
var form = $( "form" ).dialog({
...
buttons: [
{
text: "Find",
click: function() {
form.submit();
}
}
...
]
});
This works, but you lose the browsers browser’s default enter key handling. Therefore it has to be replicated as well:
var form = $( "form" ).dialog({ ... });
form.on( "keypress", "input[type=text]", function( event ) {
if ( event.keyCode == 13 ) {
form.submit();
}
});
Form Attribute
An elegant solution to this problem is to use HTML5’s new form attribute. The attribute allows you to associate a <form>
with a <button>
, even if the <button>
is not a descendant of the <form>
. It’s even smart enough to keep the browser’s default enter key handling in place.
For example here’s the example <form>
with a sibling (rather than child) submit button:
<form id="myForm" action="http://google.com">
<label for="search">Search For:</label>
<input type="text" id="search" name="q">
</form>
<button type="submit" form="myForm">Find</button>
The <button>
is outside of the <form>
, but because its form
attribute is equal to the <form>
’s id
, the <button>
will submit the <form>
regardless.
Using the Form Attribute on a Dialog
To add this attribute to the dialog version, we’ll pass form: "myForm"
into the buttons
option for the Find button:
<form id="myForm" action="http://google.com">
<label for="search">Search For:</label>
<input type="text" id="search" name="q">
<button type="submit">Find</button>
</form>
<script>
$( "form" ).dialog({
open: function() {
// On open, hide the original submit button
$( this ).find( "[type=submit]" ).hide();
},
buttons: [
{
text: "Find",
click: $.noop,
type: "submit",
form: "myForm" // <-- Make the association
},
{
text: "Close",
click: function() {
$( this ).dialog( "close" );
}
}
]
});
</script>
Support
The form
attribute is implemented in Firefox, Chrome, Safari 5.1+, and Opera, HOWEVER, IE (as of version 10) still does not support the attribute.
Because the form attribute is so handy here, I wrote a polyfill that adds support in browsers that don’t support it natively: https://gist.github.com/tjvantoll/5911571. The source is below:
/**
* The form attribute can be used to associate a submit button with a form, even
* if the button is not a child of the <form> itself.
*
* This polyfill uses a support check taken from Modernizr and polyfills the
* functionality using jQuery.
*/
(function() {
// Via Modernizr
function formAttributeSupport() {
var form = document.createElement( "form" ),
input = document.createElement( "input" ),
div = document.createElement( "div" ),
id = "formtest"+ ( new Date().getTime() ),
attr,
bool = false;
form.id = id;
// IE6/7 confuses the form idl attribute and the form content attribute
if ( document.createAttribute ) {
attr = document.createAttribute("form");
attr.nodeValue = id;
input.setAttributeNode(attr);
div.appendChild(form);
div.appendChild(input);
document.documentElement.appendChild(div);
bool = form.elements.length === 1 && input.form == form;
div.parentNode.removeChild(div);
}
return bool;
};
if ( !formAttributeSupport() ) {
$( document )
.on( "click", "[type=submit][form]", function( event ) {
event.preventDefault();
var formId = $( this ).attr( "form" ),
$form = $( "#" + formId ).submit();
})
.on( "keypress", "form input", function( event ) {
var $form;
if ( event.keyCode == 13 ) {
$form = $( this ).parents( "form" );
if ( $form.find( "[type=submit]" ).length == 0 &&
$( "[type=submit][form=" + $( this ).attr( "form" ) + "]" ).length > 0 ) {
$form.submit();
}
}
});
}
}());
This can be included anywhere on the page after jQuery is included.
Note that this polyfill only attempts to add support for the form
attribute on submit buttons. The form
attribute can also be applied to other form elements such as <input>
s, <select>
s, and <textarea>
s, but that is beyond the scope of this script.
If you encounter any issues with this polyfill let me know in the comments.
Conclusion
If you need a dialog with a submit button the easiest solution is to add a click
handler that manually submits the form. While this is not ideal, it’ll work everywhere with no extra effort. Just make sure that you add Enter key handling to text inputs within the <form>
.
Long term the form
attribute is the solution to this problem. You can try it today as long as you’re aware that you need to polyfill for IE support.