最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Filtering a List by a Text Field - Stack Overflow

programmeradmin2浏览0评论

I have a created a small app that helps me be more productive at work. It filters a list of tags by their text contents. It currently works great for finding contiguous lines of text, but I would prefer to re-implement it as a filter that works for non-contiguous binations of text.

For example, if the string inside the tag is "Appleville, MN", the current filter will find it if you type in "Apple" or "Appleville" or "App", but I want to be able to filter successfully with input such as "App MN".

Could anyone point me in the right direction? I'm not sure what exact algorithm I need to go with. Thanks!

P.S.: I've included some code below with examples of the kind of search items I am filtering, the current filter function, as well as the search field.

function filterList() {
  var input, filter, ul, li, a, i, txtValue;
  input = document.getElementById('myInput');
  filter = input.value.toUpperCase();
  ul = document.getElementById("itemList");
  li = ul.getElementsByTagName('li');
  for (i = 0; i < li.length; i++) {
    a = li[i].getElementsByTagName("a")[0];
    txtValue = a.textContent || a.innerText;
    if (txtValue.toUpperCase().indexOf(filter) > -1) {
      li[i].style.display = "";
    } else {
      li[i].style.display = "none";
    }
  }
}
<input type="text" id="myInput" onkeyup="filterList()" placeholder="Search list..">
<ul id="itemList">
  <li onclick=applevilleMN()><a href="#">Appleville, MN</a></li>
  <li onclick=orangevilleOR()><a href="#">Orangeville, OR</a></li>
  <li onclick=kiwivilleCA()><a href="#">Kiwiville, CA</a></li>
  <li onclick=bananavilleCA()><a href="#">Bananaville, CA</a></li>
</ul>

I have a created a small app that helps me be more productive at work. It filters a list of tags by their text contents. It currently works great for finding contiguous lines of text, but I would prefer to re-implement it as a filter that works for non-contiguous binations of text.

For example, if the string inside the tag is "Appleville, MN", the current filter will find it if you type in "Apple" or "Appleville" or "App", but I want to be able to filter successfully with input such as "App MN".

Could anyone point me in the right direction? I'm not sure what exact algorithm I need to go with. Thanks!

P.S.: I've included some code below with examples of the kind of search items I am filtering, the current filter function, as well as the search field.

function filterList() {
  var input, filter, ul, li, a, i, txtValue;
  input = document.getElementById('myInput');
  filter = input.value.toUpperCase();
  ul = document.getElementById("itemList");
  li = ul.getElementsByTagName('li');
  for (i = 0; i < li.length; i++) {
    a = li[i].getElementsByTagName("a")[0];
    txtValue = a.textContent || a.innerText;
    if (txtValue.toUpperCase().indexOf(filter) > -1) {
      li[i].style.display = "";
    } else {
      li[i].style.display = "none";
    }
  }
}
<input type="text" id="myInput" onkeyup="filterList()" placeholder="Search list..">
<ul id="itemList">
  <li onclick=applevilleMN()><a href="#">Appleville, MN</a></li>
  <li onclick=orangevilleOR()><a href="#">Orangeville, OR</a></li>
  <li onclick=kiwivilleCA()><a href="#">Kiwiville, CA</a></li>
  <li onclick=bananavilleCA()><a href="#">Bananaville, CA</a></li>
</ul>

Share Improve this question edited Sep 25, 2019 at 13:04 j08691 208k32 gold badges269 silver badges280 bronze badges asked Sep 25, 2019 at 12:36 artifc3artifc3 771 gold badge1 silver badge7 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 4

here's a modified version doing what you want.

It splits the filter string by spaces to create an array of filters, lis arer shown only if they are valid for each substring of the array.

function filterList() {
    var input, filter, ul, li, a, i, txtValue;
    input = document.getElementById('myInput');
    filter = input.value.toUpperCase();
    ul = document.getElementById("itemList");
    li = ul.getElementsByTagName('li');

    for (i = 0; i < li.length; i++) {
      a = li[i].getElementsByTagName("a")[0];
      txtValue = a.textContent || a.innerText;
      
      // split filter by spaces, gives ["app", "MN"] in your example  
      let filters = filter.split(" ") 

      // remove the empty filters (if your filter string 
      // starts or ends by a space) since they are source of errors

      // Array.filter takes in parameter a function returning a boolean
      // it create a new array containing only element where 
      // the function returned truthy value

      // here we return the length of the string which is falsy (== 0) for "" 
      // and truthy for every other string (!= 0)
      filters = filters.filter(f => f.length)   

      let shouldDisplay = true
      // test each filter and store true only if string contains all filter
      filters.forEach(filt => {
        shouldDisplay = shouldDisplay && txtValue.toUpperCase().includes(filt)
      })
      
      // update visibility
      // set visible if the string include all filters
      // or if there is no filter
      li[i].style.display = (shouldDisplay || filters.length === 0) ? "" : "none";
    }
  }
<ul id="itemList">
      <li onclick=applevilleMN()><a href="#" >Appleville, MN</a></li>
      <li onclick=orangevilleOR()><a href="#" >Orangeville, OR</a></li>
      <li onclick=kiwivilleCA()><a href="#" >Kiwiville, CA</a></li>
      <li onclick=bananavilleCA()><a href="#" >Bananaville, CA</a></li>
</ul>

<input type="text" id="myInput" onkeyup="filterList()" placeholder="Search list.." >

docs : Array.filter, truthy values, falsy values

var itensList = document.querySelectorAll('#itemList li');

function filterList() {

var valueInput = document.querySelector('#myInput').value.toLowerCase().trim();

for(var i = 0; i < itensList.length; i++){
  var item = itensList[i];
  var valueLi = item.innerText.toLowerCase().trim();

  item.style.display = valueLi.search(new RegExp(valueInput.replace(/\s+/, '|'))) != -1 ? '' : 'none';
  }
}
<input type="text" id="myInput" onkeyup="filterList()" placeholder="Search list..">
<ul id="itemList">
  <li onclick=applevilleMN()><a href="#">Appleville, MN</a></li>
  <li onclick=orangevilleOR()><a href="#">Orangeville, OR</a></li>
  <li onclick=kiwivilleCA()><a href="#">Kiwiville, CA</a></li>
  <li onclick=bananavilleCA()><a href="#">Bananaville, CA</a></li>
</ul>

I remend you to use fuse.js. Its an approximate string matching libraray, and its is lightweight and easy to set up. You only need to feed it with list of objects and use the object's key as its key.

const cities = [{
    state: "MN",
    city: "Appleville"
  },
  {
    state: "OR",
    city: "Orangeville"
  },
  {
    state: "CA",
    city: "Kiwiville"
  },
  {
    state: "CA",
    city: "Bananaville"
  }
]
var options = {
  shouldSort: true,
  threshold: 0.6,
  location: 0,
  distance: 100,
  maxPatternLength: 32,
  minMatchCharLength: 2,
  keys: ['state', 'city']
}
var fuse = new Fuse(cities, options)

const input = document.getElementById("myInput")

input.oninput = (event) => {
  const results = fuse.search(event.target.value)
  for (result of results) {
    console.log(result.state, result.city)
  }
}
<script src="https://cdnjs.cloudflare./ajax/libs/fuse.js/3.4.5/fuse.min.js"></script>

<ul id="itemList">
  <li onclick=applevilleMN()><a href="#">Appleville, MN</a></li>
  <li onclick=orangevilleOR()><a href="#">Orangeville, OR</a></li>
  <li onclick=kiwivilleCA()><a href="#">Kiwiville, CA</a></li>
  <li onclick=bananavilleCA()><a href="#">Bananaville, CA</a></li>
</ul>

<input type="text" id="myInput" placeholder="Search list..">

Threshold

At what point does the match algorithm give up. A threshold of 0.0 requires a perfect match (of both letters and location), a threshold of 1.0 would match anything. - fuse.js

Thrashold is the most important factor when its es to word matching. As of now, it matchs words that are 40% similar to the input. If you think its quite wide, decrease the number.

发布评论

评论列表(0)

  1. 暂无评论