0

On every change of input, i need to remove all <li> elements where inner text don't match the input value. The problem is: it don't remove all lines that doesn't match at once.

My code:

        <input name="tag-input" id="tag-input" type='text'>
    <div id="list-pol">
        <ul id="list-pol-select">
            <li class="list-pol-item">Fish</li>
            <li class="list-pol-item">Dog</li>
            <li class="list-pol-item">Chameleon</li>
            <li class="list-pol-item">Cat</li>
        </ul>
    </div>
    <script type="text/javascript">

        var input = document.getElementById('tag-input');

        function updateList(){
            if(document.getElementsByClassName("list-pol-item")[0]){

                var list = document.getElementsByClassName("list-pol-item");

                for (var i = 0; i < list.length; i++) {
                    var tag = list.item(i).innerText;
                    if(input.value !== tag.substring(0,input.value.length)){
                        list.item(i).remove();
                    }
                }
            }
        }

        input.addEventListener('input', updateList);
    </script>
3
  • Instead of list.item(i) you could just do list[i]. What is the if statement for in updateList()? It seems redundant... Commented Jan 21, 2022 at 1:54
  • I put the the if statement in case there's no list elements to iterate. Commented Jan 21, 2022 at 2:20
  • If there are no elements left to iterate the loop will not iterate (i < 0 == false), so it's definitely redundant... right? Commented Jan 21, 2022 at 2:46

1 Answer 1

1

getElementsByClassName will give you a live collection, which is very confusing. If the ith element in the collection loses the class name, the collection will lose that element and shift down immediately. If that happens while you're trying to iterate over it - like here - you'll be in trouble.

const foos = document.getElementsByClassName('foo');
foos[0].remove();
console.log(foos.length);
console.log(foos[0]);
<div class="foo">1</div>
<div class="foo">2</div>

Turn it into a (non-live) array instead first - or use querySelectorAll, which returns a static NodeList.

var input = document.getElementById('tag-input');

function updateList() {
  for (const item of document.querySelectorAll('.list-pol-item')) {
    if (!item.textContent.startsWith(input.value)) {
      item.remove();
    }
  }
}

input.addEventListener('input', updateList);
<input name="tag-input" id="tag-input" type='text'>
<div id="list-pol">
  <ul id="list-pol-select">
    <li class="list-pol-item">Fish</li>
    <li class="list-pol-item">Dog</li>
    <li class="list-pol-item">Chameleon</li>
    <li class="list-pol-item">Cat</li>
  </ul>
</div>

If you paste in "Fish", you'll see that Fish is the only item that remains.

But your current logic is weird - do you really want to .remove() the items that don't match? Unless someone pastes in text that matches exactly, everything will be removed. Did you want to toggle the items' display instead?

var input = document.getElementById('tag-input');

function updateList() {
  for (const item of document.querySelectorAll('.list-pol-item')) {
    item.classList.toggle('hidden', !item.textContent.startsWith(input.value));
  }
}

input.addEventListener('input', updateList);
.hidden {
  display: none;
}
<input name="tag-input" id="tag-input" type='text'>
<div id="list-pol">
  <ul id="list-pol-select">
    <li class="list-pol-item">Fish</li>
    <li class="list-pol-item">Dog</li>
    <li class="list-pol-item">Chameleon</li>
    <li class="list-pol-item">Cat</li>
  </ul>
</div>

You also might consider comparing the lower-cased input against the lower-cased list item, instead of requiring a case match for the elements to display.

Sign up to request clarification or add additional context in comments.

4 Comments

List items will be dynamically generated, I think would be better to remove instead of toggle display but please correct me if i'm wrong. I use substring to match the first characters of the list not the entire word.
If .remove() is what your app requires, go for it. Yeah, I just saw the === and missed that you were checking the substring - if you want to check that one string starts with another, use startsWith instead.
Can you point the difference between textContent and innerText?
.textContent is quicker and much more predictable. perfectionkills.com/the-poor-misunderstood-innerText

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.