Cloned jQuery button makes more clones than expected

68 views Asked by At

First Question on the overflow so forgive me in advance for any issues or if I missed a previous answer. I am a little new to jQuery so there may be issues with the way that I am coding. I am trying to build a dynamic form that will allow me to add text input fields as needed by a user. It works very well until I try to use a cloned button to add more fields.

I am using jquery-ui 1.11.2 and jquery 1.11.2

HTML Code:

<style>.hiddenForm{display:none};</style>
<form>
    <input id="numentries" class="numspinner" />
    <div class="repeatedForm hiddenForm">
        <input type="button" class="numbutton" value="Add Fields" />
        <div class="repeatedInnerForm hiddenForm">
            <input type="text" placeholder="Gimme information" />
            <input type="text" placeholder="Gimme more information" />
        </div>
    </div>
</form>

Javascript

$(".numspinner").spinner({min:1});
$(".numspinner").on("spin",function(event, ui){
var oldvalue = $(this).val();
var newvalue = ui.value;
var diff = newvalue - oldvalue;
if(newvalue >= 1){
    if(diff == 1){
        var newForm = $(".repeatedForm").clone(true);
        newForm.removeClass("repeatedForm");
        newForm.removeClass("hiddenForm");
        newForm.attr("id","innerForm"+newvalue);
        newForm.appendTo("form");
    }else if(diff == -1){
        $("#innerForm"+oldvalue).remove();
    }
}
});
$(".numbutton").button();
$(".numbutton").click(function(event){
    var buttonclicked = $(this);
    var newForm = $(".repeatedInnerForm").clone();
    newForm.removeClass("repeatedInnerForm");
    newForm.removeClass("hiddenForm");
    newForm.insertAfter(buttonclicked);
});

JSFiddle

I would like it to only display one set of text fields for each button press and associate the textfields with the button being pressed. I think it has to do with the initial clone of the button. Any input or constructive criticism on code style is appreciated.

3

There are 3 answers

1
Ted On

I'm not 100% sure this does what you hope, but I changed your code to add the number of fields based on the inputs value:

var fieldCount = 0;
var formCount = 0;

$(".numspinner").spinner({
    min:1,
    spin:function(event, ui){fieldCount = ui.value;}
});

$(".numbutton").button().click(function(event){
    for(var i = 0; i < fieldCount; i++){
        formCount++;
        var newForm = $(".repeatedInnerForm")
            .clone()
            .attr('id', 'form'+formCount)
            .removeClass("repeatedInnerForm")
            .insertAfter(this);
    }
});

For this HTML

<form>
    <input id="numentries" class="numspinner" />
    <input type="button" class="numbutton" value="Add Fields" />
    <div class="repeatedForm hiddenForm">
        <div class="repeatedInnerForm">
            <input type="text" placeholder="Gimme information" />
            <input type="text" placeholder="Gimme more information" />
        </div>
    </div>
</form>

See it in action here

2
Craig Harshbarger On

First off, jsFiddle only excepts external resources via https. cdnjs.com is a great place to get https links to js and css library files.

Your getting duplicate fields because you're cloning the inputs in two places. First it clones them on('spin') when diff == 1 and then again in your click function. I edited your code to clone the inputs once on('spin') and then to show the inputs on click. Here is a working fiddle

$(".numspinner").spinner({min:1});
$(".numspinner").on("spin",function(event, ui){
    var oldvalue = $(this).val();
    var newvalue = ui.value;
    var diff = newvalue - oldvalue;
    if(newvalue >= 1){
        if(diff == 1){
            var newForm = $(".repeatedForm").clone(true);
            newForm.removeClass("repeatedForm");
            newForm.removeClass("hiddenForm");
            newForm.attr("id","innerForm"+newvalue);
            newForm.appendTo("form");
        }else if(diff == -1){
            $("#innerForm"+oldvalue).remove();
        }
    }
});
$(".numbutton").button();
$(".numbutton").click(function(event){
    var form = $(this).siblings('.hiddenForm');
    form.removeClass('repeatedInnerForm');
    form.removeClass('hiddenForm');
});

It might also be a good idea to reverse the way your code works. Since the fields are not show until the user clicks the corresponding "Add More" button. It would be better to add the button on spin and then add the inputs on click. That way you're not creating elements when the user hasn't requested them.

0
Joshua Harrison On

Ok so your comments were extremely helpful @craig, @ted thank you very much.

working JSFiddle

HTML Code:

<form>
    <input id="numentries" class="numspinner" />
    <div id="hiddenOuterFormTemplate" class="hiddenForm">
        <p class="groupTitle"></p>
        <div class="buttonDiv">
            <input id="addButton" type="button" class="numbutton"  value="Add Fields" />
            <input id="removeButton" type="button" class="numbutton" value="Remove Fields" />
        </div>
    </div>
    <div id="hiddenInnerFormTemplate" class="hiddenForm">
        <input type="text" placeholder="Gimme information" />
        <input type="text" placeholder="Gimme more information" />
    </div>
</form>

I decided to make them separate elements to avoid the multi-copy problem and then use jQuery to position them as desired on the page.

Javascript Below

var totalgroups = 0;

$(".numspinner").spinner({min:1});
$(".numspinner").on("spin",function(event, ui){
    var oldvalue = $(this).val();
    var newvalue = ui.value;
    var diff = newvalue - oldvalue;
    if(newvalue >= 1){
        if(diff == 1){
            var newForm = $("#hiddenOuterFormTemplate").clone(true);
            newForm.removeClass("hiddenForm");
            totalgroups++;
            newForm.attr("id","outerForm"+newvalue);
            newForm.addClass("outerFormDiv");
            newForm.find(".groupTitle").text("Group "+newvalue + ":");
            newForm.appendTo("form");
        }else if(diff == -1){
            $("#outerForm"+oldvalue).remove();
            totalgroups--;
        }
    }
});
$(".numbutton").button();
$("#addButton").click(function(event){
    var buttonclicked = $(this);
    var newForm = $("#hiddenInnerFormTemplate").clone();
    newForm.removeClass("hiddenForm");
    newForm.addClass("dynamicTextfields")
    newForm.insertAfter(buttonclicked.parent(".buttonDiv"));
});
$("#removeButton").click(function(event){
      var buttonclicked = $(this);
      var buttonouterForm = buttonclicked.parents(".outerFormDiv");
      buttonouterForm.find(".dynamicTextfields:last").remove();         
});

Thanks again...