【问题标题】: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 查询响应来创建额外的表单字段。

标签: php jquery forms jquery-ui-tabs


【解决方案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 不显眼地添加所需的行为,通过:

    1. 隐藏第二个表单,#newperson
    2. 通过绑定到#newpersonProxy 中任何输入的keyup 事件以及按钮的单击事件来响应“提交”。对于 keyup 事件,使用 event.which 而不是 event.keyCode,因为 jQuery 对此进行了规范化。您需要 event.which == 13 作为回车键。
    3. 然后在调用 $("#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。
                猜你喜欢
                • 2017-05-26
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-03-16
                • 1970-01-01
                • 2017-07-14
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多