Javascript 克隆/删除输入字段-保持元素id唯一

Javascript 克隆/删除输入字段-保持元素id唯一,javascript,jquery,Javascript,Jquery,我目前正在表单中生成动态输入字段。我有一个使用复选框和选择框的复杂示例。它有两种类型的元素:主项和子项。如前所述,我可以通过clone函数使用一些jquery动态添加输入字段,该函数复制一组具有唯一id属性的新输入字段。但是我在两件事上遇到了很大的困难:第一,保持id对于每个重复的元素的唯一性,特别是对于选择框。第二,我只能让第一个下拉菜单为第一个项目工作,但我还没有找到一种方法来为其他项目工作 $('#btnAdd')。单击(函数(){ var num=$('.clonedSection').

我目前正在表单中生成动态输入字段。我有一个使用复选框和选择框的复杂示例。它有两种类型的元素:
主项
子项
。如前所述,我可以通过
clone
函数使用一些jquery动态添加输入字段,该函数复制一组具有唯一id属性的新输入字段。但是我在两件事上遇到了很大的困难:第一,保持
id
对于每个重复的元素的唯一性,特别是对于选择框。第二,我只能让第一个下拉菜单为第一个项目工作,但我还没有找到一种方法来为其他项目工作

$('#btnAdd')。单击(函数(){
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);
//隐藏子项
newSection.find('.sub-item').hide();
//将输入元素选择器更改为使用名称
newSection.find('input[name^=“first_item_uu']).attr('id','main_item_'+newNum).attr('name','main_item_'+newNum);
newSection.find('input[name^=“second\u item\uuuu']).attr('id','second\u item\u'+newNum).attr('name','second\u item\u'+newNum);
newSection.find('input[name^=“item\u count\uuu]”).attr('id','item\u count\uuuu'+newNum).attr('name','item\u count\uuuu'+newNum);
newSection.find('input[name^=“sub_item_u”]').attr('id','sub_item_u'+newNum).attr('name','sub_item_u'+newNum);
newSection.find('input[name^=“other_item_uu]”).attr('id','other_item_uu'+newNum).attr('name','other_item_uu'+newNum);
newSection.insertAfter('#pq_entry_'+num).last();
$('#btnDel')。单击(函数(){
var num=$('.clonedSection').length;//当前有多少个“可复制”输入字段
$('#pq_entry.'+num).remove();//删除最后一个元素
//启用“添加”按钮
$('btnAdd').prop('disabled','');
//如果只剩下一个元素,请禁用“删除”按钮
如果(num-1==1)$('btnDel').prop('disabled','disabled');
});
});
$('btnDel').prop('disabled','disabled');
//生成下拉列表
$('#项目_计数_1')。更改(函数(){
var option=$(this.val();
展示区(可选);
返回false;
});
函数显示字段(选项){
var内容=“”;
对于(var i=1;i我的方法是:

首先,正确使用

因此,您可以通过元素的
数据id
属性来查找元素:

var myFirstSection = $("ul.pq_entry[data-id=1]");
通过这样做,在许多元素中根本不需要设置
id
属性,因为您可以简单地使用
class
并通过遍历DOM找到单个项。 例如,
main_项
变为:

 <input class="main-item" name="main_item[]" type="checkbox">
克隆节时,可以动态分配
data xxx
属性,如中所示:

var myNewId = myOldId + 1;
$clonedSection.data("id", myNewId);

然后,我将使用像
main\u item[]
这样的名称数组,因此您不需要在名称中手动指定id,但必须将此方法限制为仅在克隆部分中出现一次的元素

名称数组意味着,当您从表单、服务器端(例如,通过在PHP中使用$u POST)检索值时,您将按照值在表单中的显示顺序获得一个值数组。就像任何语言中的常规数组一样,您可以访问以下部分中的项(例如,在PHP中):


尝试分解代码以实现更好的管理

对于上述情况

HTML

将可恢复的html块隐藏在模板中:

<div class="form-template"> <!-- will pull form section template from here -->
    <ul data-custom-attributes="" data-id="formSectionIdPrefix" class="form-section">
        <li>
            <input data-custom-attributes="" data-id="firstCheckBoxIdPrefix" data-name="firstCheckBoxNamePrefix" class="main-item checkbox1" type="checkbox" />
            <label>First Item</label>

            <ul class="sub-item" style="display:none;">
                <li>
                    <input type="checkbox" />
                    <label>Sub Item</label>
                </li>
                <li>
                    <input class="main-item" data-id="checkBoxSubItem2IdPrefix" data-name="checkBoxSubItem2NamePrefix" type="checkbox" />
                    <label>Second Item</label>

                    <ul class="sub-item" style="display:none;">
                        <li>
                            <label>How many items:</label>
                            <select data-custom-attributes="" data-id="selectItem1IdPrefix" data-name="selectItem1IdPrefix" 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 data-custom-attributes="" class="dependant-select" data-id="selectItem2IdPrefix"></div>
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</div>

<div class="select-template hidden"> <!-- will pull dependant select template -->
    <select class="course_list" data-id="dependantSelectIdPrefix">
        <option value="">-- select --</option>
        <option value="apple">apples</option>
        <option value="apple">bananas</option>
    </select>
</div>

<div class="form-area"> <!-- main area to append form sections to -->
</div>

<div class="form-area-controls"> <!-- form controls -->
    <input type='button'  class="button tiny radius" id='btnAdd' value='Add Another' />
    <input type='button'   class="button tiny radius alert" id='btnDel' value='Delete Last' />
</div>
JS

从配置对象开始,轻松管理属性

var config = {};

config.formSectionIdPrefix = "pq_entry_";

config.firstCheckBoxIdPrefix = "first_item_";
config.firstCheckBoxNamePrefix = "main_item_";

config.checkBoxSubItem1IdPrefix = "sub_item_";
config.checkBoxSubItem1NamePrefix = "sub_item_";

config.checkBoxSubItem2IdPrefix = "second_item_";
config.checkBoxSubItem2NamePrefix = "main_item_";

config.selectItem1IdPrefix = "item_count_";
config.selectItem2IdPrefix = "item_names_";
config.dependantSelectIdPrefix = "item_";
缓存对FormSectionTemplate的引用,选择DropDownTemplate和FormArea

var $formTemplate = $(".form-template");
var $selectTemplate = $(".select-template");
var $formArea = $(".form-area");
可能还有一个索引变量来跟踪Id增量

var index = 0;
有一个助手方法
getFormTemplate
,该方法执行以下操作:

表格组

将事件附加到克隆的表单节

增加克隆节的ID(进一步介绍)

将事件附加到克隆的表单节
attachEvents

function attachEvents( $formSection ) {
    var $mainCheckBoxes = $formSection.find( ".main-item" );
    var $selectBox = $formSection.find( ".medium" );
    var $dependantSelectSection = $formSection.find( ".dependant-select" );

    $mainCheckBoxes.on("click", function() {
        var $this = $( this );
        var $subItem = $this.siblings(".sub-item");
        if ( $this.is(":checked") ) {
            $subItem.show();
        } else {
            $subItem.hide();   
        }
    });

    $selectBox.on("change", function() {
        var option = $(this).val();

        var $dependantSelect = getSelectField( option );

        $dependantSelectSection.children().remove();
        $dependantSelectSection.append( $dependantSelect );
    });

    return $formSection;
}
正在增加克隆表单节的ID

嗯,有很多方法(这在很大程度上取决于你的咖啡因含量)

在下面的部分中,我们将查找使用
数据自定义属性标记的所有元素

遍历所有这些元素,在
config
部分中找到我们应该查找的id和名称键,然后将这些值赋给
索引
递增项后面的值

function incrementAttributes( $formSection ) {
    index = index + 1;
    var $customAttributeElements = $formSection.find("[data-custom-attributes]");

    $customAttributeElements.each( function() {
        var $this = $(this);

        var idNamePrefix = $this.attr( "data-id" );
        var namePrefix = $this.attr( "data-name" );

        var idName = config[idNamePrefix] + index;
        var name = config[namePrefix] + index;

        $this.attr( "id", idName );
        $this.attr( "name", name );
    });

    return $formSection;
}
获取依赖项选择字段(由选择下拉列表上的
onchange
事件触发)

它只接收来自父选择框的值,并使用
config
对象的前缀将其分配给克隆的选择框的ID等

function getSelectField( indexValue ) {
    var $selectItem = $selectTemplate.find("select").clone();

    var selectElementIdPrefix = $selectItem.attr("data-id");
    var selectElementId = config[selectElementIdPrefix] + indexValue;

    $selectItem.attr( "id", selectElementId );

    return $selectItem;
}
把一切放在一起

$("#btnAdd").on("click", function(e) {
     e.preventDefault();

    var $formSection = getFormTemplate();
    $formArea.append($formSection);

});

$("#btnDel").on("click", function(e) {
    e.preventDefault();
    $formArea.children().last().remove();

    if ( index > 0 ) {
        index = index - 1;    
    }
});
事件中唯一需要提及的是
#btnDel
递减索引,以确保下一个表单节插入附加了正确的ID

JS小提琴:

编辑

刚刚注意到上面的提琴中有一些HTML标记不匹配(已修复)

下拉选择应该根据选择添加一个或多个子下拉列表

这可以通过将
$selectBox
上的
change
事件更改为以下内容来实现:

$selectBox.on("change", function() {
    var option = $(this).val();

    var optionInt = parseInt( option );

    $dependantSelectSection.children().remove();

    for ( var i = 0; i < optionInt; i++ ) {
        var $dependantSelect = getSelectField( option );
        $dependantSelectSection.append( $dependantSelect );     
    }

});
$selectBox.on(“更改”,函数(){
var option=$(this.val();
var optionInt=parseInt(选项);
$dependent selectsection.children().remove();
对于(变量i=0;i
更新小提琴
var config = {};

config.formSectionIdPrefix = "pq_entry_";

config.firstCheckBoxIdPrefix = "first_item_";
config.firstCheckBoxNamePrefix = "main_item_";

config.checkBoxSubItem1IdPrefix = "sub_item_";
config.checkBoxSubItem1NamePrefix = "sub_item_";

config.checkBoxSubItem2IdPrefix = "second_item_";
config.checkBoxSubItem2NamePrefix = "main_item_";

config.selectItem1IdPrefix = "item_count_";
config.selectItem2IdPrefix = "item_names_";
config.dependantSelectIdPrefix = "item_";
var $formTemplate = $(".form-template");
var $selectTemplate = $(".select-template");
var $formArea = $(".form-area");
var index = 0;
 function getFormTemplate() {
    var $newTemplate = $formTemplate.children().clone(true);

    var $formSectionWithEvents = attachEvents( $newTemplate );

    var $formSectionWithUpdatedAttributes = incrementAttributes( $formSectionWithEvents );

    return $formSectionWithUpdatedAttributes;
}
function attachEvents( $formSection ) {
    var $mainCheckBoxes = $formSection.find( ".main-item" );
    var $selectBox = $formSection.find( ".medium" );
    var $dependantSelectSection = $formSection.find( ".dependant-select" );

    $mainCheckBoxes.on("click", function() {
        var $this = $( this );
        var $subItem = $this.siblings(".sub-item");
        if ( $this.is(":checked") ) {
            $subItem.show();
        } else {
            $subItem.hide();   
        }
    });

    $selectBox.on("change", function() {
        var option = $(this).val();

        var $dependantSelect = getSelectField( option );

        $dependantSelectSection.children().remove();
        $dependantSelectSection.append( $dependantSelect );
    });

    return $formSection;
}
function incrementAttributes( $formSection ) {
    index = index + 1;
    var $customAttributeElements = $formSection.find("[data-custom-attributes]");

    $customAttributeElements.each( function() {
        var $this = $(this);

        var idNamePrefix = $this.attr( "data-id" );
        var namePrefix = $this.attr( "data-name" );

        var idName = config[idNamePrefix] + index;
        var name = config[namePrefix] + index;

        $this.attr( "id", idName );
        $this.attr( "name", name );
    });

    return $formSection;
}
function getSelectField( indexValue ) {
    var $selectItem = $selectTemplate.find("select").clone();

    var selectElementIdPrefix = $selectItem.attr("data-id");
    var selectElementId = config[selectElementIdPrefix] + indexValue;

    $selectItem.attr( "id", selectElementId );

    return $selectItem;
}
$("#btnAdd").on("click", function(e) {
     e.preventDefault();

    var $formSection = getFormTemplate();
    $formArea.append($formSection);

});

$("#btnDel").on("click", function(e) {
    e.preventDefault();
    $formArea.children().last().remove();

    if ( index > 0 ) {
        index = index - 1;    
    }
});
$selectBox.on("change", function() {
    var option = $(this).val();

    var optionInt = parseInt( option );

    $dependantSelectSection.children().remove();

    for ( var i = 0; i < optionInt; i++ ) {
        var $dependantSelect = getSelectField( option );
        $dependantSelectSection.append( $dependantSelect );     
    }

});
$selectBox.on("change", function() {
    var option = $(this).val();

    var optionInt = parseInt( option );

    $dependantSelectSection.children().remove();

    for ( var i = 1; i <= optionInt; i++ ) {
        var $dependantSelect = getSelectField( option );
        $dependantSelectSection.append( "item" + i );
        $dependantSelectSection.append( $dependantSelect );     
    }

});
// 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
}
var stuff = []; // our list
var thing = new Thing(); // add a new item
stuff.push(thing); // add the thing we just created to our list
<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>
var template;
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. // WHOOPS? How do I find the things, you removed all the IDs!?!?
}
<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>
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}
}
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);
}
 <input type='button' value='add' data-action='add' />
var stuff = [];
$("[data-action='add']").click(function(e){
     var item = addItem();
     $("body").append(item.el);
     stuff.push(item);
});
<input type='button' value='remove' data-action='remove' />
$("[data-action='remove']").click(function(e){
     var item = stuff.pop()
     item.el.remove();
});
<input type='button' value='show' data-action='alertData' />
$("[data-action='alertData']").click(function(e){
    var things = stuff.map(function(el){ return el.thing;});
    alert(JSON.stringify(things));
});
<input type='button' value='formData' data-action='asFormData' />
$("[data-action='asFormData']").click(function(e){
    var things = stuff.map(function(el){ return el.thing;});
    alert($.param({data:things}));
});