javascriptjqueryadditionremovechild

Todolist: Remove dynamically added divs


I have a kind of todolist, in which I add elements when clicking on the 'add' button. There are two default lines. The add function works great, here's the original code

$(function() {
  $(".container").on('input', 'input[type=text]', function(event) {
    var i = $(this).closest(".flex-row").index();
    var v = (i == 0) ? $(this).val() : "|" + $(this).val();
    $("#custom_wrapper .output").eq(i).html(v);
  });

  $('.add').click(function() {
    var count = $("input").length;
    count++;
    var row = $("<div>", {
      class: "flex-row"
    }).insertBefore(this);
    $("<label>").appendTo(row);
    $("<input>", {
      type: "text",
      class: "input",
      placeholder: "custom text " + count,
      tabindex: count
    }).appendTo($("label", row));
    $("<button>", {
      class: "remove"
    }).html("-").appendTo(row);
    $("<span>", {
      class: "output"
    }).insertAfter($("#custom_wrapper .output:last"));
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>


<div class="container">
  <div class="wrapper">
    <article>
      <section>
        <div class="flex-row">
          <label><input class="input" type="text" name="" placeholder="custom text" tabindex="1" /></label><button class="remove">-</button>
        </div>
        <div class="flex-row">
          <label><input class="input" type="text" name="" placeholder="custom text 2" tabindex="2"></label><button class="remove">-</button>
        </div>
        <button class="add">+</button>
        <div class="flex-row">
          <div>customText{{[<span id="custom_wrapper"><span class="output"></span><span class="output"></span></span>]}}</div>
        </div>
      </section>
    </article>
  </div>
</div>

Now, I've added a button to delete the lines. This is where it gets stuck.

For example: I add two rows to have 4 in total. For example, each line has one letter on it. (a,b,c,d) I delete "b,c,d" and I leave "a"; When I add more, the added text will be inserted before the "a" when it should come after. If I keep adding more, a text will replace an existing text, instead of being added.

Moreover I would like that in my span.output, there is never a pipe before the first text.

Does anyone have any idea where the problem comes from?

Here is my updated code:

$(function() {
  $(".container").on('input', 'input[type=text]', function(event) {
    var i = $(this).closest(".flex-row").index();
    var v = (i == 0) ? $(this).val() : "|" + $(this).val();
    $("#custom_wrapper .output").eq(i).html(v);
  });

  $('.add').click(function() {
    var count = $("input").length;
    count++;
    var row = $("<div>", {
      class: "flex-row"
    }).insertBefore(this);
    $("<label>").appendTo(row);
    $("<input>", {
      type: "text",
      class: "input",
      id: "custom-text-" + count,
      placeholder: "custom text " + count,
      tabindex: count
    }).appendTo($("label", row));
    $("<button>", {
      class: "remove"
    }).html("-").appendTo(row);
    $("<span>", {
      class: "output",
      dataid: "custom-text-" + count
    }).insertAfter($("#custom_wrapper .output:last"));
  });

  $('body').on('click', '.remove', function() {
    $(this).parent('.flex-row').remove();
    var j = $(this).parent().find('.input').attr("id");
    $('#custom_wrapper .output[dataid="' + j + '"').empty();
  })

})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<div class="container">
  <div class="wrapper">
    <article>
      <section>
        <div class="flex-row">
          <label><input class="input" type="text" name="" id="custom-text-1" placeholder="custom text" tabindex="1" /></label><button class="remove">-</button>
        </div>
        <div class="flex-row">
          <label><input class="input" type="text" name="" id="custom-text-2" placeholder="custom text 2" tabindex="2"></label><button class="remove">-</button>
        </div>
        <button class="add">+</button>
        <div class="flex-row">
          <div class="token">{{customText[<span id="custom_wrapper"><span class="output" dataid="custom-text-1"></span><span class="output" dataid="custom-text-2"></span></span>]}}</div>
        </div>
      </section>
    </article>
  </div>
</div>


Solution

  • Does this help?

    Explanation: The reason for such behavior was because when an input row was being removed, the corresponding #custom_wrapper .output was not being removed. This was leading to desired elements not having the same indices, which was supposed to be same, and was being used on input event.

    Extra changes: The id's of new elements were being assigned on the basis of children count, thus creating a case, where there might be multiple elements having same id. This code takes care of that as well.

    $(function() {
      let nextInputId = 3;
    
      $(".container").on('input', 'input[type=text]', function(event) {
        const i = $(this).closest(".flex-row").index();
        const v = $(this).val();
        $("#custom_wrapper .output").eq(i).html(v);
      });
    
      $('.add').click(function() {
        const count = $("input").length;
    
        const row = $("<div>", {
          class: "flex-row"
        }).insertBefore(this);
    
        $("<label>").appendTo(row);
    
        $("<input>", {
          type: "text",
          class: "input",
          id: "custom-text-" + nextInputId,
          placeholder: "custom text " + (count + 1),
          tabindex: (count + 1)
        }).appendTo($("label", row));
    
        $("<button>", {
          class: "remove"
        }).html("-").appendTo(row);
    
        $("<span>", {
          class: "output",
          dataid: "custom-text-" + nextInputId
        }).appendTo($("#custom_wrapper"));
    
        nextInputId++;
      });
    
      $('body').on('click', '.remove', function() {
        const i = $(this).closest(".flex-row").index();
        $("#custom_wrapper .output").eq(i).remove();
        $(this).parent('.flex-row').remove();
      })
    });
    #custom_wrapper .output:not(:first-child):not(:empty)::before {
      content: "|"
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
    <div class="container">
      <div class="wrapper">
        <article>
          <section>
            <div class="flex-row">
              <label><input class="input" type="text" name="" id="custom-text-1" placeholder="custom text" tabindex="1" /></label><button class="remove">-</button>
            </div>
            <div class="flex-row">
              <label><input class="input" type="text" name="" id="custom-text-2" placeholder="custom text 2" tabindex="2"></label><button class="remove">-</button>
            </div>
            <button class="add">+</button>
            <div class="flex-row">
              <div class="token">{{customText[<span id="custom_wrapper"><span class="output" dataid="custom-text-1"></span><span class="output" dataid="custom-text-2"></span></span>]}}</div>
            </div>
          </section>
        </article>
      </div>
    </div>