@francoischalifour/autocomplete.js v1.0.0-alpha.1
Autocomplete.js
Autocomplete.js is a JavaScript library that creates a fast and fully-featured auto-completion experience.
Features
- Displays suggestions as you type
- Shows top suggestion as a completion
- Supports custom templates for UI flexibility
- Works well with RTL languages
- Triggers custom hooks to plug your logic
- Plugs easily to Algolia's realtime search engine
Usage
HTML
<body>
<div id="autocomplete"></div>
</body>
JavaScript
const items = [
{ value: 'Apple', count: 120 },
{ value: 'Banana', count: 100 },
{ value: 'Cherry', count: 50 },
{ value: 'Orange', count: 150 },
];
autocomplete({
container: '#autocomplete',
getSources() {
return [
{
getSuggestions({ query }) {
return items.filter(item =>
item.value.toLocaleLowerCase().includes(query.toLocaleLowerCase())
);
},
getInputValue({ suggestion }) {
return suggestion.value;
},
templates: {
suggestion({ suggestion }) {
return `<div>${suggestion.value} (${suggestion.count})</div>`;
},
},
},
];
},
});
You can learn more about the options and the top-level API.
Installation
Autocomplete.js is available on the npm registry.
yarn add @francoischalifour/autocomplete.js
# or
npm install @francoischalifour/autocomplete.js
If you do not wish to use a package manager, you can use standalone endpoints:
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/autocomplete.js/1"></script>
<!-- unpkg -->
<script src="https://unpkg.com/autocomplete.js/1"></script>
API
Options
container
string | HTMLElement
| required
The container for the autocomplete search box.
getSources
(options: { query: string }) => AutocompleteSource[] | Promise<AutocompleteSource[]>
Called to fetch the sources.
dropdownContainer
string | HTMLElement
| defaults todocument.body
The container for the autocomplete dropdown.
dropdownPosition
'left' | 'right'
| defaults to'left'
The dropdown position related to the container.
placeholder
string
| defaults to""
The text that appears in the search box input when there is no query.
It is fowarded to the input
's placeholder.
showCompletion
boolean
| defaults tofalse
Whether to show the highlighted suggestion as completion in the input.
minLength
number
| defaults to1
The minimum number of characters long the autocomplete opens.
autofocus
boolean
| defaults tofalse
Whether to focus the search box when the page is loaded.
keyboardShortcuts
string[]
The keyboard shortcuts keys to focus the input.
defaultHighlightedIndex
number
| defaults to0
(the first item)
The default item index to pre-select.
stallThreshold
number
| defaults to300
The number of milliseconds that must elapse before the autocomplete experience is stalled. The timeout is set from the moment getSources
is called.
When the experience is stalled:
- The CSS class
algolia-autocomplete--stalled
is added to the autocomplete container - The
isStalled
boolean istrue
in the state
initialState
The initial state to apply when the page is loaded.
templates
Refer to the "Global Templates" section.
transformResultsRender
(results: JSX.Element[]) => JSX.Element | JSX.Element[]
Called before rendering the results.
Useful to wrap results in containers to organize the display.
autocomplete({
// ...
transformResultsRender(results) {
const [recentSearches, querySuggestions, products] = results;
return (
<div style={{ display: 'flex' }}>
<div style={{ flex: 1 }}>
{recentSearches}
{querySuggestions}
</div>
<div style={{ flex: 2 }}>{products}</div>
</div>
);
},
});
environment
typeof window
| defaults towindow
The environment from where your JavaScript is running.
Useful if you're using Autocomplete.js in a different context than window
.
onFocus
(options: { state: AutocompleteState, ...setters }) => void
Called when the input is focused.
This function is also called when the input is clicked while already having the focus and the dropdown is closed.
onError
(options: { state: AutocompleteState, ...setters }) => void
| defaults to({ state }) => throw state.error
Called when an error is thrown while getting the suggestions.
When an error is caught:
- The error is thrown (default
onError
implementation) - The CSS class
algolia-autocomplete--errored
is added to the autocomplete container - The error is available in the state
onClick
(event: MouseEvent, options: { state: AutocompleteState, ...setters, suggestion: any, suggestionValue: string }) => void
Called when a click
event is fired on an item.
This function is useful to alter the behavior when a special key is held (e.g. keeping the dropdown open when the meta key is used).
onKeyDown
(event: KeyboardEvent, options: { state: AutocompleteState, ...setters, suggestion?: any, suggestionValue?: string }) => void
Called when a keydown
event is fired.
This function is useful to alter the behavior when a special key is held.
autocomplete({
// ...
onKeyDown(event, { suggestion, state, setIsOpen }) {
if (!suggestion || !suggestion.url) {
return;
}
if (event.key === 'Enter') {
if (event.metaKey || event.ctrlKey) {
setIsOpen(true);
const windowReference = window.open(suggestion.url, '_blank');
windowReference.focus();
} else if (event.shiftKey) {
window.open(suggestion.url, '_blank');
} else if (event.altKey) {
// Keep native browser behavior
} else {
window.location.assign(suggestion.url);
}
}
},
});
onInput
(options: { query: string, state: AutocompleteState, ...setters }) => void | Promise <void | { state: AutocompleteState }>
Called when the input changes.
This turns experience is "controlled" mode. You'll be in charge of updating the state with the top-level API.
Sources
An Autocomplete source refers to an object with the following properties:
getInputValue
(options: { suggestion: Suggestion, state: AutocompleteState }) => string
Called to get the value of the suggestion. The value is used to fill the search box.
If you do not wish to update the input value when an item is selected, you can return state.query
.
const items = [{ value: 'Apple' }, { value: 'Banana' }];
const source = {
getInputValue: ({ suggestion }) => suggestion.value,
// ...
};
getSuggestions
(options: { query: string, state: AutocompleteState, ...setters }) => Suggestion[] | Promise<Suggestion[]>
| required
Called when the input changes. You can use this function to filter/search the items based on the query.
const items = [{ value: 'Apple' }, { value: 'Banana' }];
const source = {
getSuggestions({ query }) {
return items.filter(item => item.value.includes(query));
},
// ...
};
templates
required
Templates to use for the source. A template supports strings and JSX elements.
header
(options: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display before the suggestions.
suggestion
(options: { suggestion: Suggestion, state: AutocompleteState, ...setters }) => string | JSX.Element
The template for each suggestion.
footer
(options: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display after the suggestions.
empty
(options: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display when there are no suggestions.
Using strings
const items = [{ value: 'Apple' }, { value: 'Banana' }];
const source = {
templates: {
header() {
return '<h2>Fruits</h2>';
},
suggestion({ suggestion }) {
return suggestion.value;
},
footer() {
return '<a href="/fruits">See more</a>';
},
},
// ...
};
Using JSX elements
const items = [{ value: 'Apple' }, { value: 'Banana' }];
const source = {
templates: {
header() {
return <h2>Fruits</h2>;
},
suggestion({ suggestion }) {
return suggestion.value;
},
footer() {
return <a href="/fruits">See more</a>;
},
},
// ...
};
onSelect
(options: { state: AutocompleteState, ...setters }) => void
Called when an item is selected.
State
The Autocomplete.js state drives the behavior of the experience.
Getters
The state can be initially set with initialState
and it's is passed to all templates.
query
string
| defaults to''
The query.
results
Result[]
| defaults to[]
The results of all the sources.
interface Result {
source: AutocompleteSource;
suggestions: any[];
}
isOpen
boolean
| defaults tofalse
Whether the dropdown is open.
isLoading
boolean
| defaults tofalse
Whether the experience is loading.
isStalled
boolean
| defaults tofalse
Whether the experience is stalled.
error
null | Error
| defaults tonull
The error that happened, null
if none.
context
object
| defaults to{}
The autocomplete context to store data in. It's useful to use custom data in templates.
Setters
Each state has a setter that can be used in the lifecycle of Autocomplete.js.
setQuery
(value: string | ((prevState: string) => string)) => void
Sets the query
value in the state.
setResults
(value: Result[] | ((prevState: Result[]) => Result[])) => void
Sets the results
value in the state.
setIsOpen
(value: boolean | ((prevState: boolean) => boolean)) => void
Sets the isOpen
value in the state.
setIsLoading
(value: boolean | ((prevState: boolean) => boolean)) => void
Sets the isLoading
value in the state.
setIsStalled
(value: boolean | ((prevState: boolean) => boolean)) => void
Sets the isStalled
value in the state.
setError
(value: Error | null | ((prevState: Error | null) => Error | null)) => void
Sets the error
value in the state.
setContext
(value: object | ((prevState: object) => object)) => void
Sets the context
value in the state.
Storing nbHits
from the Algolia response
autocomplete({
// ...
getSources({ query, setContext }) {
return getAlgoliaResults({
searchClient,
query,
searchParameters: [
{
indexName: 'instant_search',
params: {
attributesToSnippet: ['description'],
},
},
],
}).then(results => {
const productsResults = results[0];
setContext({
nbProducts: productsResults.nbHits,
});
return [
{
// ...
templates: {
header({ state }) {
return `<h2>Products (${state.context.nbProducts})</h2>`;
},
},
},
];
});
},
});
Global templates
In addition to the source templates, Autocomplete.js supports some global templates.
header
(options: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display before all sources.
footer
(options: { state: AutocompleteState, ...setters }) => string | JSX.Element
The template to display after all sources.
Top-level API
autocomplete
autocomplete
is the default export from the autocomplete.js
package. It is the main function that starts the autocomplete experience and accepts options.
The autocomplete
function returns an API that allows you to turn Autocomplete.js in a "controlled" mode. It returns all the setters so that you update the state of the experience.
// Instantiate Autocomplete.js
const autocompleteSearch = autocomplete({
// options
});
// Retrieve the state of your app that you want to forward to Autocomplete.js
const app = getAppState();
// Update the state of Autocomplete.js based on your app state
autocompleteSearch.setQuery(app.query);
autocompleteSearch.setResults(
app.indices.map(index => {
return {
source: getSource({ index }),
suggestions: index.hits,
};
})
);
autocompleteSearch.setIsOpen(app.isOpen);
autocompleteSearch.setIsLoading(app.isLoading);
autocompleteSearch.setIsStalled(app.isStalled);
autocompleteSearch.setError(app.error);
autocompleteSearch.setContext(app.context);
Algolia presets
Autocomplete.js comes with presets to facilitate the integration with Algolia.
getAlgoliaHits
(options: { searchClient: SearchClient, query: string, searchParameters: SearchParameters[] }) => Promise<Response['hits']>
Function that retrieves and merges Algolia hits from multiple indices.
This function comes with default Algolia search parameters:
hitsPerPage
:5
highlightPreTag
:<mark>
highlightPostTag
:</mark>
import autocomplete, { getAlgoliaHits } from 'autocomplete.js';
import algoliasearch from 'algoliasearch';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
autocomplete({
// ...
getSources({ query }) {
return [
{
// ...
getSuggestions({ query }) {
return getAlgoliaHits({
searchClient,
query,
searchParameters: [
{
indexName: 'instant_search',
params: {
hitsPerPage: 3,
},
},
],
});
},
},
];
},
});
getAlgoliaResults
(options: { searchClient: SearchClient, query: string, searchParameters: SearchParameters[] }) => Promise<MultiResponse['results']>
Function that retrieves Algolia results from multiple indices.
This function comes with default Algolia search parameters:
hitsPerPage
:5
highlightPreTag
:<mark>
highlightPostTag
:</mark>
import autocomplete, { getAlgoliaResults } from 'autocomplete.js';
import algoliasearch from 'algoliasearch';
const searchClient = algoliasearch(
'latency',
'6be0576ff61c053d5f9a3225e2a90f76'
);
autocomplete({
// ...
getSources({ query }) {
return [
{
// ...
getSuggestions({ query }) {
return getAlgoliaResults({
searchClient,
query,
searchParameters: [
{
indexName: 'instant_search',
params: {
hitsPerPage: 3,
},
},
],
}).then(results => {
const firstResult = results[0];
return firstResult.hits;
});
},
},
];
},
});
highlightAlgoliaHit
Highlights and escapes the value of a record.
import autocomplete, { highlightAlgoliaHit } from 'autocomplete.js';
autocomplete({
// ...
templates: {
suggestion({ suggestion }) {
return highlightAlgoliaHit({
hit: suggestion,
attribute: 'name',
});
},
},
});
reverseHighlightAlgoliaHit
This function reverse-highlights and escapes the value of a record.
It's useful when following the pattern of Query Suggestions to highlight the difference between what the user types and the suggestion shown.
import autocomplete, { reverseHighlightAlgoliaHit } from 'autocomplete.js';
autocomplete({
// ...
templates: {
suggestion({ suggestion }) {
return reverseHighlightAlgoliaHit({
hit: suggestion,
attribute: 'query',
});
},
},
});
snippetAlgoliaHit
Highlights and escapes the snippet value of a record.
import autocomplete, { snippetAlgoliaHit } from 'autocomplete.js';
autocomplete({
// ...
templates: {
suggestion({ suggestion }) {
return snippetAlgoliaHit({
hit: suggestion,
attribute: 'name',
});
},
},
});
Design
Search box
<div
class="algolia-autocomplete"
role="combobox"
aria-haspopup="listbox"
aria-labelledby="autocomplete-0-label"
>
<form role="search" novalidate="" class="algolia-autocomplete-form">
<label
for="autocomplete-0-input"
class="algolia-autocomplete-magnifierLabel"
>
<svg>
...
</svg>
</label>
<div class="algolia-autocomplete-loadingIndicator">
<svg>
...
</svg>
</div>
<div class="algolia-autocomplete-searchbox">
<input
id="autocomplete-0-input"
class="algolia-autocomplete-input"
aria-autocomplete="list"
aria-labelledby="autocomplete-0-label"
autocomplete="off"
placeholder="Search…"
type="search"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
maxlength="512"
/>
</div>
<button
type="reset"
title="Clear the query"
class="algolia-autocomplete-reset"
hidden="true"
>
<svg>
...
</svg>
</button>
</form>
</div>
Dropdown
<div class="algolia-autocomplete-dropdown">
<div class="algolia-autocomplete-dropdown-container">
<header class="algolia-autocomplete-header">
Global header
</header>
<section class="algolia-autocomplete-suggestions">
<header class="algolia-autocomplete-suggestions-header">
<h2>Fruits</h2>
</header>
<ul
id="autocomplete-0-menu"
role="listbox"
aria-labelledby="autocomplete-0-label"
>
<li
class="algolia-autocomplete-suggestions-item"
id="autocomplete-0-item-0"
role="option"
tabindex="0"
>
Apple
</li>
<li
class="algolia-autocomplete-suggestions-item"
id="autocomplete-0-item-1"
role="option"
tabindex="0"
>
Banana
</li>
</ul>
<footer class="algolia-autocomplete-suggestions-footer">
Showing 2 out of 10 fruits
</footer>
</section>
<footer class="algolia-autocomplete-footer">
Global footer
</footer>
</div>
</div>
Examples
Browser support
Contributing
Please refer to the contributing guide.
License
Autocomplete.js is MIT licensed.
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago