I know that xhtml doesn't support nested form tags and I have already read other answers
here in stackoverflow regarding this subject, but I still haven't figured out an elegant solution to the problem.
Some say you don't need it and that they can't think of a scenario were this would be needed. Well, personally I can't think of a scenario that I HAVEN'T needed it.
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.
Since the action url is defined in the Form tag and not in each individual submit button, our only option is to post to a generic url and then start "if...then...else" to determine the name of the button that was submitted. Not very elegant, but our only choice, since we don't want to rely on javascript.
The only problem is that pressing "Delete", will submit ALL the form fields on the server even though the only thing needed for this action is a Hidden input with the post-id. Not very big deal in this small example, but I have forms with hundreds (so to speak) of fields and tabs in my LOB applications that (because of requirements) have to submit everything in one-go and in any case this seems very inefficient and a waste. If form nesting was supported, I would at least be able to wrap the "Delete" submit button inside it's own form with only the post-id field.
You may say "Just implement the "Delete" as a link instead of submit". This would be wrong in so many levels, but most importantly because Side-effect actions like "Delete" here, should never be a GET request.
So my question (particularly to those that say they haven't needed form nesting) is What do YOU do? Is there any elegant solution that I'm missing or the bottom line is really "Either require javascript or submit everything"?
Kind of old topic, but this one might be useful for someone:
As someone mentioned above - you can use dummy form. I had to overcome this issue some time ago. At first I totally forgot about this HTML restriction and just added the nested forms. Result was interesting - I lost my first form from the nested. Then it turned out to be some kind of a "trick" to simply add a dummy form (that will be removed from the browser) before the actual nested forms.
In my case it looks like this:
Works fine with Chrome, FireFox and Safari. IE up to 9 (not sure about 10) and Opera does not detect parameters in the main form. The $_REQUEST global is empty, regardless of the inputs. Inner forms seems to work fine everywhere.
Haven't tested another suggestion described here - fieldset around nested forms.
EDIT: Frameset didn't work! I simply added Main form after the others (no more nested forms) and used jQuery's "clone" to duplicate inputs in the form on button click. Added .hide() to each of the cloned inputs to keep layout unchanged and now it works like a charm.
I think Jason's right. If your "Delete" action is a minimal one, make that be in a form by itself, and line it up with the other buttons so as to make the interface look like one unified form, even if it's not.
Or, of course, redesign your interface, and let people delete somewhere else entirely which doesn't require them to see the enormo-form at all.
My solution is to have the buttons call JS functions which write and then submit forms outwith the main form
This discussion is still of interest to me. Behind the original post are "requirements" which the OP seems to share - i.e. a form with backwards compatibility. As someone whose work at time of writing must sometimes support back to IE6 (and for years to come), I dig that.
Without pushing the framework (all organisations are going to want to reassure themselves on compatibility/robustness, and I'm not using this discussion as justification for the framework), the Drupal solutions to this issue are interesting. Drupal is also directly relevant because the framework has had a long time policy of "it should work without Javascript (only if you want)" i.e. the OP's issue.
Drupal uses it's rather extensive form.inc functions to find the triggering_element (yes, that's the name in code). See the bottom of the code listed on the API page for form_builder (if you'd like to dig into details, source is recommended - drupal-x.xx/includes/form.inc). The builder uses automatic HTML attribute generation and, via that, can on return detect which button was pressed, and act accordingly (these can be set up to run separate processes too).
Beyond the form builder, Drupal splits data 'delete' actions into separate urls/forms, likely for the reasons mentioned in the original post. This needs some sort of search/listing step (groan another form!, but is user-friendly) as a preliminary. But this has the advantage of eliminating the "submit everything" issue. The big form with the data is used for it's intended purpose, data creation/updating (or even a 'merge' action).
In other words, one way of working round the problem is to devolve the form into two, then the problem vanishes (and the HTML methods can be corrected through a POST too).
One way I would do this without javascript would be to add a set of radio buttons that define the action to be taken:
Then the action script would take different actions depending on the value of the radio button set.
Another way would be to put two forms on the page as you suggested, just not nested. The layout may be difficult to control though:
Using the hidden "task" field in the handling script to branch appropriately.
I just came up with a nice way of doing it with jquery.