Problem
I’m aware that XHTML doesn’t support nested form tags, and I’ve already read numerous Stack Overflow answers on the matter, but I haven’t yet come up with an elegant solution.
Some argue that you don’t need it and that they can’t imagine a scenario in which you would. Personally, I can’t conceive of a situation where it wouldn’t be useful.
Let’s see a very simple example:
You are making a blog app and you have a form with some fields for creating a new post and a toolbar with “actions” like “Save”, “Delete”, “Cancel”.
<form
action="/post/dispatch/too_bad_the_action_url_is_in_the_form_tag_even_though_conceptually_every_submit_button_inside_it_may_need_to_post_to_a_diffent_distinct_url"
method="post">
<input type="text" name="foo" /> <!-- several of those here -->
<div id="toolbar">
<input type="submit" name="save" value="Save" />
<input type="submit" name="delete" value="Delete" />
<a href="/home/index">Cancel</a>
</div>
</form>
Our objective is to write the form in a way that doesn’t require JavaScript, just plain old HTML form and submit buttons.
Because the action URL is defined in the Form element rather than in each individual submit button, we have no choice but to post to a generic URL and then use “if…then…else” to figure out which button was submitted. Because we don’t want to rely on JavaScript, this is our only option.
The only issue is that pressing “Delete” would send ALL form fields to the server, despite the fact that all that is required for this action is a Hidden input with the post-id. In this tiny example, it’s not a significant concern, but in my LOB applications, I have forms with hundreds (so to speak) of fields and tabs that (according to regulations) must be submitted all at once, which seems inefficient and wasteful. I’d be able to wrap the “Delete” submit button inside its own form with only the post-id field if form nesting was permitted.
“Just implement the “Delete” as a link instead of submit,” you might argue. This would be incorrect on numerous levels, the most important of which being that side-effect actions such as “Delete” should never be performed via a GET request.
So, what do YOU do (especially for those who claim they haven’t used form nesting)? Is there a more elegant approach that I’m overlooking, or is the bottom line “Require JavaScript or submit everything”?
Asked by gCoder
Solution #1
I realize this is an old subject, however HTML5 adds a few new features.
The first step is to split the form from the toolbar in the markup, create a new form for the delete action, and use the form property to link the toolbar buttons to their individual forms.
<form id="saveForm" action="/post/dispatch/save" method="post">
<input type="text" name="foo" /> <!-- several of those here -->
</form>
<form id="deleteForm" action="/post/dispatch/delete" method="post">
<input type="hidden" value="some_id" />
</form>
<div id="toolbar">
<input type="submit" name="save" value="Save" form="saveForm" />
<input type="submit" name="delete" value="Delete" form="deleteForm" />
<a href="/home/index">Cancel</a>
</div>
This option is fairly versatile, but the original post also indicated that different tasks may be required with a single form. HTML5 saves the day once more. The formaction attribute can be used on submit buttons to allow various buttons in the same form to send data to distinct URLs. This example simply adds a clone function to the toolbar outside the form, but it would work in the form as well.
<div id="toolbar">
<input type="submit" name="clone" value="Clone" form="saveForm"
formaction="/post/dispatch/clone" />
</div>
http://www.whatwg.org/specs/web-apps/current-work/#attributes-for-form-submission
The benefit of these new capabilities is that they accomplish everything declaratively, without the use of JavaScript. The problem is that they aren’t compatible with earlier browsers, so you’ll have to do some polyfilling.
Answered by shovavnik
Solution #2
To see what button was pressed, use if/else.
plement a Javascript call to the form’s onsubmit event, which would check before submitting the form and only send the data that was relevant to the server (possibly through a second form on the page with the ID needed to process the thing as a hidden input, or refresh the page location with the data you need passed as a GET request, or do an Ajax post to the server, or…).
People who do not have Javascript will still be able to use the form, but the server load will be reduced because those who do have Javascript will only be submitting the single piece of data required. Limiting your options excessively by focusing on only supporting one of the two is a bad idea.
Alternatively, if you’re working behind a corporate firewall and everyone has Javascript turned off, you may create two forms and use CSS to make the delete button appear to be part of the first form.
Answered by One Crayon
Solution #3
Can you have the forms on the page side by side but not nested? Then you can use CSS to make all the buttons look nice?
<form method="post" action="delete_processing_page">
<input type="hidden" name="id" value="foo" />
<input type="submit" value="delete" class="css_makes_me_pretty" />
</form>
<form method="post" action="add_edit_processing_page">
<input type="text" name="foo1" />
<input type="text" name="foo2" />
<input type="text" name="foo3" />
...
<input type="submit" value="post/edit" class="css_makes_me_pretty" />
</form>
Answered by Jason
Solution #4
The “form” attribute for input elements in HTML5 has a concept of “form owner.” It will fix the problem by simulating nested forms.
Answered by Nishi
Solution #5
Although this is an old topic, it may be valuable to someone.
You can use a dummy form, as someone stated earlier. I had to deal with this problem a long time ago. I initially ignored the HTML constraint and simply implemented the nested forms. I lost my initial form from the nested as a result of the result. Then it was discovered that adding a dummy form (that will be removed from the browser) before the actual nested forms was a “trick.”
In my situation, it appears as follows:
<form id="Main">
<form></form> <!--this is the dummy one-->
<input...><form id="Nested 1> ... </form>
<input...><form id="Nested 1> ... </form>
<input...><form id="Nested 1> ... </form>
<input...><form id="Nested 1> ... </form>
......
</form>
Chrome, Firefox, and Safari all work properly. Up to version 9 of Internet Explorer (not sure about version 10) and Opera do not recognize parameters in the main form. Regardless of the inputs, the $_REQUEST global is empty. Inner forms appear to operate well in all situations.
Another idea given here – fieldset surrounding nested forms – has yet to be tested.
EDIT: Frameset isn’t working anymore! I simply placed the Main form after the others (no more nested forms) and used jQuery’s “clone” function to replicate inputs in the form when the button was pressed. To maintain the layout of the cloned inputs the same, I added.hide() to each of them, and it now works perfectly.
Answered by B.D.Joe
Post is based on https://stackoverflow.com/questions/597596/how-do-you-overcome-the-html-form-nesting-limitation