如何克服HTML表单嵌套的限制?

我知道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。

唯一的问题是,按下“删除”,将提交所有的表单字段在服务器上,即使这个操作唯一需要的是一个隐藏的输入与后id。在这个小例子中不是什么大问题,但我在我的LOB应用程序中有数百个(可以说)字段和选项卡的表单,(由于需求)必须一次性提交所有内容,在任何情况下,这似乎非常低效和浪费。如果表单嵌套被支持,我至少能够将“Delete”提交按钮包装在它自己的表单中,只有post-id字段。

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

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

111311 次浏览

在没有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>

如果你真的不想使用多个表单(就像Jason建议的那样),那么就使用按钮和onclick处理程序。

<form id='form' name='form' action='path/to/add/edit/blog' method='post'>
<textarea name='message' id='message'>Blog message here</textarea>
<input type='submit' id='save' value='Save'>
</form>
<button id='delete'>Delete</button>
<button id='cancel'>Cancel</button>

然后在javascript中(为了简单起见,我在这里使用jQuery)(尽管添加一些onclick处理程序有点多余)

$('#delete').click( function() {
document.location = 'path/to/delete/post/id';
});
$('#cancel').click( function() {
document.location = '/home/index';
});

我也知道,如果没有javascript,这将使一半的页面无法工作。

我认为杰森是对的。如果你的“删除”操作是一个最小的操作,那就把它单独放在一个表单中,并把它与其他按钮对齐,这样界面看起来就像一个统一的表单,即使它不是。

或者,当然,重新设计你的界面,让人们完全删除其他地方,这根本不需要他们看到巨大的形式。

HTML5有一个“表单所有者”的概念——输入元素的“form”属性。它允许模拟嵌套表单,并将解决问题。

或者,你也可以在动态中分配表单actiob…可能不是最好的解决方案,但确实缓解了服务器端逻辑……

<form name="frm" method="post">
<input type="submit" value="One" onclick="javascript:this.form.action='1.htm'" />
<input type="submit" value="Two" onclick="javascript:this.form.action='2.htm'" />
</form>

对嵌套表单使用iframe。如果他们需要共享场地,那么……它并不是真正的嵌套。

为了回应Yar在评论中提出的问题,我提供了一些JavaScript,可以调整iframe的大小。对于表单按钮,可以安全地假设iframe在同一个域中。这是我使用的代码。你将不得不改变你自己的网站的数学/常数:

function resizeIFrame(frame)
{
try {
innerDoc = ('contentDocument' in frame) ? frame.contentDocument : frame.contentWindow.document;
if('style' in frame) {
frame.style.width = Math.min(755, Math.ceil(innerDoc.body.scrollWidth)) + 'px';
frame.style.height = Math.ceil(innerDoc.body.scrollHeight) + 'px';
} else {
frame.width = Math.ceil(innerDoc.body.scrollWidth);
frame.height = Math.ceil(innerDoc.body.scrollHeight);
}
} catch(err) {
window.status = err.message;
}
}

然后这样称呼它:

<iframe ... frameborder="0" onload="if(window.parent && window.parent.resizeIFrame){window.parent.resizeIFrame(this);}"></iframe>

我知道这是一个老问题,但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>

< a href = " http://www.whatwg.org/specs/web-apps/current-work/ attributes-for-form-submission " > http://www.whatwg.org/specs/web-apps/current-work/ attributes-for-form-submission < / >

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

我刚刚想出了一个很好的方法来做jquery。

<form name="mainform">
<div id="placeholder">
<div>
</form>


<form id="nested_form" style="position:absolute">
</form>


<script>
$(document).ready(function(){
pos = $('#placeholder').position();
$('#nested_form')
.css('left', pos.left.toFixed(0)+'px')
.css('top', pos.top.toFixed(0)+'px');
});
</script>
好吧,如果你提交一个表单,浏览器也会发送一个输入提交名称和值。 所以你可以做的是

<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的东西

这是一个老话题了,但这个可能对某些人有用:

正如上面有人提到的,你可以使用虚拟形式。 前段时间我不得不克服这个问题。起初,我完全忘记了这个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()以保持布局不变,现在它就像一个魅力

我的解决方案是让按钮调用JS函数编写,然后提交表单与主表单

<head>
<script>
function removeMe(A, B){
document.write('<form name="removeForm" method="POST" action="Delete.php">');
document.write('<input type="hidden" name="customerID" value="' + A + '">');
document.write('<input type="hidden" name="productID" value="' + B + '">');
document.write('</form>');
document.removeForm.submit();
}
function insertMe(A, B){
document.write('<form name="insertForm" method="POST" action="Insert.php">');
document.write('<input type="hidden" name="customerID" value="' + A + '">');
document.write('<input type="hidden" name="productID" value="' + B + '">');
document.write('</form>');
document.insertForm.submit();
}
</script>
</head>


<body>
<form method="POST" action="main_form_purpose_page.php">


<input type="button" name="remove" Value="Remove" onclick="removeMe('$customerID','$productID')">
<input type="button" name="insert" Value="Insert" onclick="insertMe('$customerID','$productID')">


<input type="submit" name="submit" value="Submit">
</form>
</body>
我通过包含一个复选框来绕过这个问题,这取决于人们想要做什么形式。 然后用1个按钮提交整个表单

我仍然对这个讨论感兴趣。原来的帖子后面是“要求”;OP似乎共享——即具有向后兼容性的形式。在我写这篇文章的时候,我的工作有时必须支持IE6(并且在未来的几年里),我理解这一点。

在不推动框架的情况下(所有组织都希望在兼容性/健壮性方面让自己放心,我并不是用这个讨论作为框架的理由),Drupal解决这个问题的方案是有趣的。Drupal也是直接相关的,因为这个框架有一个长期的政策:“没有Javascript也能工作(只有当你想要的时候)”。即OP的问题。

Drupal使用它相当广泛的form.inc函数来查找triggering_element(是的,这是代码中的名称)。参见API页面上列出的form_builder代码的底部(如果你想深入了解细节,建议使用源代码- drupal-x.xx /包括/ form.inc)。构建器使用自动HTML属性生成,通过它,可以在返回时检测按下了哪个按钮,并相应地采取行动(这些也可以设置为运行单独的进程)。

除了表单构建器之外,Drupal还将数据“删除”操作分割到单独的url /表单中,可能是出于最初文章中提到的原因。这需要某种搜索/列出步骤(呻吟另一种形式!但是用户友好的)作为一个初步。但这样做的好处是消除了“提交一切”。问题。包含数据的大表单用于预期目的,即数据创建/更新(甚至是“合并”操作)。

换句话说,解决这个问题的一种方法是将表单转换为两个,然后问题就消失了(HTML方法也可以通过POST进行纠正)。