Create an Autocomplete Bar With Accessibility in Angular
Building autocomplete for those who use computers differently
It’s very easy to understand the functionality of common tools used on the web. A common oversight but essential part of developing these tools is to ensure that they are accessible to everyone. Like most other engineers, I was taught to build tools to be functionally sound. Accessibility has always been an afterthought.
“You might also think of accessibility as treating everyone the same, and giving them equal opportunities, no matter what their ability or circumstances. Just as it is wrong to exclude someone from a physical building because they are in a wheelchair (modern public buildings generally have wheelchair ramps or elevators), it is also not right to exclude someone from a website because they have a visual impairment. We are all different, but we are all human, and therefore have the same human rights.” — MDN Web Docs
Let’s go over the requirements that we would like to meet:
- As a user, I would like to be able to type in a search for movies.
- As a user, I would expect a list of movies to be shown after a short delay during typing.
- As a user, after the list of movies is shown, I should be able to use the arrow keys to navigate the list without changing my focus.
Building the component:
Here’s an explanation of the attributes that you don’t normally see:
aria-autocomplete
— Indicates what to expect when inputting text into the control.role
— Indicates that this element has a combination of another element that may dynamically pop up to help the user with setting the text.attr.aria-expanded
— Self-explanatory, but simply put, it highlights when the list of movies is showing.autocomplete
— Tells the browser how to handle the autocomplete of forms. Here, we set it tonone
so we can clearly see the movie results without an overlapping autocomplete based on previous inputs.
Now we need to describe how the results will be shown:
For the input, we added:
aria-owns
— Gives a contextual/functional relationship between the input search as the parent and theul li
elements as the child.aria-activedescendant
— Indicates which element in the list is active while the focus is on our combo input box.
To be semantically accurate, we also added ul
and li
elements that should show the list of movies.
We should now have something like this:
I won’t go over the API aspect of it where you type and get results. This component’s responsibility is to show the movies whenever the parent passes it down.
Secret Sauce: Angular CDK
We already have our input and the movies will be shown whenever the parent passes it down. So now we need to be able to navigate with the up and down arrow keys and select with enter.
This directive allows us to do a few things. Firstly, this implements Highlightable
, which is a class from the @angular/cdk/a11y
lib. It is used in conjunction with ActiveDescendantKeyManager
(see below), which is also from @angular/cdk/a11y
. The main purpose of this is to correctly handle keyboard events across multiple browsers via a single API. You can read more about this on Angular Material’s website.
Secondly, this directive will be on each movie result. As such, we have an active class to identify.
Lastly, we have a getItem
function that we can access from our autocomplete when the user selects an item.
<li role="option" appLiAutocomplete *ngFor="let movie of movies">
{{ movie }}</li>
We update our autocomplete component to work the directive via ActiveDescendantKeyManager
. This update will query our template for our directive. The withWrap
function ensures that if we are at the end of the list, it will start back at the top (and vice versa).
Lastly, on our keyup
event for our input, we want to update this to handle arrow keys in addition to when the user is searching.
- Lines 2-5 ensure that if we are pressing the arrow keys, the text inside the input won’t emit another event to the parent to start the search.
- Line 6 allows us to get the item that is currently highlighted.
- Line 7 checks if the event key pressed is the enter key, which will output to the parent as the selected item.
- Line 10 will handle if the user pressed the up or down arrows, which item needs to be highlighted, and if the item needs to wrap up or down.
And that’s pretty much it!
Using this approach gives you the separation of concerns and you can easily extend this accessibility for each li
element by updating the directive.
Additionally, the benefit of adding this accessibility will be really appreciated for many users who are non-visual or use assistive technology when they are on the web.
See the full code on Stackblitz and check me out on Twitter or LinkedIn