克隆/删除输入字段 - 保持元素ID唯一 [英] Cloning/Removing input fields - keeping element id unique

查看:75
本文介绍了克隆/删除输入字段 - 保持元素ID唯一的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在处理在表单中生成动态输入字段。我有一个复杂的例子,它使用复选框和选择框。它有两种类型的元素: main_items sub_items 。如上所述,我可以通过一个 clone 函数动态添加一些jquery输入字段,该函数复制一组具有唯一id属性的新输入字段。但是我对两件事情有很大的困难:首先,保持 id 对于每个复制的元素都是唯一的,特别是对于选择框。其次,我只能得到第一个下拉菜单才能用于第一个项目,但我还没有想出办法为其他项目做这个。 JSFIDDLE

I am currently working with generating dynamic input field inside a form. I have complex example that uses checkboxes and select boxes. It has two type of elements: main_items and sub_items. As mentioned, I can add input fields dynamically with some jquery through a clone function that replicates a new set of input fields with unique id attributes. But I am having great difficulty with two things: First, keeping the id’s unique for each element duplicated, specifically for the select boxes. Second, I have only been able to get the first drop down menu to work for the first item but I have not figured out a way to do it for the other items. JSFIDDLE

$('#btnAdd').click(function () {
    var num = $('.clonedSection').length;
    var newNum = num + 1;

    var newSection = $('#pq_entry_' + num).clone().attr('id', 'pq_entry_' + newNum);
    newSection.find('input[type="text"]').val('');
    newSection.find('select').val('');
    newSection.find('input[type="checkbox"]').prop('checked', false);
    //hide sub item
    newSection.find('.sub-item').hide();

    //change the input element selectors to use name
    newSection.find('input[name^="first_item_"]').attr('id', 'main_item_' + newNum).attr('name', 'main_item_' + newNum);
    newSection.find('input[name^="second_item_"]').attr('id', 'second_item_' + newNum).attr('name', 'second_item_' + newNum);
    newSection.find('input[name^="item_count_"]').attr('id', 'item_count_' + newNum).attr('name', 'item_count_' + newNum);
    newSection.find('input[name^="sub_item_"]').attr('id', 'sub_item_' + newNum).attr('name', 'sub_item_' + newNum);
    newSection.find('input[name^="other_item_"]').attr('id', 'other_item_' + newNum).attr('name', 'other_item_' + newNum);
    newSection.insertAfter('#pq_entry_' + num).last();

    $('#btnDel').click(function () {
        var num = $('.clonedSection').length; // how many "duplicatable" input fields we currently have
        $('#pq_entry_' + num).remove(); // remove the last element

        // enable the "add" button
        $('#btnAdd').prop('disabled', '');

        // if only one element remains, disable the "remove" button
        if (num - 1 == 1) $('#btnDel').prop('disabled', 'disabled');
    });
});


$('#btnDel').prop('disabled', 'disabled');

//Generate Dropdown
$('#item_count_1').change(function() {
    var option = $(this).val();
    showFields(option);
    return false;
});

function showFields(option){ 
    var content = '';
    for (var i = 1; i <= option; i++){
        content += '<div id="item_'+i+'"><label>Item # '+i+'</label><br /><label>Item Name:</label> <select id="item_name_'+i+'" name="item_name_'+i+'" class="course_list"><option value="" >--- Select ---</option><option value="apples" >apples</option><option value="banana" >banana</option><option value="mango" >mango</option></select></div>';  
    }
    $('#item_names_1').html(content);
}

HTML

<ul id="pq_entry_1" class="clonedSection">
  <li style="list-style-type: none;">
    <input id="first_item_1" class="main-item" name="main_item_1" type="checkbox"><label>First Item</label>
  </li>
  <li style="list-style-type: none;">
    <input id="second_item_1" class="main-item" name="main_item_1" type="checkbox"><label>Second Item</label>
  </li>
  <ul class="sub-item" style='display: none;'>
    <li style="list-style-type: none;">
      <label>
        How many items:
        <small>required</small>
      </label>
      <select id="item_count_1" name="item_count_1" class="medium" required>
        <option value="">---Select---</option>
        <option value="1">1</option>
        <option value="2">2</option>
      </select>
    </li>
    <li style="list-style-type: none;">
      <div id="item_name_1"></div>
    </li>
  </ul>
</ul>


推荐答案

那么,我们来谈谈如何构建基本的GUI应用程序。在我们继续之前,我希望你知道下面的代码可以用Knockout / Angular中的~20 LoC编写,但我选择不这样做,因为那不会真正教会任何人。

So, let's talk about how to build basic GUI applications. Before we proceed I'd like you to know the code below can be written in ~20 LoC in Knockout/Angular but I chose not to because that wouldn't really teach anyone anything.

所以,我们来谈谈GUI。

So, let's talk about GUI.


  • 演示文稿 - 这是您的HTML,CSS以及用户直接与之交互的内容。

  • 数据 - 这是您的实际数据和逻辑。

我们希望分开它们,以便它们可以独立行动。我们想要实际表示用户在JavaScript对象中看到的内容,以便它可以维护,可测试可读等等。有关详细信息,请参见分离问题

We want to separate them so that they can act independently. We want an actual representation of what the user sees in JavaScript object so it'll be maintainable, testable readable and so on and so on. See Separation of Concerns for more information.

那么,你的应用程序中每件东西都有什么?

So, what does each thing have in your application?


  • 第一项,无论是真还是假

  • 子项是真还是假,但从不如果第一项不成立,则为true。

  • 第二项,无论是真还是假。

  • A 商品数量这是一个数字


    • 这些商品中的每一项都是苹果,香蕉或芒果

    • A First Item, either true or false
    • A Sub Item either true or false, but never true if the First Item isn't true.
    • A Second Item which is either true or false.
    • A Number of Items which is a number
      • Each of these items is an apple, banana or mango

      最直观的是在那里开始

      The most intuitive thing is to start right there.

      // our item, like we've just described it :) 
      function Thing(){ //we use this as an object constructor.
          this.firstItem = false;
          this.subItem = false;
          this.secondItem = false;
          this.numItems = 0;
          this.items = []; // empty list of items
      }
      

      嗯,这是一件事,我们现在可以创建使用 new Thing()然后设置它们的属性,例如 thing.firstItem = true

      Well, that's a thing, we can now create them with new Thing() and then set their properties, for example thing.firstItem = true.

      但我们没有 a Thing 我们有东西。东西只是一个(有序的)一堆东西。有序集合通常由JavaScript中的数组表示,因此我们可以:

      But we don't have a Thing we have stuff. Stuff is just an (ordered) bunch of things. An ordered collection is commonly represented by an array in JavaScript, so we can have:

      var stuff = []; // our list
      var thing = new Thing(); // add a new item
      stuff.push(thing); // add the thing we just created to our list
      

      我们当然也可以将此信息传达给PHP提交。另一种方法是提交一个JSON对象并在PHP中读取它(这很好!),或者我们可以将其序列化为params (如果您对该问题中的方法有任何疑问 - 请告诉我)。

      We can of course also communicate this to PHP when submitting. One alternative is submitting a JSON object and reading that in PHP (this is nice!), alternatively we can serialize it as form params (if you have any trouble with the methods in that question - let me know).

      非常精明。到目前为止,您只有对象,您没有在任何地方指定它们的行为。我们有'数据'层,但我们还没有任何表示层。我们首先要删除所有ID并添加行为。

      Quite astute. So far you only have objects, you did not specify their behavior anywhere. We have our 'data' layer, but we don't have any presentation layer yet. We'll start by getting rid of all the IDs and add behavior in.

      克隆现有对象,我们希望有一个'cookie cutter'方式来创建新元素的外观。为此,我们将使用模板。让我们首先提取项目列表在HTML模板中的显示方式。基本上,给出你的html,它是这样的:

      Instead of cloning existing objects we'll want to have a 'cookie cutter' way to create the looks of new elements. For this we'll use a template. Let's start by extracting how your 'item list' looks into an HTML template. Basically, given your html it's something like:

      <script type='text/template' data-template='item'>
      
      <ul class="clonedSection">
        <li style="list-style-type: none;">
          <label><input class="main-item" type="checkbox" />First Item</label>
          <ul class="sub-item" style="display: none;">
            <li style="list-style-type: none;">
              <label><input type="checkbox" />Sub Item</label>
            </li>
          </ul>
        </li>
        <li style="list-style-type: none;">
          <label>
            <input class="main-item" type="checkbox" />Second Item</label>
          <ul class="sub-item" style='display: none;'>
            <li style="list-style-type: none;">
              How many items:
              <select class="medium" required>
                <option value="">---Select---</option>
                <option value="1">1</option>
                <option value="2">2</option>
              </select>
            </li>
            <li style="list-style-type: none;"><div></div></li>
          </ul>
        </li>
      </ul>
      </script>
      

      现在让我们创建一个'哑'方法,在屏幕上显示模板。

      Now let's create a 'dumb' method for showing the template on the screen.

      var template;
      function renderItem(){
          template = template || $("[data-template=item]").html();
          var el = $("<div></div>").html(template);
          return el; // a new element with the template
      } 
      

      [这是我们的第一个jsfiddle演示演示] ( http://jsfiddle.net/RLRtv/ ,只添加三项,没有行为到屏幕。阅读代码,看看你理解它并且不要害怕询问你不理解的部分:)

      [Here's our first jsfiddle presentation demo](http://jsfiddle.net/RLRtv/, that just adds three items, without behavior to the screen. Read the code, see that you understand it and don't be afraid to ask about bits you don't understand :)

      接下来,我们将添加一些行为,当我们创建一个项目时,我们将它耦合到 Thing 。因此,我们可以采用单向数据绑定方式(视图中的更改反映在模型中)。如果你感兴趣,我们可以稍后实现另一个绑定方向,但它不是原始问题的一部分,所以为了简洁起见,我们暂时跳过它。

      Next, we'll add some behavior in, when we create an item, we'll couple it to a Thing. So we can do one way data binding (where changes in the view reflect in the model). We can implement the other direction of binding later if you're interested but it's not a part of the original question so for brevity let's skip it for now.

      function addItem(){
          var thing = new Thing(); // get the data
          var el = renderItem(); // get the element
          el. // WHOOPS? How do I find the things, you removed all the IDs!?!?
      }
      

      那么,我们在哪里停留?我们需要将行为附加到我们的模板,但普通的HTML模板没有钩子,所以我们必须手动完成。让我们首先使用'数据绑定'属性更改我们的模板。

      So, where are we stuck? We need to append behavior to our template but normal HTML templates do not have a hook for that so we have to do it manually. Let us begin by altering our template with 'data binding' properties.

      <script type='text/template' data-template='item'>
      
      <ul class="clonedSection">
          <li style="list-style-type: none;">
              <label>
                  <input class="main-item" data-bind = 'firstItme' type="checkbox" />First Item</label>
              <ul class="sub-item" data-bind ='subItem' style="display: none;">
                  <li style="list-style-type: none;">
                      <label>
                          <input type="checkbox" />Sub Item</label>
                  </li>
              </ul>
          </li>
          <li style="list-style-type: none;">
              <label>
                  <input class="main-item" data-bind ='secondItem' type="checkbox" />Second Item</label>
              <ul class="sub-item" style='display: none;'>
                  <li style="list-style-type: none;">How many items:
                      <select class="medium" data-bind ='numItems' required>
                          <option value="">---Select---</option>
                          <option value="1">1</option>
                          <option value="2">2</option>
                      </select>
                  </li>
                  <li style="list-style-type: none;">
                      <div data-bind ='items'> 
      
                      </div>
                  </li>
              </ul>
          </li>
      </ul>
      </script>
      

      查看所有 data-bind 属性我们添加?让我们尝试选择那些。

      See all the data-bind attributes we added? Let's try selecting on those.

      function addItem() {
          var thing = new Thing(); // get the data
          var el = renderItem(); // get the element
          //wiring
          el.find("[data-bind=firstItem]").change(function(e){
             thing.firstItem = this.checked;
              if(thing.firstItem){//show second item
                  el.find("[data-bind=subItem]").show(); //could be made faster by caching selectors
              }else{
                  el.find("[data-bind=subItem]").hide();
              }
          });
          el.find("[data-bind=subItem] :checkbox").change(function(e){
              thing.subItem = this.checked;    
          });
          return {el:el,thing:thing}
      }
      

      In < a href =http://jsfiddle.net/bne97/ =nofollow noreferrer>这个小提琴我们已经为第一个项目和子项目添加了属性,他们已经更新了元素。

      In this fiddle we've added properties to the first item and sub item and they already update the elements.

      让我们继续为第二个属性做同样的事情。它几乎是相同的,直接绑定。另外,有几个库会自动为您执行此操作 - 例如 Knockout

      Let's proceed to do the same for the second attribute. It's pretty much more of the same, binding directly. On a side note there are several libraries that do this for you automatically - Knockout for example

      以下是另一个小提琴,其中包含所有绑定,此结论我们的表示层,我们的数据层及其绑定。

      Here is another fiddle with all the bindings set in, this concluded our presentation layer, our data layer and their binding.

      var template;
      
      function Thing() { //we use this as an object constructor.
          this.firstItem = false;
          this.subItem = false;
          this.secondItem = false;
          this.numItems = 0;
          this.items = []; // empty list of items
      }
      
      function renderItem() {
          template = template || $("[data-template=item]").html();
          var el = $("<div></div>").html(template);
          return el; // a new element with the template
      }
      
      function addItem() {
          var thing = new Thing(); // get the data
          var el = renderItem(); // get the element
          el.find("[data-bind=firstItem]").change(function (e) {
              thing.firstItem = this.checked;
              if (thing.firstItem) { //show second item
                  el.find("[data-bind=subItem]").show(); //could be made faster by caching selectors
              } else {
                  el.find("[data-bind=subItem]").hide();
              }
          });
          el.find("[data-bind=subItem] :checkbox").change(function (e) {
              thing.subItem = this.checked;
          });
          el.find("[data-bind=secondItem]").change(function (e) {
              thing.secondItem = this.checked;
              if (thing.secondItem) {
                  el.find("[data-bind=detailsView]").show();
              } else {
                  el.find("[data-bind=detailsView]").hide();
              }
          });
          var $selectItemTemplate = el.find("[data-bind=items]").html();
          el.find("[data-bind=items]").empty();
      
          el.find("[data-bind=numItems]").change(function (e) {
              thing.numItems = +this.value;
              console.log(thing.items);
              if (thing.items.length < thing.numItems) {
                  for (var i = thing.items.length; i < thing.numItems; i++) {
                      thing.items.push("initial"); // nothing yet
                  }
              }
              thing.items.length = thing.numItems;
              console.log(thing.items);
              el.find("[data-bind=items]").empty(); // remove old items, rebind
              thing.items.forEach(function(item,i){
      
                  var container = $("<div></div>").html($selectItemTemplate.replace("{number}",i+1));
                  var select = container.find("select");
                  select.change(function(e){                
                      thing.items[i] = this.value;
                  });
                  select.val(item);
                  el.find("[data-bind=items]").append(container);
      
              })
      
          });
          return {
              el: el,
              thing: thing
          }
      }
      
      for (var i = 0; i < 3; i++) {
          var item = addItem();
          window.item = item;
          $("body").append(item.el);
      }
      



      按钮



      有趣的是,现在我们完成了繁琐的部分,按钮是小菜一碟。

      The buttons

      The fun thing is, now that we're done with the tedious part, the buttons are a piece of cake.

      让我们添加添加按钮

       <input type='button' value='add' data-action='add' />
      

      和JavaScript:

      and JavaScript:

      var stuff = [];
      $("[data-action='add']").click(function(e){
           var item = addItem();
           $("body").append(item.el);
           stuff.push(item);
      });
      

      男孩,很容易

      好的,所以删除应该很难,对吧?

      Ok, so remove should be pretty hard, right?

      HTML:

      <input type='button' value='remove' data-action='remove' />
      

      JS:

      $("[data-action='remove']").click(function(e){
           var item = stuff.pop()
           item.el.remove();
      });
      

      好的,所以非常甜蜜。那么我们如何得到我们的数据呢?让我们创建一个按钮,显示屏幕上的所有项目?

      Ok, so that was pretty sweet. So how do we get our data? Let's create a button that shows all the items on the screen?

      <input type='button' value='show' data-action='alertData' />
      

      和JS

      $("[data-action='alertData']").click(function(e){
          var things = stuff.map(function(el){ return el.thing;});
          alert(JSON.stringify(things));
      });
      

      哇!我们在模型层中实际表示了我们的数据。我们可以随心所欲地做任何事情,这很可爱。

      Woah! We have an actual representation of our data in our model layer. We can do whatever we want with it, that's pretty sweet.

      如果我想将其作为表格提交怎么办? $。param 救援。

      What if I want to submit it as a form? $.param to the rescue.

      <input type='button' value='formData' data-action='asFormData' />
      

      和JS:

      $("[data-action='asFormData']").click(function(e){
          var things = stuff.map(function(el){ return el.thing;});
          alert($.param({data:things}));
      });
      

      虽然这种格式不是很好,但PHP(或任何其他流行技术)很乐意在服务器端阅读。

      And while this format is not very nice it's something PHP (or any other popular technology) will gladly read on the server side.


      • 从数据中单独表示

      • 如果你有JS逻辑 - 只有一个事实来源 - JavaScript对象

      • 考虑更多地阅读它,了解像KnockoutJS或AngularJS这样有趣的常见框架解决这个问题的冗长解决方案(以假设为代价)。

      • 了解有关UI架构的更多信息。 这是一个很好的(但对初学者很难)资源

      • 避免重复ID,它们很糟糕 - 当你在那里时不要在你的dom中存储数据。

      • 不要害怕提问 - 这就是你学习的方式。

      • 你可以在这里轻松摆脱jQuery。

      • Separate presentation from data
      • If you have JS logic - have a single source of truth - JavaScript objects
      • Consider reading about it more, learn about common frameworks like KnockoutJS or AngularJS which have interesting less verbose solutions to this problem (at the cost of assumptions).
      • Read more about UI architecture. This is a good (but hard for beginners) resource
      • Avoid duplicate IDs, they're bad - while you're there don't store data in your dom.
      • Don't be afraid to ask question - this is how you learn.
      • You can pretty easily get rid of jQuery here.

      这篇关于克隆/删除输入字段 - 保持元素ID唯一的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆