我知道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或提交一切”?
我知道这是一个老问题,但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就可以声明性地完成所有这些工作。缺点是旧的浏览器不支持它们,所以您必须为旧的浏览器做一些填充。
这是一个老话题了,但这个可能对某些人有用:
正如上面有人提到的,你可以使用虚拟形式。
前段时间我不得不克服这个问题。起初,我完全忘记了这个HTML限制,只是添加了嵌套表单。结果很有趣——我从嵌套中丢失了我的第一个表单。然后,它被证明是一种“技巧”,只是在实际的嵌套表单之前添加一个虚拟表单(将从浏览器中删除)。
在我的例子中,它看起来是这样的:
<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和Safari。IE高达9(不确定10)和Opera不检测参数在主要形式。不管输入是什么,$_REQUEST全局变量都是空的。内在形式似乎在任何地方都很有效。
还没有测试这里描述的另一个建议-围绕嵌套表单的fieldset。
编辑:框架不起作用!
我只是在其他表单之后添加了Main表单(不再嵌套表单),并使用jQuery的“克隆”来复制按钮单击表单中的输入。添加.hide()到每个克隆的输入以保持布局不变,现在它就像一个魅力。
我仍然对这个讨论感兴趣。在最初的帖子后面是OP似乎共享的“需求”——即具有向后兼容性的表单。在我写这篇文章的时候,我的工作有时必须支持IE6(并且在未来的几年里),我理解这一点。
在不推动框架的情况下(所有组织都希望在兼容性/健壮性方面让自己放心,我并不是用这个讨论作为框架的理由),Drupal解决这个问题的方案是有趣的。Drupal也是直接相关的,因为这个框架有一个很长时间的政策,即“它应该在没有Javascript的情况下工作(只有当你想要的时候)”,即OP的问题。
Drupal使用相当广泛的形式。Inc函数来查找triggering_element(是的,这是代码中的名称)。请参阅form_builder API页面上列出的代码的底部(如果您想深入了解细节,建议使用源代码drupal-x.xx/includes/form.inc)。构建器使用自动HTML属性生成,通过它,可以在返回时检测按下了哪个按钮,并相应地采取行动(这些也可以设置为运行单独的进程)。
除了表单构建器之外,Drupal还将数据“删除”操作分割到单独的url /表单中,可能是出于最初文章中提到的原因。这需要一些搜索/列表步骤(另一种形式!但是用户友好的)作为一个初步。但这样做的好处是消除了“提交所有内容”的问题。包含数据的大表单用于预期目的,即数据创建/更新(甚至是“合并”操作)。
换句话说,解决这个问题的一种方法是将表单转换为两个,然后问题就消失了(HTML方法也可以通过POST进行纠正)。