在选项卡式表单中间添加额外(嵌套)表单的最佳方法

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 的基础。就像&lt;ul&gt; 应该只包含&lt;li&gt; 元素一样,&lt;form&gt; 应该只有一个目的。使用 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>

重要的是您仍然可以在主表单中实现所需的流程。在您的主表单上,不要使用嵌套表单,而是使用&lt;fieldset&gt;。此字段集将充当 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 隐藏 &lt;fieldset&gt;,并使用 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 触发

HTML 选项卡式表单滑动

Xamarin 表单选项卡式页面选项卡正在初始化

为啥我更改的字符串没有出现在使用选项卡式表单 xamarin 的其他页面上

嵌套我的表单 - 带有单选按钮的 JavaScript 条件

带有来自模型的额外信息的 Django 渲染单选按钮选项