JavaScript autocomplete without external library

后端 未结 6 2077
面向向阳花
面向向阳花 2020-12-23 11:33

Is there a javascript autocomplete library that does not depend on any other libraries?

I am not using jQuery or the likes as I am making a mobile app that I need to

相关标签:
6条回答
  • 2020-12-23 11:50

    For anyone looking at this in 2017 onwards who needs a simple solution, you can use HTML5's built-in <datalist> tag instead of relying on JavaScript.

    Example:

    <datalist id="languages">
      <option value="HTML">
      <option value="CSS">
      <option value="JavaScript">
      <option value="Java">
      <option value="Ruby">
      <option value="PHP">
      <option value="Go">
      <option value="Erlang">
      <option value="Python">
      <option value="C">
      <option value="C#">
      <option value="C++">
    </datalist>
    
    <input type="text" list="languages">
    

    Note that this won't work in Safari (as of April 2017).

    https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist

    0 讨论(0)
  • 2020-12-23 11:57

    I was researching this the other night, and my solution was originally like the ES6 solution in here, like this:

    return this.data.filter((option) => {
        return option.first_name
            .toString()
            .toLowerCase()
            .indexOf(this.searchTerms.toLowerCase()) >= 0
    })
    

    But the problem with that is it isn't robust enough to handle filtering nested data. You can see it is filtering this.data which has a data structure like:

    [
        { first_name: 'Bob', },
        { first_name: 'Sally', },
    ]
    

    You can see it filters based on this.searchTerms after lowercasing both the search term and option.first_name, but it is too rigid to search for option.user.first_name. My original attempt was to pass in the field to filter by, such as:

    this.field = 'user.first_name';
    

    But this involves savage, custom JavaScript to handle something like this.field.split('.') and dynamically generating the filter function.

    Instead, I remembered an old library I've used before called fuse.js, and it is working well because it not only handles that case of arbitrary nesting on what I just called this.field but also handles fuzzy matching based on defined thresholds.

    Check out here: https://fusejs.io/

    [edit note]: I realize this question is looking for no-external-library, but I want to keep this post here as it provides adjacent value. It is not intended to be "the" solution.

    Here is how I'm currently using it:

    import Fuse from 'fuse.js';
    
    const options = {
        threshold: 0.3,
        minMatchCharLength: 2,
        keys: [this.field],
    };
    
    const fuse = new Fuse(this.data, options);
    
    this.filteredData = fuse.search(this.searchTerms);
    

    You will have to read the Fuse docs to understand that better, but fundamentally, you can see that a new Fuse() object is created using the data to filter and the options.

    The keys: [this.field] part is important because that's where you pass in the keys to search by, and you can pass in an array of them. For example, you could filter this.data by keys: ['user.first_name', 'user.friends.first_name'].

    I am using this currently in Vue JS, so I have that above logic inside an instance watch function, so every time this.searchTerms changes, that logic runs and updates this.filteredData which is placed into my dropdown list in my autocomplete component.

    Also I'm sorry I just realized this question specifically says without an external library, but I'll post this anyway because I find this question every time I make an ES6 autocomplete in Vue JS or React JS. I think it is very valuable to have strict or loose fuzzy matching and to support arbitrarily nested data. Based on Webpack bundle analyzer, fuse.js is 4.1kb gzipped, so it is quite small given that it can support "all" client-side filtering needs.

    If you are limited in your ability to use external libraries, consider my first example piece of code. It works if your data structure is static, and you can easily change option.first_name to something like option[this.field] if you wish to variablize the searched field (ie: if your objects are always flat).

    You could variablize the list to search as well. Try something like this:

    const radicalFilter = ({ collection, field, searchTerms }) => {
        return collection.filter((option) => {
            return option[field]
                .toString()
                .toLowerCase()
                .indexOf(searchTerms.toLowerCase()) >= 0
        })
    }
    
    radicalFilter({
        collection: [{ first_name: 'Bob' }, { first_name: 'Sally' }],
        field: 'first_name',
        searchTerms: 'bob',
    })
    

    Based on my experiences over the past couple years, the above sample is very performant. I've used it to filter 10,000 records in a react-table component, and it didn't break a sweat. It doesn't create any extra intermediate data structures. It is simply just Array.prototype.filter() which takes your array and returns a new array with matched items.

    0 讨论(0)
  • 2020-12-23 12:03

    Here is a basic JavaScript example, which could be modified into an autocomplete control:

    var people = ['Steven', 'Sean', 'Stefan', 'Sam', 'Nathan'];
    
    function matchPeople(input) {
      var reg = new RegExp(input.split('').join('\\w*').replace(/\W/, ""), 'i');
      return people.filter(function(person) {
        if (person.match(reg)) {
          return person;
        }
      });
    }
    
    function changeInput(val) {
      var autoCompleteResult = matchPeople(val);
      document.getElementById("result").innerHTML = autoCompleteResult;
    }
    <input type="text" onkeyup="changeInput(this.value)">
    <div id="result"></div>

    0 讨论(0)
  • 2020-12-23 12:05

    ES2016 feature: Array.prototype.includes without external library.

    function autoComplete(Arr, Input) {
        return Arr.filter(e =>e.toLowerCase().includes(Input.toLowerCase()));
    }
    

    Codepen Demo

    0 讨论(0)
  • 2020-12-23 12:07

    The core of an autocomplete script will be the ajax call to the dictionary of terms.

    I assume your mobile application already includes an ajax function, so maybe you're better off just writing your autocomplete from scratch? Basically all you need in an input tag, a keyup event handler that triggers the ajax call, and a div to collect the response.

    [Update] Based on the comments, some references from John Resig's blog:

    http://ejohn.org/blog/revised-javascript-dictionary-search/

    http://ejohn.org/blog/jquery-livesearch/

    0 讨论(0)
  • 2020-12-23 12:10

    I did this once by sending a JSON request back to the server and using Python code to do the autocomplete. It Was a little slow, but it saved sending a ton of data across.

    0 讨论(0)
提交回复
热议问题