1

Similar to this question, I have a form and want to use clone() and remove() to dynamically change the fields visible to the user. The user has two controls: + and - to add or remove rows, respectively.

My problem is slightly different, however, because I want to enable cloning for each of the nested rows inside a form, independent of one another. The usage is to allow a user to specify one or more time intervals (e.g. 9AM to 5PM on each day). In other words, a user should be able to add as many time intervals as they want, but clicking the + or - under Monday would only affect the row of fields inside Monday - not affect other days.

Screenshots

  1. Form - Initial State
  2. Form - How it Should Look (when a user adds a row under each day)

I was able to get it where clicking the + or - duplicates the entire row (including the checkbox) - which is not what I want: https://jsfiddle.net/9jpraney/

I know the From and To fields need to be inside a parent div and this parent div is the one that should get cloned. However, when I make this change, it doesn't clone at all.

Here's my latest attempt: https://jsfiddle.net/znxj5Lrs/

var regex = /^(.+?)(\d+)$/i;
var cloneIndex = $(".time-interval").length;

function clone(){
    $(this).parents(".time-interval").clone()
        .appendTo("body")
        .attr("id", "time-interval" +  cloneIndex)
        .find("*")
        .each(function() {
            var id = this.id || "";
            var match = id.match(regex) || [];
            if (match.length == 3) {
                this.id = match[1] + (cloneIndex);
            }
        })
        .on('click', '.add-interval', clone)
        .on('click', '.remove-interval', remove);
    cloneIndex++;
}
function remove(){
    $(this).parents(".time-interval").remove();
}
$(".add-interval").on("click", clone);

$(".remove-interval").on("click", remove);
.row {
  background: #f8f9fa;
  margin-top: 20px;
}

.col {
  border: solid 1px #6c757d;
  padding: 10px;
}

.btn-alt-success {
    color: #5c852c;
    background-color: #ebf5df;
    border-color: #ebf5df;
}

.btn-alt-danger {
    color: #af1310;
    background-color: #fae9e8;
    border-color: #fae9e8;
}

.actions > a {
    cursor: pointer;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="form-group row">
    <label class="col-3">Days <span class="text-danger">*</span></label>
    <label class="col-9">Times</label>
</div>
<div class="form-group row">
    <!--<label class="col-lg-2 col-form-label mt-10">Step</label>-->

    <div class="col-lg-3">
        <div class="custom-control custom-checkbox mb-20">
            <input class="custom-control-input" type="checkbox" id="monday" name="days[]" value="monday">
            <label class="custom-control-label" for="monday">Monday</label>
        </div>
    </div>
    <div class="col-lg-5">
        <div class="row time-interval">
            <div class="col-lg-5">
                <select class="form-control form-control-lg" name="from_hours[monday][]">

                    <option value="0">12 AM</option>

                    <option value="1"> 1 AM</option>

                    <option value="2"> 2 AM</option>

                    <option value="3"> 3 AM</option>

                    <option value="4"> 4 AM</option>

                    <option value="5"> 5 AM</option>

                    <option value="6"> 6 AM</option>

                    <option value="7"> 7 AM</option>

                    <option value="8"> 8 AM</option>

                    <option value="9" selected> 9 AM</option>

                    <option value="10">10 AM</option>

                    <option value="11">11 AM</option>

                    <option value="12">12 PM</option>

                    <option value="13"> 1 PM</option>

                    <option value="14"> 2 PM</option>

                    <option value="15"> 3 PM</option>

                    <option value="16"> 4 PM</option>

                    <option value="17"> 5 PM</option>

                    <option value="18"> 6 PM</option>

                    <option value="19"> 7 PM</option>

                    <option value="20"> 8 PM</option>

                    <option value="21"> 9 PM</option>

                    <option value="22">10 PM</option>

                    <option value="23">11 PM</option>

                </select>
            </div>
            <div class="col-lg-2">
                <span class="pl-15">to</span>
            </div>
            <div class="col-lg-5">
                <select class="form-control form-control-lg mb-30" name="to_hours[monday][]">

                    <option value="0">12 AM</option>

                    <option value="1"> 1 AM</option>

                    <option value="2"> 2 AM</option>

                    <option value="3"> 3 AM</option>

                    <option value="4"> 4 AM</option>

                    <option value="5"> 5 AM</option>

                    <option value="6"> 6 AM</option>

                    <option value="7"> 7 AM</option>

                    <option value="8"> 8 AM</option>

                    <option value="9"> 9 AM</option>

                    <option value="10">10 AM</option>

                    <option value="11">11 AM</option>

                    <option value="12">12 PM</option>

                    <option value="13"> 1 PM</option>

                    <option value="14"> 2 PM</option>

                    <option value="15"> 3 PM</option>

                    <option value="16"> 4 PM</option>

                    <option value="17" selected> 5 PM</option>

                    <option value="18"> 6 PM</option>

                    <option value="19"> 7 PM</option>

                    <option value="20"> 8 PM</option>

                    <option value="21"> 9 PM</option>

                    <option value="22">10 PM</option>

                    <option value="23">11 PM</option>

                </select>
            </div>
        </div>
    </div>
    <div class="col-lg-4 actions">
        <a class="btn btn-alt-success add-interval">+</a>
        <a class="btn btn-alt-danger remove-interval">-</a>
    </div>

    <div class="col-lg-3">
        <div class="custom-control custom-checkbox mb-20">
            <input class="custom-control-input" type="checkbox" id="tuesday" name="days[]" value="tuesday">
            <label class="custom-control-label" for="tuesday">Tuesday</label>
        </div>
    </div>
    <div class="col-lg-5">
        <div class="row time-interval">
            <div class="col-lg-5">
                <select class="form-control form-control-lg" name="from_hours[tuesday][]">

                    <option value="0">12 AM</option>

                    <option value="1"> 1 AM</option>

                    <option value="2"> 2 AM</option>

                    <option value="3"> 3 AM</option>

                    <option value="4"> 4 AM</option>

                    <option value="5"> 5 AM</option>

                    <option value="6"> 6 AM</option>

                    <option value="7"> 7 AM</option>

                    <option value="8"> 8 AM</option>

                    <option value="9" selected> 9 AM</option>

                    <option value="10">10 AM</option>

                    <option value="11">11 AM</option>

                    <option value="12">12 PM</option>

                    <option value="13"> 1 PM</option>

                    <option value="14"> 2 PM</option>

                    <option value="15"> 3 PM</option>

                    <option value="16"> 4 PM</option>

                    <option value="17"> 5 PM</option>

                    <option value="18"> 6 PM</option>

                    <option value="19"> 7 PM</option>

                    <option value="20"> 8 PM</option>

                    <option value="21"> 9 PM</option>

                    <option value="22">10 PM</option>

                    <option value="23">11 PM</option>

                </select>
            </div>
            <div class="col-lg-2">
                <span class="pl-15">to</span>
            </div>
            <div class="col-lg-5">
                <select class="form-control form-control-lg mb-30" name="to_hours[tuesday][]">

                    <option value="0">12 AM</option>

                    <option value="1"> 1 AM</option>

                    <option value="2"> 2 AM</option>

                    <option value="3"> 3 AM</option>

                    <option value="4"> 4 AM</option>

                    <option value="5"> 5 AM</option>

                    <option value="6"> 6 AM</option>

                    <option value="7"> 7 AM</option>

                    <option value="8"> 8 AM</option>

                    <option value="9"> 9 AM</option>

                    <option value="10">10 AM</option>

                    <option value="11">11 AM</option>

                    <option value="12">12 PM</option>

                    <option value="13"> 1 PM</option>

                    <option value="14"> 2 PM</option>

                    <option value="15"> 3 PM</option>

                    <option value="16"> 4 PM</option>

                    <option value="17" selected> 5 PM</option>

                    <option value="18"> 6 PM</option>

                    <option value="19"> 7 PM</option>

                    <option value="20"> 8 PM</option>

                    <option value="21"> 9 PM</option>

                    <option value="22">10 PM</option>

                    <option value="23">11 PM</option>

                </select>
            </div>
        </div>
    </div>
    <div class="col-lg-4 actions">
        <a class="btn btn-alt-success add-interval">+</a>
        <a class="btn btn-alt-danger remove-interval">-</a>
    </div>

    <div class="col-lg-3">
        <div class="custom-control custom-checkbox mb-20">
            <input class="custom-control-input" type="checkbox" id="wednesday" name="days[]" value="wednesday">
            <label class="custom-control-label" for="wednesday">Wednesday</label>
        </div>
    </div>
    <div class="col-lg-5">
        <div class="row time-interval">
            <div class="col-lg-5">
                <select class="form-control form-control-lg" name="from_hours[wednesday][]">

                    <option value="0">12 AM</option>

                    <option value="1"> 1 AM</option>

                    <option value="2"> 2 AM</option>

                    <option value="3"> 3 AM</option>

                    <option value="4"> 4 AM</option>

                    <option value="5"> 5 AM</option>

                    <option value="6"> 6 AM</option>

                    <option value="7"> 7 AM</option>

                    <option value="8"> 8 AM</option>

                    <option value="9" selected> 9 AM</option>

                    <option value="10">10 AM</option>

                    <option value="11">11 AM</option>

                    <option value="12">12 PM</option>

                    <option value="13"> 1 PM</option>

                    <option value="14"> 2 PM</option>

                    <option value="15"> 3 PM</option>

                    <option value="16"> 4 PM</option>

                    <option value="17"> 5 PM</option>

                    <option value="18"> 6 PM</option>

                    <option value="19"> 7 PM</option>

                    <option value="20"> 8 PM</option>

                    <option value="21"> 9 PM</option>

                    <option value="22">10 PM</option>

                    <option value="23">11 PM</option>

                </select>
            </div>
            <div class="col-lg-2">
                <span class="pl-15">to</span>
            </div>
            <div class="col-lg-5">
                <select class="form-control form-control-lg mb-30" name="to_hours[wednesday][]">

                    <option value="0">12 AM</option>

                    <option value="1"> 1 AM</option>

                    <option value="2"> 2 AM</option>

                    <option value="3"> 3 AM</option>

                    <option value="4"> 4 AM</option>

                    <option value="5"> 5 AM</option>

                    <option value="6"> 6 AM</option>

                    <option value="7"> 7 AM</option>

                    <option value="8"> 8 AM</option>

                    <option value="9"> 9 AM</option>

                    <option value="10">10 AM</option>

                    <option value="11">11 AM</option>

                    <option value="12">12 PM</option>

                    <option value="13"> 1 PM</option>

                    <option value="14"> 2 PM</option>

                    <option value="15"> 3 PM</option>

                    <option value="16"> 4 PM</option>

                    <option value="17" selected> 5 PM</option>

                    <option value="18"> 6 PM</option>

                    <option value="19"> 7 PM</option>

                    <option value="20"> 8 PM</option>

                    <option value="21"> 9 PM</option>

                    <option value="22">10 PM</option>

                    <option value="23">11 PM</option>

                </select>
            </div>
        </div>
    </div>
    <div class="col-lg-4 actions">
        <a class="btn btn-alt-success add-interval">+</a>
        <a class="btn btn-alt-danger remove-interval">-</a>
    </div>

    <div class="col-lg-3">
        <div class="custom-control custom-checkbox mb-20">
            <input class="custom-control-input" type="checkbox" id="thursday" name="days[]" value="thursday">
            <label class="custom-control-label" for="thursday">Thursday</label>
        </div>
    </div>
    <div class="col-lg-5">
        <div class="row time-interval">
            <div class="col-lg-5">
                <select class="form-control form-control-lg" name="from_hours[thursday][]">

                    <option value="0">12 AM</option>

                    <option value="1"> 1 AM</option>

                    <option value="2"> 2 AM</option>

                    <option value="3"> 3 AM</option>

                    <option value="4"> 4 AM</option>

                    <option value="5"> 5 AM</option>

                    <option value="6"> 6 AM</option>

                    <option value="7"> 7 AM</option>

                    <option value="8"> 8 AM</option>

                    <option value="9" selected> 9 AM</option>

                    <option value="10">10 AM</option>

                    <option value="11">11 AM</option>

                    <option value="12">12 PM</option>

                    <option value="13"> 1 PM</option>

                    <option value="14"> 2 PM</option>

                    <option value="15"> 3 PM</option>

                    <option value="16"> 4 PM</option>

                    <option value="17"> 5 PM</option>

                    <option value="18"> 6 PM</option>

                    <option value="19"> 7 PM</option>

                    <option value="20"> 8 PM</option>

                    <option value="21"> 9 PM</option>

                    <option value="22">10 PM</option>

                    <option value="23">11 PM</option>

                </select>
            </div>
            <div class="col-lg-2">
                <span class="pl-15">to</span>
            </div>
            <div class="col-lg-5">
                <select class="form-control form-control-lg mb-30" name="to_hours[thursday][]">

                    <option value="0">12 AM</option>

                    <option value="1"> 1 AM</option>

                    <option value="2"> 2 AM</option>

                    <option value="3"> 3 AM</option>

                    <option value="4"> 4 AM</option>

                    <option value="5"> 5 AM</option>

                    <option value="6"> 6 AM</option>

                    <option value="7"> 7 AM</option>

                    <option value="8"> 8 AM</option>

                    <option value="9"> 9 AM</option>

                    <option value="10">10 AM</option>

                    <option value="11">11 AM</option>

                    <option value="12">12 PM</option>

                    <option value="13"> 1 PM</option>

                    <option value="14"> 2 PM</option>

                    <option value="15"> 3 PM</option>

                    <option value="16"> 4 PM</option>

                    <option value="17" selected> 5 PM</option>

                    <option value="18"> 6 PM</option>

                    <option value="19"> 7 PM</option>

                    <option value="20"> 8 PM</option>

                    <option value="21"> 9 PM</option>

                    <option value="22">10 PM</option>

                    <option value="23">11 PM</option>

                </select>
            </div>
        </div>
    </div>
    <div class="col-lg-4 actions">
        <a class="btn btn-alt-success add-interval">+</a>
        <a class="btn btn-alt-danger remove-interval">-</a>
    </div>



</div>

I'm totally stuck here, not sure why it works on the whole row but not part of it. I suspect it has something to do with the use of parent(), but not sure. Any insight is MUCH appreciated!

tzazo
  • 323
  • 3
  • 13
  • Welcome to Stack Overflow! Please post a [mcve]. You can use a [Stack Snippet](https://meta.stackoverflow.com/questions/358992/ive-been-told-to-create-a-runnable-example-with-stack-snippets-how-do-i-do) to make it executable. – Barmar Jan 21 '20 at 18:40
  • Thanks! Posted above, but you may prefer the JSFiddle version (https://jsfiddle.net/znxj5Lrs/) because it's formatted nicely (Bootstrap). – tzazo Jan 21 '20 at 18:53
  • 1
    The `+` and `-` buttons aren't inside the `time-interval` DIVs. – Barmar Jan 21 '20 at 19:10
  • Thanks! Good catch - that was indeed the problem with the buttons not working. However, I still have an issue with the `clone` not attaching to the right parent. Clicking `+` adds a row to the end of the form, not under the day that you selected the `+` under. Updated fiddle: https://jsfiddle.net/fanjcvq0/ – tzazo Jan 21 '20 at 19:21
  • Welcome to Stack Overflow! When you do appendTo you are adding onto the end of whatever element you selected. By using body as the selector it will append to the end of all elements present under the tag. If you want to appendTo the particular days div you would need to select that particular days div. – Julian Dasilva Jan 21 '20 at 19:25
  • @JulianDasilva Thanks! I switched to use the parent selector you suggested in your earlier answer and now it appends to the correct parent, but there's weird behavior in that it doubles the number of elements when you click the plus. Try clicking `+` three times on this updated version. The `-` then deletes all the added elements suddenly (instead of the one you clicked `-` on). Fiddle: https://jsfiddle.net/zex21n4p/ – tzazo Jan 21 '20 at 19:28
  • I actually deleted the answer thanks to Barmar's suggestion. With his catch you don't need find(), and the .parents() method should work and has the added benefit of being faster! – Julian Dasilva Jan 21 '20 at 19:33

1 Answers1

2

Refer this for the working code

https://jsfiddle.net/p9Loj8kc/1 ,

The way you are finding the .time-interval is wrong.

Below is the code change in js

js:

var regex = /^(.+?)(\d+)$/i;
var cloneIndex = $(".time-interval").length;
function clone(){
var $closestTimeInterval = $(this).closest(".actions").siblings(".time-interval").first();
    $closestTimeInterval.clone()
        .insertAfter($closestTimeInterval).attr("id", "time-interval" +  cloneIndex)
        .find("*")
        .each(function() {
            var id = this.id || "";
            var match = id.match(regex) || [];
            if (match.length == 3) {
                this.id = match[1] + (cloneIndex);
            }
        });
    cloneIndex++;
}
function remove(){
    $(this).parents(".time-interval").remove();
}
$(".add-interval").on("click", clone);

$(".remove-interval").on("click", remove);
CNKR
  • 568
  • 5
  • 19
  • Check this for `remove()` functionality , https://jsfiddle.net/p9Loj8kc/2/ – CNKR Jan 21 '20 at 19:43
  • The layout looks good to me in jsfiddle, i made changes to the html as well in fiddle. – CNKR Jan 21 '20 at 19:44
  • Thanks! `remove()` works in the latest one you sent, but the layout looks like this (after clicking `+` twice for Monday): https://imgur.com/dZn83PK. Try maximizing the width of your screen as wide as possible. Is it because I need to account for the `.col-lg-3` to create empty space that pushes the new row three columns to the right (since there's no day name taking up space on added rows)? – tzazo Jan 21 '20 at 19:51
  • Is the screenshot from the fiddle or your application? – CNKR Jan 21 '20 at 19:55
  • We need to add the space of the label `col-lg-3` before each new time interval. – CNKR Jan 21 '20 at 20:11
  • I was able to fix the formatting by adding `.offset-lg-3` to each of the cloned elements to shift them to the right. One last issue I noticed: clicking the `+` on a different day always adds the new row under Monday (not the day you picked). I suspect that's because `$(this).closest(".actions").siblings(".time-interval").first();` always selects the first time interval? Fiddle: https://jsfiddle.net/fc47kx8a/ – tzazo Jan 21 '20 at 20:16
  • 1
    https://jsfiddle.net/913wmsfp/, check this add time-interval for the parent div like . `
    ` in html for all the days and take the latest js
    – CNKR Jan 21 '20 at 20:23