在选项卡式表单中间添加额外(嵌套)表单的最佳方法
Posted
技术标签:
【中文标题】在选项卡式表单中间添加额外(嵌套)表单的最佳方法【英文标题】:Best way to add an extra (nested) form in the middle of a tabbed form 【发布时间】:2011-05-31 08:54:00 【问题描述】:我有一个 Web 应用程序,主要由一个带有信息的大表单组成。表单被分成多个选项卡,以使其对用户更具可读性:
<form>
<div id="tabs">
<ul>
<li><a href="#tab1">Tab1</a></li>
<li><a href="#tab2">Tab2</a></li>
</ul>
<div id="tab1">A big table with a lot of input rows</div>
<div id="tab2">A big table with a lot of input rows</div>
</div>
</form>
表单是动态扩展的(额外的行被添加到表中)。 表单每 10 秒序列化一次并与服务器同步。
我现在想在其中一个选项卡上添加一个交互式表单:当用户在字段中输入名称时,此信息将发送到服务器并返回与该名称关联的 id。此 id 用作一些动态添加的表单字段的标识符。
这样一个页面的快速草图如下所示:
<form action="bigform.php">
<div id="tabs">
<ul>
<li><a href="#tab1">Tab1</a></li>
<li><a href="#tab2">Tab2</a></li>
</ul>
<div id="tab1">A big table with a lot of input rows</div>
<div id="tab2">
<div class="associatedinfo">
<p>Information for Joe</p>
<ul>
<li><input name="associated[26][]" /></li>
<li><input name="associated[26][]" /></li>
</ul>
</div>
<div class="associatedinfo">
<p>Information for Jill</p>
<ul>
<li><input name="associated[12][]" /></li>
<li><input name="associated[12][]" /></li>
</ul>
</div>
<div id="newperson">
<form action="newform.php">
<p>Add another person:</p>
<input name="extra" /><input type="submit" value="Add" />
</form>
</div>
</div>
</div>
</form>
上述方法不起作用:html 中不允许嵌套表单。但是,我确实需要在该选项卡上显示表单:它是该页面功能的一部分。我还想要一个单独表单的行为:当用户在表单字段中点击返回时,按下“添加”提交按钮并在部分表单上触发提交操作。
解决这个问题的最佳方法是什么?
【问题讨论】:
使用带有多个按钮的大表单。每个按钮都可以有不同的值,这就是您的后端代码如何判断实际按下哪个按钮来提交表单的方式。然后,您的后端代码可以根据按下的按钮执行不同的操作。或者你甚至可以让“子表单”上的按钮调用AJAX而不是提交表单,这样你就可以在不刷新页面的情况下查询服务器并获得响应。然后,您可以使用 javascript 来使用 AJAX 查询响应来创建额外的表单字段。 【参考方案1】:您可以做的一件事是将“添加”输入元素的类型设置为“提交”,而是将其设置为“按钮”并添加一个id
属性来注册点击处理程序:
<!-- ... -->
<div id="newperson">
<p>Add another person:</p>
<input id="extra_input" name="extra" /><input id="add_button" type="button" value="Add" />
</div>
<!-- ... -->
输入类型“按钮”的原因是页面上的结果按钮看起来就像一个提交按钮,但默认情况下它在单击时什么也不做,允许您自定义它的效果。此外,所有支持 HTML 3.2 或更高版本的浏览器都支持输入类型“按钮”including HTML 5。
在您的 Javascript 中的某个地方,您可能希望在用户单击“添加”按钮时调用该函数。假设它被称为addAnotherPerson
,您只需在点击处理程序中调用addAnotherPerson
:
// This is jQuery code, but all JS frameworks that I have seen have a similar feature to register a click handler on a DOM element referenced by ID.
$("#add_button").click(function (event)
addAnotherPerson();
$("#extra_input").val(""); // reset the value
);
您还需要在“额外”文本输入中添加一个 keydown 处理程序,以在用户按下 Enter 键时执行相同的操作:
$("#extra_input").keydown(function (event)
if (event.keyCode == 0xa || event.keyCode == 0xd)
addAnotherPerson();
$("#extra_input").val(""); // reset the value
);
请务必将属性autocomplete="off"
添加到“额外”文本输入中。
addAnotherPerson
函数可以发送一个异步 POST 请求,其中包含“额外”文本框中的值,然后在适当的情况下更新 DOM。
编辑:如果您还想支持禁用了 Javascript 的用户,那么通过为每个提交按钮添加 name
属性,并为每个提交按钮添加一个唯一的 name
属性值,您的服务器端应用程序将能够判断用户单击了哪个按钮。
以http://www.alanflavell.org.uk/www/trysub.html中的这段代码为例:
<table>
<tr><td align=left><label for="aquavit_button"><input id="aquavit_button" type="submit" name="aquavit" value="[O]"> Aquavit</label></td></tr>
<tr><td align=left><label for="beer_button"><input id="beer_button" type="submit" name="beer" value="[O]"> Beer</label></td></tr>
<tr><td align=left><label for="champagne_button"><input id="champagne_button" type="submit" name="champagne" value="[O]"> Champagne</label></td></tr>
<tr><td align=left><label for="water_button"><input id="water_button" type="submit" name="water" value="[O]"> Dihydrogen monoxide</label></td></tr>
</table>
如果用户单击“啤酒”旁边的“[O]”按钮,则浏览器会发送“啤酒”POST 变量,但不会发送“aquavit”、“香槟”或“水”POST 变量。试试http://www.alanflavell.org.uk/www/trysub.html。
这种技术的一个棘手问题是用户通过在文本输入中按 Enter 键来提交表单的情况。 Firefox 为表单中的第一个提交按钮(在本例中为“aquavit”)发送一个 POST 变量,而 Internet Explorer 不为任何提交按钮发送一个 POST 变量。您可以通过在表单的最开始添加一个隐藏的、无名的提交按钮来修补这种差异:
<form action="bigform.php"><input type="submit" style="display:none;" />
<!-- ... -->
EDIT2:如果您始终确保在提交大表单之前,“额外”文本输入中的值已被 Javascript 清除,那么您将能够判断用户是否有 Javascript disabled 并且他们在“extra”文本输入中按下 Enter 键(表明他们希望运行 add-a-person 操作),方法是检查“extra”POST 变量是否不为空服务器端。如果它不为空,那么您可以假设用户禁用了 Javascript,并且他们想要添加一个人。
添加此代码:
$("#bigform").submit(function (event)
$("#extra_input").val(""); // reset the value in the "extra" text input
return true;
);
【讨论】:
【参考方案2】:HTML 应该是语义化的,并且应该首先考虑 - 即它应该具有清晰的结构和含义,而无需了解 javascript 的基础。就像<ul>
应该只包含<li>
元素一样,<form>
应该只有一个目的。使用 javascript 通过检索动态数据来增强表单是可以的,但是如果使用额外的 HTTP POST(在服务器上修改数据的类型),那么这确实代表了第二个目的,因此应该使用第二种形式。第二种形式,具有自己的“动作”属性,表示此处正在发生不同的行为,不仅在概念上更合理,而且在必要时更易于分析、调试或扩展。
<form id="bigform" action="bigform.php">
<!-- Tabs here -->
</form>
<form id="newperson" action="newform.php">
<input name="extra">
<button type="submit" value="Add">
<!-- Note also the use of <button> instead of <input> -->
</form>
重要的是您仍然可以在主表单中实现所需的流程。在您的主表单上,不要使用嵌套表单,而是使用<fieldset>
。此字段集将充当 newperson 表单的代理。这里的输入可以有一个ID,但不应该有一个名字,因为它的值并不意味着要与主表单一起提交。正如其他答案中已经提到的,按钮应更改为“按钮”类型,而不是“提交”。
<form id="bigform" action="bigform.php">
<!-- Tabs here -->
<fieldset id="newpersonProxy">
<input id="extra">
<button type="button" value="Add">
</fieldset>
</form>
现在通过 Javascript 不显眼地添加所需的行为,通过:
-
隐藏第二个表单,
#newperson
。
通过绑定到#newpersonProxy
中任何输入的keyup 事件以及按钮的单击事件来响应“提交”。对于 keyup 事件,使用 event.which
而不是 event.keyCode
,因为 jQuery 对此进行了规范化。您需要 event.which == 13
作为回车键。
然后在调用 $("#newperson").submit()
之前,将代理输入中的值复制到实际表单中。
这实际上是将序列化和发送数据的责任委托给第二种形式,允许将任何类型的逻辑附加到第二种形式,并与第一种形式分离。
此外,由于首先考虑了 HTML 功能,一个简单的调整可以让表单在没有 Javascript 的情况下完美运行 - 只需使用 CSS 隐藏 <fieldset>
,并使用 Javascript 显示它。如果关闭 Javascript,选项卡将分开,嵌套的字段集将被隐藏,第二个表单将显示(并且功能齐全,只是不通过 AJAX)。
【讨论】:
【参考方案3】:应该这样做。
如果 JavaScript 被禁用,您将需要检测用户是否按下了“添加”submitButton
:
$_POST["submitButton"] == "Add"
<!-- other stuff goes here -->
<div id="newperson">
<p>Add another person:</p>
<input name="extra" id="extra" /><input name="submitButton" id="addPerson" type="submit" value="Add" />
</div>
</div>
</div>
</form>
<script>
var extra = $("#extra"),
addPerson = $("#addPerson").click(function(e)
// val would be the new person's name, e.g., "David"
var val = extra.val();
// you will want to give the user some AJAXy feedback here.
$.post(this.form.action, "extra": val, function(response)
// rest the extra div.
extra.val("");
/* 'response' could look like: */
// <div class="associatedinfo">
// <p>Information for David</p>
// <ul>
// <li><input name="associated[13][]" /></li>
// <li><input name="associated[13][]" /></li>
// </ul>
// </div>
$(response).insertBefore("#newperson");
// or 'response' could be json data, or whatever. I have a feeling
// you know enough about jQuery to handle the response-data. :-)
);
// prevent it from bubbling up.
e.preventDefault();
);
extra.keydown(function(e)
// submit the new person's name via ajax if enter is pressed.
if(e.keyCode === 13)
addPerson.click();
e.preventDefault();
);
</script>
【讨论】:
$("#extra")
仅在 DOM 准备好时有效。您需要将脚本包装在 $(function ()
和 );
或等效语法 $(document).ready(function() ... );
中。
@Daniel Trebbien。不,你没有。所有 A 级浏览器都可以很好地触发此代码。阅读 this 了解更多关于为什么 Closure Library 甚至没有 dom-ready 事件的信息。
有趣。您提供的链接表明,如果您将脚本放在 脚本需要准备好的 DOM 元素之后,则您的代码可以工作。但是,如果您将script
元素放在head
中(是的,我知道这很糟糕,但很常见)或之前的某个地方,那么它需要被包裹在$(function ()
和);
中,对吧?这种 DOM 准备绕过技术在 IE 6 中有效吗?
是的,应该。自从我不得不担心 IE6 以来已经有一段时间了...但我认为您只需要确保您之前没有在 IE6 中修改 DOM它“准备好了”。话虽如此,如果用户设法在 DOM 准备好之前输入名称、点击提交并返回 AJAX 请求,如果$(response).insertBefore("#newperson");
行没有用$();
包裹,我的回答可能会出现问题.【参考方案4】:
这并不像其他答案那样冗长,但是..
应该超级简单;)
彻底摆脱您的 FORM 标签 使用jQuery获取输入字段值 当用户单击看起来像提交按钮的提交“a”元素时,它只是发出一个 ajax 请求,捕获输入字段的值。这是一个example,它显示了我引用的格式的一点,但是它保留了表单标签,并且是我使用 scriptaculous 库时的一个较旧的示例。这是the JS,它展示了一些结构。
如果您 +1 我并让我知道您喜欢这个答案,我将为您开发一个功能示例;)
干杯! 柯克
【讨论】:
【参考方案5】:为什么不在一个 php 代码中嵌套表单对象并处理所有操作...
# have this form appended to the DOM via ajax/js when a link is clicked
<div id="newperson">
<p>Add another person:</p>
<input type="text" name="associated[new][]" />
</div>
如果是“新”,则在后端使用php逻辑创建关联:
foreach($_POST['associated'] as $obj)
if($obj == "new")
// create a new record
else
// do your other processing
【讨论】:
【参考方案6】:您可以将添加表单移到父表单之外并使用 CSS 将其隐藏,然后,正如另一位发帖者所说,将添加表单的提交按钮替换为具有正确 id 的按钮,最后,挂接到两个 onkeypress在输入上,然后单击按钮模拟实际添加表单的提交。
<form action="bigform.php">
....
<div id="newperson">
<p>Add another person:</p>
<input name="extra" /><button value="Add"/>
</div>
</div>
</div>
</form>
<div style='display:none;'>
<form action="newform.php" id='add_person_form'>
<input id='add_person_name' />
<input type="submit" id='add_person_submit' />
</form>
</div>
<script>
$('#newperson input').keyup(function(e)
if(e.keyCode == 13) submitNewPersonForm();
);
$('#newperson button').click(function(e)
submitNewPersonForm();
);
function submitNewPersonForm()
// copy the content from the placeholder form
$('#add_person_name').val($("#newperson input").val());
// submit this form
$("#add_person_form").submit();
</script>
据我所知,这应该可以在保留原始功能的同时满足您的所有需求。
注意:#add_person_form
表单上实际上不需要提交按钮,我只是将其保留在那里,以便您可以轻松查看我将表单“移动”到的位置。
【讨论】:
【参考方案7】:我看到处理类似场景的方式是为“内部”表单创建一个模式页面,因此不需要嵌套。我认为这种方法不会显着阻碍页面的流动。
否则,通过 AJAX 提交“内部”表单或整个表单的其他建议也可以使用。
【讨论】:
【参考方案8】:您不需要嵌套表单,您可以使用另一个表单(非嵌套)并使用绝对定位将其放置在页面上的任何位置..无论如何,所有这些动态控件的创建似乎都与用户输入的名称相关非常复杂..如果您可以分享您可能尝试解决的业务问题,我们可以提供更简单的解决方案。
【讨论】:
【参考方案9】:您可以使用 ajax 或在第一个表单之外制作其他表单,并使用绝对 DIV 层对其进行定位。
使用 javascript 很容易读取层位置。首先,您将制作一个具有预定义高度的占位符,然后您可以在占位符上方显示表单。
<form>
...
<div style='height: 300px;' class='placeholder'> ... </div>
</form>
<form>...<form>
显示 .placeholder 所在的第二种形式
【讨论】:
这将破坏表单的“流程”:我希望能够仅使用键盘来切换它。我可以通过设置大量的 tabindexes 来解决这个问题,但是动态内容使这成为了很多簿记。 如果你想要精确的表单行为,这是唯一的选择。否则,只需删除嵌套的 FORM 标记并使用 AJAX 执行所有操作。您可以使用简单的 ajax 函数在客户端轻松设置 tabindex。以上是关于在选项卡式表单中间添加额外(嵌套)表单的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
MS Access 选项卡式表单多个 OnCurrent 触发
为啥我更改的字符串没有出现在使用选项卡式表单 xamarin 的其他页面上