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


199

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

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

您可以说“仅将“删除”实现为链接而不是提交”。在许多级别中,这都是错误的,但是最重要的是,由于副作用操作(例如此处的“删除”)永远都不应该是GET请求。

所以我的问题(特别是那些说不需要表格嵌套的人)是您要做什么?是否有任何我缺少的优雅解决方案,或者底线确实是“要么需要JavaScript要么提交所有内容”?


17
这很有趣,但是XHTML确实根据有趣的注释anderwald.info/internet/nesting-form-tags-in-xhtml允许间接表格嵌套-(X)HTML不允许嵌套表格,例如“ form> form”,但允许“ form> fieldset>形式”,W3验证程序说这是有效的,但浏览器存在此类嵌套错误。
西


Answers:


53

我将按照您所描述的完全实现:将所有内容提交到服务器,然后执行简单的if / else检查单击的按钮。

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

这样,没有Javascript的人可以很好地使用表单,但是服务器负载可以抵消,因为拥有Javascript的人只提交所需的单个数据。让您仅专注于支持彼此中的一种确实会不必要地限制您的选择。

另外,如果您在公司防火墙或其他设备下工作,并且每个人都禁用了Javascript,则可能要执行两种形式并使用一些CSS魔术来使它看起来像删除按钮是第一种形式的一部分。


15
OP原始人说没有JavaScript
Jason

26
这种方法的问题在于它是非RESTful的。保存和删除对应于对资源的不同操作:“保存”-> POST / my_resource(创建新资源)“保存”-> PUT / my_resource(修改现有资源)“删除”-> DELETE / my_resource(销毁) REST而言,问题在于应该通过POST来创建新资源。当然,它绝不应删除现有资源。
user132447 '02

177

我知道这是一个老问题,但是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。本示例仅向表单外部的工具栏添加了一个clone方法,但它与嵌套在表单中的方法相同。

<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就可以声明式地完成所有这些工作。缺点是旧版浏览器不支持它们,因此您必须对旧版浏览器进行一些填充。


4
+ form=1- 但是知道浏览器支持的功能真是太好了-CanIUse并没有真正说出来。caniuse.com/#feat=forms
AKX

1
好的,这是优秀的,但今天的功能不支持IE仍然是不够格的“生产”使用
JAKO

3
<input>声明按钮是不好的做法。该<button>元素被引入15年前为了这个目的。真是人
尼古拉斯·香克斯

27
您的观点不必要地引起争议,与OP的问题无关。OP询问了不使用JavaScript的表单嵌套限制,答案提供了解决方案。正如您所说,尽管按钮<input><button>按钮通常更适合工具栏,但使用还是元素无关紧要。顺便说一句,所有浏览器都不支持按钮元素已有15年了。
shovavnik 2012年

6
@AKX尽管这是一个古老的声明,但是您可以在这里查看对此功能的支持:html5test.com/compare/feature/form-association-form.html。IE被注销,大多数其他桌面浏览器似乎可以忍受。
2013年

16

您可以在页面上并排放置表单,但不嵌套吗?然后使用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>

5
是的,感觉太黑了。此外,在一个非常大的页面(想象中的选项卡和子选项卡以及在多个作用域级别中嵌套提交按钮)中,几乎不可能将元素的屏幕位置与它们在文档中的位置完全分开。
gCoder

3
好吧,它总是在您想做什么和可以做什么之间取得平衡。看起来是这些选项-是在服务器上处理的一种表单,还是难以维护的多种表单-明智地选择:)
Jason,2009年

3
是的,不幸的是,由于存在html中的所有这些限制,我将坚持所支持的内容并将整个表单发送到服务器,然后为支持它的客户端毫不费力地使用javascript来拦截表单提交并仅发送真正需要的内容。那是最好的妥协。
gCoder

13

HTML5具有“表单所有者”的思想-输入元素的“表单”属性。它允许模拟嵌套形式,并将解决问题。


1
有什么值得参考的参考吗?
dakab

请参阅w3.org/TR/html5/forms.html#form-owner “与表单相关的元素默认情况下与其最接近的祖先form元素相关联,但是如果可以重新关联,则可以指定要覆盖的form属性这个。”
Nishi

11

有点旧的话题,但是这个话题可能对某人有用:

如上所述,您可以使用虚拟表格。我前段时间不得不克服这个问题。起初,我完全忘记了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为空。内部形式似乎在任何地方都可以正常工作。

还没有测试这里描述的另一个建议-围绕嵌套表单的字段集。

编辑:框架集不起作用!我只是简单地在其他表单之后添加了Main表单(不再有嵌套表单),并使用jQuery的“克隆”在按钮单击时复制了表单中的输入。在每个克隆的输入中添加了.hide(),以保持布局不变,现在它就像一个吊饰一样工作。


这对我来说非常有效。我正在一个asp.net页面上工作,该页面具有全封闭形式。我有一个内部嵌套表单可用于jQuery Validation插件(github.com/jzaefferer/jquery-validation),尽管它在FireFox和IE中运行良好,但在Chrome中失败,出现“ TypeError:无法调用方法的'element'未定义”。事实证明validate()初始化调用失败了,因为它找不到内部形式,因为Chrome从使用jQuery.html()添加的html块中将其删除了。首先添加一个小的虚拟表单会导致Chrome离开真实的表单。
约翰-没有数字

这似乎不再起作用。Firefox删除了第二个<form>标记,然后使用第一个</ form>标记终止了我的表单。这在页面底部留下了一个无效的</ form>标记,并且表单无法正确提交。:(
Tarquin

对我非常有用。我正在将包含表单的Web小部件添加到asp.net页面中。WebForm感到惊讶的是,它总是生成一个将HTML正文中的所有内容都包含在内的表单。封闭的表单使Web窗口小部件头疼。虚拟表格似乎是这种情况下的快速有效解决方案。到目前为止,它在Chrome和Safari中都能完美运行,尚未测试Firefox。感谢您的分享,为我节省了很多时间!
JM Yang

4

我认为杰森是对的。如果您的“删除”操作是最小的操作,则使它本身成为一种形式,并将其与其他按钮对齐,以使界面看起来像一个统一的形式,即使不是。

或者,当然,重新设计您的界面,让人们完全删除其他地方,根本不需要他们看到enormo表单。


2

我不用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 =“保存” value =“保存” />
</ form>

<form name =“ delform” action =“ the_action_url” method =“ post”>
   <input type =“ hidden” name =“ task” value =“删除” />
   <input type =“ hidden” name =“ post_id” value =“ 5” />
   <input type =“ submit” name =“删除” value =“删除” />
</ form>

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


1

我仍然对此讨论感兴趣。原始帖子的后面是OP似乎要共享的“要求”,即具有向后兼容性的表单。作为一个在撰写本文时有时必须支持IE6(以及未来几年)的人,我对此深有体会。

在不推动框架的情况下(所有组织都希望在兼容性/稳健性上放心,并且我不以讨论为框架的理由),Drupal解决此问题的方法很有趣。Drupal也直接相关,因为该框架具有长期的政策“它应该在没有Javascript的情况下也可以工作(仅在需要时)”,即OP的问题。

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

除了表单构建器之外,Drupal还将数据“删除”操作拆分为单独的url /表单,这可能是由于原始帖子中提到的原因。首先,这需要某种搜索/列出步骤(step 另一种形式!,但用户友好)。但这具有消除“提交所有内容”问题的优点。带有数据的大表格用于其预期目的,即数据创建/更新(甚至是“合并”操作)。

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


0

iframe用作嵌套形式。如果他们需要共享字段,那么...它并不是真正的嵌套。


1
使用iframe是一个好主意,在大多数情况下,您需要使用javascript来调整它的大小,这很可惜(因为您不知道用户的文本大小,因此按钮的大小)。
Nicholas Shanks

@Nicholas,如何使用Javascript调整大小?iframed HTML不能与iframe对话,反之亦然,除非它们在同一个域中。
Dan Rosenstark 2011年

@Nicholas,谢谢,只是想确保他们必须在同一个域中
Dan Rosenstark 2011年

0

我的解决方案是让按钮调用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

如果您真的不想使用多种形式(如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,这将使一半的页面无法使用。


1
除非我忽略了某些内容,否则这并不比链接到delete动作更好:最终结果将是对delete动作的GET请求(错误)。实际上,这有点麻烦,因为它需要JavaScript才能完成普通的旧链接标记可以完成的工作。
凯尔·福克斯

-1

为了回应Yar在对自己的答案的评论中发布的问题,我提供了一些JavaScript,这些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>

-1

我只是想出了一种使用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>

-1

好吧,如果您提交表单,浏览器还会发送输入的提交名称和值。所以你能做的是

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

因此在服务器端,您只需查找以宽度字符串“ action:”开头的参数,其余部分会告诉您采取什么操作

因此,当您点击“保存”浏览器按钮时,您会收到类似foo = asd&action:save = Save的信息


-1

我通过包括一个复选框来解决该问题,具体取决于该人想要做什么。然后使用1按钮提交整个表格。


2
欢迎使用Stack Overflow。最好描述一下这是如何解决问题的,而不是仅仅说您做了什么。有关在堆栈溢出上创建出色答案的提示,请参见“ 如何回答 ”。谢谢!
澳洲航空94重型

-2

或者,您可以动态分配表单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>

为什么?我在认真地问,因为没有简单的方法来获得单击哪个按钮的意思,您是说应该使用jQuery的click()方法来设置按钮的onclick处理程序吗?还是在提交表单之前处理click事件是问题所在?
扎克·莫里斯 Zack Morris)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.