我知道XHTML不支持嵌套的表单标记,我已经在Stack Overflow上阅读了关于这个主题的其他答案,但我仍然没有找到一个优雅的解决方案。

有些人说你不需要它,他们想不出有什么场景会需要它。就我个人而言,我想不出任何一种情况下我不需要它。

让我们看一个非常简单的例子:

你正在制作一个博客应用程序,你有一个带有一些字段的表单用于创建新帖子,还有一个带有“操作”的工具栏,如“保存”、“删除”、“取消”。

<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>

我们的目标是以一种不需要JavaScript的方式编写表单,只需要普通的HTML表单和提交按钮。

由于动作URL是在Form标签中定义的,而不是在每个单独的提交按钮中,所以我们唯一的选择是发布到一个通用URL,然后开始“if…then…”Else”来确定提交的按钮的名称。不是很优雅,但这是我们唯一的选择,因为我们不想依赖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.

你可能会说“只是实现‘删除’链接,而不是提交”。这在很多层面上都是错误的,但最重要的是,像“Delete”这样的副作用操作永远不应该是GET请求。

所以我的问题是(特别是对那些说他们不需要表单嵌套的人)你会做什么?有什么优雅的解决方案,我错过或底线真的是“要么需要JavaScript或提交一切”?


当前回答

在没有javascript的情况下,我可以添加一组单选按钮来定义要采取的操作:

更新 删除 无论

然后,操作脚本将根据单选按钮集的值采取不同的操作。

另一种方法是按照您的建议在页面上放置两个表单,只是不嵌套。虽然布局可能很难控制:

<form name="editform" action="the_action_url"  method="post">
   <input type="hidden" name="task" value="update" />
   <input type="text" name="foo" />
   <input type="submit" name="save" value="Save" />
</form>

<form name="delform" action="the_action_url"  method="post">
   <input type="hidden" name="task" value="delete" />
   <input type="hidden" name="post_id" value="5" />
   <input type="submit" name="delete" value="Delete" />
</form>

在处理脚本中使用隐藏的“task”字段进行适当的分支。

其他回答

如果你提交一个表单,浏览器也会发送一个输入提交名称和值。 所以你能做的是

<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="action:save" value="Save" />
        <input type="submit" name="action:delete" value="Delete" />
        <input type="submit" name="action:cancel" value="Cancel" />
    </div>
</form>

所以在服务器端,你只需要寻找以width string "action:"开头的参数,剩下的部分告诉你采取什么行动

所以当你点击保存按钮时,浏览器会给你发送类似foo=asd&action: Save =Save的东西

在没有javascript的情况下,我可以添加一组单选按钮来定义要采取的操作:

更新 删除 无论

然后,操作脚本将根据单选按钮集的值采取不同的操作。

另一种方法是按照您的建议在页面上放置两个表单,只是不嵌套。虽然布局可能很难控制:

<form name="editform" action="the_action_url"  method="post">
   <input type="hidden" name="task" value="update" />
   <input type="text" name="foo" />
   <input type="submit" name="save" value="Save" />
</form>

<form name="delform" action="the_action_url"  method="post">
   <input type="hidden" name="task" value="delete" />
   <input type="hidden" name="post_id" value="5" />
   <input type="submit" name="delete" value="Delete" />
</form>

在处理脚本中使用隐藏的“task”字段进行适当的分支。

我将按照您所描述的那样实现这一点:将所有内容提交给服务器,并执行简单的if/else检查单击了哪个按钮。

然后,我将实现一个Javascript调用绑定到表单的onsubmit事件,该事件将在表单提交之前进行检查,并且仅将相关数据提交到服务器(可能通过页面上的第二个表单将需要的ID作为隐藏输入处理,或使用您需要作为GET请求传递的数据刷新页面位置,或向服务器发送Ajax post等等)。

这样,没有Javascript的人也能很好地使用表单,但服务器负载被抵消了,因为有Javascript的人只提交所需的单个数据。让自己只专注于支持其中一个,会不必要地限制你的选择。

或者,如果您在公司防火墙或其他东西后面工作,并且每个人都禁用Javascript,您可能想要做两个表单,并使用一些CSS魔法,使其看起来像删除按钮是第一个表单的一部分。

您可以在页面上并排放置表单,但不嵌套吗?然后使用CSS使所有的按钮排成漂亮?

<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>

我知道这是一个老问题,但HTML5提供了一些新的选择。

第一种方法是将表单与标记中的工具栏分离,为删除操作添加另一个表单,并使用form属性将工具栏中的按钮与其各自的表单关联起来。

<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>

这个选项非常灵活,但最初的帖子也提到,可能需要对单个表单执行不同的操作。HTML5又来拯救我们了。您可以在提交按钮上使用formaction属性,因此同一表单中的不同按钮可以提交到不同的url。这个例子只是在表单之外的工具栏中添加了一个克隆方法,但是它在表单中嵌套的工作方式是一样的。

<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

这些新特性的优点是,它们无需JavaScript就可以声明性地完成所有这些工作。缺点是旧的浏览器不支持它们,所以您必须为旧的浏览器做一些填充。