skip to Main Content

I have a todo list that I made with the help of Pranav Rustagi and Twisty. I still have a problem for which I can’t find a solution.

Between each text added, I want to add a separator (pipe). My problem is that when I delete the first element, the next element, which becomes the first one, has an unwanted pipe.

So how can I add a separator only if there is more than one element, and only if it’s not the first element ?

To reproduce the scenario: add a, b, c, and then delete a. You’ll see the pipe before the b still there. Is it better to imagine a pipe only if there is an element after?

One main issue here is the empty() inside the remove function. It doesn’t delete the span.output from the DOM so I cannot use first() or do something in css like :

.output:not(:first-of-type):before {
  content:'|';
  padding:0 5px;
}

I can’t use remove() instead of empty() either because I won’t be able to add a line again if I delete them all.

Any help would be appreciated.

$(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>

I also tried to add the pipe with css instead, the part that changes in the jquery is the following:

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

I reformulated my delete function to delete instead of emptying

$('body').on('click', '.remove', function() {
const i = $(this).closest(".flex-row").index();
$("#custom_wrapper .output").eq(i).remove();
$(this).parent('.flex-row').remove();
 })

Final code looks like this :

<html><head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script>
$(function() {
  $(".container").on('input', 'input[type=text]', function(event) {
    var i = $(this).closest(".flex-row").index();
    // var v = (i == 0) ? $(this).val() : "|" + $(this).val();
    v=$(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"
    }).appendTo($("#custom_wrapper"));
  });

  $('body').on('click', '.remove', function() {
    const i = $(this).closest(".flex-row").index();
    $("#custom_wrapper .output").eq(i).remove();
    $(this).parent('.flex-row').remove();
  })
});

</script>
<style>
.output:not(:first-child):before{
content:'|';
}

</style>
 </head>

    <body>
 <div class="container">
  <div class="wrapper">
    <article>
      <section>
        <div class="flex-row">
          <label for="custom-text-1"><span class="visually-hidden">Custom Text 1</span><input class="input" type="text" id="custom-text-1" placeholder="custom text" tabindex="1" /></label><button class="remove">-</button>
        </div>
        <div class="flex-row">
          <label for="custom-text-2"><span class="visually-hidden">Custom Text 2</span><input class="input" type="text" 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>customText{{[<span id="custom_wrapper"><span class="output"></span><span class="output"></span></span>]}}</div>
        </div>
      </section>
    </article>
  </div>
</div>


</body>

It works, but this solution doesn’t suit me: I use a copy function to copy and paste the exit code, and when I copy and paste, the separators are not there, because they are added as pseudo-elements, and seem not to be present in the DOM.

So I definitely need a jquery/javascript solution that actually adds these pipes…

2

Answers


  1. Might make more sense, if you rebuilt the output completely every time:

      $(".container").on('input', 'input[type=text]', function(event) {
        var values = [];
        $(this).closest('section').find('input[type=text]').each(function() {
          values.push($('<span class="output"></span>').text($(this).val()).html());
        });
        $("#custom_wrapper").html(values.join('|'));
      });
    

    – loop over all the input fields, get their value wrapped into a span – and then finally join the whole array together with a pipe symbol. (And the CSS that added it as a pseudo element, needs to be removed.)

    And your function that removes a field, needs to trigger the input event on the first field left, to update the output – or simply clear the wrapper content, if there are no fields left:

      $('body').on('click', '.remove', function() {
        const i = $(this).closest(".flex-row").index();
        $(this).parent('.flex-row').remove();
        if($('.container input[type=text]').length) {
          $('.container input[type=text]').eq(0).trigger('input');
        } else {
          $('#custom_wrapper').html('');
        }
      });
    
    Login or Signup to reply.
  2. I think what you’re trying achieve can simply be done by using CSS only.

    .container span:after {
      content: '|';
      margin: 0 2px;
    }
    
    .container span:last-child:after {
      content: '';
      margin: 0 2px;
    }
    <div class='container'>
      <span>a</span>
      <span>b</span>
      <span>c</span>
    </div>
    
    <div class='container'>
      <span>a</span>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search