Reputation: 58531
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 keep extra light.
Upvotes: 50
Views: 69123
Reputation: 1002
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>
<option value="CSS"></option>
<option value="JavaScript"></option>
<option value="Java"></option>
<option value="Ruby"></option>
<option value="PHP"></option>
<option value="Go"></option>
<option value="Erlang"></option>
<option value="Python"></option>
<option value="C"></option>
<option value="C#"></option>
<option value="C++"></option>
</datalist>
<input type="text" list="languages">
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist
Upvotes: 32
Reputation: 17132
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.
Upvotes: 1
Reputation: 24308
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>
Upvotes: 35
Reputation: 727
ES2016 feature: Array.prototype.includes
without external library.
function autoComplete(Arr, Input) {
return Arr.filter(e =>e.toLowerCase().includes(Input.toLowerCase()));
}
Upvotes: 3
Reputation: 673
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.
Upvotes: 0
Reputation: 28114
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/
Upvotes: 9