We cannot currently respond to issues or pull requests.
You can still raise issues or pull requests if you want to. As soon as we’re able to, we will prioritise dealing with any bugs that have been raised by people in the UK public sector. We cannot prioritise adding new features.
accessible-autocomplete
is a JavaScript autocomplete built from the ground up to be accessible. The design goals are:
- Accessibility: Following WAI-ARIA best practices and testing with assistive technologies.
- User experience: Supporting a wide variety of user needs.
- Compatibility: Working with as many browsers, devices, and assistive technologies as possible.
Install it by running:
npm install --save accessible-autocomplete
The accessibleAutocomplete
function will render an autocomplete <input>
and its accompanying suggestions and aria-live
region. Your page should provide a <label>
and a container element:
<label for="my-autocomplete">Select your country</label>
<div id="my-autocomplete-container"></div>
Then import it using a module system like Browserify / Webpack / Rollup, and call the accessibleAutocomplete
function, providing an array of values:
import accessibleAutocomplete from "accessible-autocomplete";
const countries = ["France", "Germany", "United Kingdom"];
accessibleAutocomplete({
element: document.querySelector("#my-autocomplete-container"),
id: "my-autocomplete", // To match it to the existing <label>.
source: countries,
});
If you want to use it as a replacement for a <select>
element, read the Progressive enhancement section.
You can copy the dist/accessible-autocomplete.min.js file to your JavaScript folder and import it into the browser:
<script
type="text/javascript"
src="assets/js/accessible-autocomplete.min.js"
></script>
A stylesheet is included with the package at dist/accessible-autocomplete.min.css.
You can copy it to your stylesheets folder and import it into the browser:
<link rel="stylesheet" href="assets/css/accessible-autocomplete.min.css" />
You can also import it using Sass:
@import "accessible-autocomplete";
If you already use Preact in your application, you can import a bundle that will use that:
import preact from "preact";
import Autocomplete from "accessible-autocomplete/preact";
preact.render(
<Autocomplete id="autocomplete" source={suggest} />,
document.querySelector("#container")
);
If you already use React in your application, you can import a bundle that will use that:
import React from "react";
import ReactDOM from "react-dom";
import Autocomplete from "accessible-autocomplete/react";
ReactDOM.render(
<Autocomplete id="autocomplete" source={suggest} />,
document.querySelector("#container")
);
React v15.5.4 has been tested to work with the Accessible Autocomplete - although make sure to check out documented issues.
React v15.6.2 and 16.0 have been incompletely tested with the Accessible Autocomplete: while no undocumented issues were found, we recommend you carry out thorough testing if you wish to use these or later versions of React.
Type: HTMLElement
The container element in which the autocomplete will be rendered in.
Type: string
The id
to assign to the autocomplete input field, to use with a <label for=id>
. Not required if using enhanceSelectElement
.
Type: Array | Function
An array of values to search when the user types in the input field, or a function to take what the user types and call a callback function with the results to be displayed.
An example of an array of values:
const countries = ["France", "Germany", "United Kingdom"];
If source
is a function, the arguments are: query: string, populateResults: Function
Similar to the source
argument for typeahead.js, a backing data source for suggestions. query
is what gets typed into the input field, which will callback to populateResults
synchronously with the array of string results to display in the menu.
An example of a simple suggestion engine:
function suggest(query, populateResults) {
const results = ["France", "Germany", "United Kingdom"];
const filteredResults = results.filter(
(result) => result.indexOf(query) !== -1
);
populateResults(filteredResults);
}
Type: Boolean
Set to true to highlight the first option when the user types in something and receives results. Pressing enter will select it.
Type: Boolean
The autocomplete will confirm the currently selected option when the user clicks outside of the component. Set to false
to disable.
Type: string
Use this property to override the BEM block name that the JavaScript component will use. You will need to rewrite the CSS class names to use your specified block name.
Type: string
Specify a string to prefill the autocomplete with.
Type: 'inline' | 'overlay'
You can set this property to specify the way the menu should appear, whether inline or as an overlay.
Type: number
The minimum number of characters that should be entered before the autocomplete will attempt to suggest options. When the query length is under this, the aria status region will also provide helpful text to the user informing them they should type in more.
Type: string
The name
for the autocomplete input field, to use with a parent <form>
.
Type: Function
Arguments: confirmed: Object
This function will be called when the user confirms an option, with the option they've confirmed.
Type: string
This option will populate the placeholder
attribute on the input element.
We think placeholders have usability issues and that there are better alternatives to input placeholder text, so we do not recommend using this option.
Type: Boolean
The input field will be rendered with a required
attribute, see W3C required
attribute definition.
Type: Boolean
If this is set to true
, all values are shown when the user clicks the input. This is similar
to a default dropdown, so the autocomplete is rendered with a dropdown arrow to convey
this behaviour.
Type: Boolean
The autocomplete will display a "No results found" template when there are no results. Set to false
to disable.
Templates can be modified to show recent search history by setting recentSearch to true. You will still need to pass in a suggestion source.
source: (query, populateResults) => {
const recentSearches = ["Biology", "Bio"];
const results = [
"Biomes",
"Biodiversity",
"Biology",
"Biomedical",
"Biometrics",
];
const filteredResults = results.filter((result) => {
let name;
if (typeof result === "string") {
name = result;
} else {
name = result.name;
}
return name.indexOf(query) === 0;
});
if (query.length === 0) {
populateResults(recentSearches);
} else {
populateResults(filteredResults);
}
},
});
#### `templates` (default: `undefined`)
Type:
```js
{
inputValue: Function,
suggestion: Function
}
```
This object defines templates (functions) that are used for displaying parts of the autocomplete.
`inputValue` is a function that receives one argument, the currently selected suggestion. It returns the string value to be inserted into the input.
`suggestion` is a function that receives one argument, a suggestion to be displayed. It is used when rendering suggestions, and should return a string, which can contain HTML. :warning: **Caution:** because this function allows you to output arbitrary HTML, you should [make sure it's trusted](https://en.wikipedia.org/wiki/Cross-site_scripting), and accessible.
#### `dropdownArrow` (default: A rectangle pointing down)
Type: `Function`
A function that gets passed an object with the property `className` (`{ className: '' }`) and should return a string of HTML or a (P)React element. :warning: **Caution:** because this function allows you to output arbitrary HTML, you should [make sure it's trusted](https://en.wikipedia.org/wiki/Cross-site_scripting), and accessible.
### Internationalization
#### `tNoResults` (default: `() => 'No results found'`)
Type: `Function`
A function that receives no arguments and should return the text used in the dropdown to indicate that there are no results.
#### `tRecentSearch` (default: `() => 'Recent searches'`)
Type: `Function`
A function that receives no arguments and should return the text used in the dropdown to indicate the most recent searches are available.
#### `tStatusQueryTooShort` (default: `` (minQueryLength) => `Type in ${minQueryLength} or more characters for results` ``)
Type: `Function`
A function that receives one argument that indicates the minimal amount of characters needed for the dropdown to trigger and should return the text used in the accessibility hint to indicate that the query is too short.
#### `tStatusNoResults` (default: `() => 'No search results'`)
Type: `Function`
A function that receives no arguments and should return the text that is used in the accessibility hint to indicate that there are no results.
#### `tStatusSelectedOption` (default: `` (selectedOption, length, index) => `${selectedOption} ${index + 1} of ${length} is highlighted` ``)
Type: `Function`
A function that receives two arguments, the selectedOption and the amount of available options, and it should return the text used in the accessibility hint to indicate which option is selected.
#### `tStatusResults`
Default:
```js
(length, contentSelectedOption) => {
const words = {
result: length === 1 ? "result" : "results",
is: length === 1 ? "is" : "are",
};
return (
<span>
{length} {words.result} {words.is} available. {contentSelectedOption}
</span>
);
};
Type: Function
A function that receives two arguments, the count of available options and the return value of tStatusSelectedOption
, and should return the text used in the accessibility hint to indicate which options are available and which is selected.
tAssistiveHint
(default: () => 'When autocomplete results are available use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.'
)
Type: Function
A function that receives no arguments and should return the text to be assigned as the aria description of the html input
element, via the aria-describedby
attribute.
This text is intended as an initial instruction to the assistive tech user. The aria-describedby
attribute is automatically removed once user input is detected, in order to reduce screen reader verbosity.
If your autocomplete is meant to select from a small list of options (a few hundred), we strongly suggest that you render a <select>
menu on the server, and use progressive enhancement.
If you have the following HTML:
<select id="location-picker">
<option value="fr">France</option>
<option value="de">Germany</option>
<option value="gb">United Kingdom</option>
</select>
You can use the accessibleAutocomplete.enhanceSelectElement
function to enhance it into an autocomplete:
accessibleAutocomplete.enhanceSelectElement({
selectElement: document.querySelector("#location-picker"),
});
This will:
- Place an autocomplete input field after the specified
<select>
- Default the autocomplete
autoselect
totrue
- Default the autocomplete
defaultValue
to the select'soption[selected]
- Default the autocomplete
id
to the<select>
'sid
- Default the autocomplete
name
attribute to''
to prevent it being included in form submissions - Default the autocomplete
source
to use existing<option>
s from the<select>
- Hide the
<select>
using inlinedisplay: none
- Set the
<select>
'sid
to${id}-select
to decouple from any<label>
- Upon confirming a value in the autocomplete, update the original
<select>
This function takes the same options as accessibleAutocomplete
, with the only difference being that it uses selectElement
instead of element
, which needs to be an instance of HTMLSelectElement
.
Note: The
accessibleAutocomplete.enhanceSelectElement
function is fairly light and wraps the public API foraccessibleAutocomplete
. If your use case doesn't fit the above defaults, try reading the source and seeing if you can write your own.
If your <select>
element has a "null" option - a default option with no value - then you can pass a defaultValue
option to enhanceSelectElement
which will replace the label of this option when it is selected.
With the following HTML:
<select id="location-picker">
<option value="">Select a country</option>
<option value="fr">France</option>
<option value="de">Germany</option>
<option value="gb">United Kingdom</option>
</select>
Then passing a defaultValue
option of ''
will then leave the autocomplete blank if the null option is selected.
accessibleAutocomplete.enhanceSelectElement({
defaultValue: "",
selectElement: document.querySelector("#location-picker"),
});
Any null options will also be filtered out of the options used to populate the source
of the autocomplete element. To preserve options with no value in the autcomplete then pass a preserveNullOptions
flag of true
to enhanceSelectElement
.
The following events get triggered on the input element during the life cycle of the autocomplete:
onConfirm
- This function will be called when the user confirms an option, with the option they've chosen.
Example usage:
accessibleAutocomplete({
// additional options
onConfirm: (val) => {
track(val);
},
});
accessible-autocomplete
was built after studying many existing solutions and prototyping patches to fix user experience or accessibility issues. It draws heavy inspiration from the following (and a lot of others):
- ljwatson/design-patterns: great accessible experience
- corejavascript/corejs-typeahead: flexible autocomplete/suggestion engine architecture
- JamieAppleseed/selectToAutocomplete: ease of use
Check out the CONTRIBUTING guide for instructions.
If you want to help and want to get more familiar with the codebase, try starting with the "good for beginners" issues.
MIT.