← Back toMirror a text area

Add autocomplete to your text area

Written byPhuoc Nguyen
Created
28 Sep, 2023
Last updated
04 Oct, 2023
Autocomplete is a super helpful feature that can save users lots of time and effort when filling out forms or typing in text areas.
For example, when filling out an address form, autocomplete can suggest the complete address as the user types in the first few letters or numbers of their street, city, or zip code. This not only saves time but also ensures accuracy.
Another great use for autocomplete is when searching for products on an e-commerce website. As the user types in their query, autocomplete can suggest relevant product names or keywords that match their search criteria. This makes it easy to find what you're looking for quickly and easily.
Autocomplete can also be a lifesaver in fields like email addresses, usernames, and phone numbers where there may be a limited set of valid options to choose from. By suggesting possible options as the user types, autocomplete makes it easier to enter accurate information without having to remember exact details.
In this post, we'll walk you through the steps to implement autocomplete functionality in a text area using JavaScript. Get ready to level up your user experience!

Finding the current word

To enable our autocomplete feature, it's crucial that we track the current word being typed in a text area. We can accomplish this by utilizing the `input` event and the `selectionStart` property.
Here's how it works: when the user types into the text area, we can retrieve the value of the text area using `textarea.value` and find the index of the cursor using `textarea.selectionStart`. With these values, we can iterate backwards through each character until we find either a space or a newline character. This gives us the start index of the current word.
We've created a handy `findIndexOfCurrentWord` function that returns the beginning index of the current word. Here's a sample code snippet to help you visualize how it works:
js
const findIndexOfCurrentWord = () => {
// Get current value and cursor position
const currentValue = textarea.value;
const cursorPos = textarea.selectionStart;

// Iterate backwards through characters until we find a space or newline character
let startIndex = cursorPos - 1;
while (startIndex >= 0 && !/\s/.test(currentValue[startIndex])) {
startIndex--;
}
return startIndex;
};
Once we have the index of the current word, we can easily extract it using string manipulation.
js
textarea.addEventListener('input', () => {
const currentValue = textarea.value;
const cursorPos = textarea.selectionStart;
const startIndex = findIndexOfCurrentWord();

// Extract just the current word
const currentWord = currentValue.substring(startIndex + 1, cursorPos);
});

Finding matching words

Finding matching words is a breeze once we have the current word. We simply loop through the `suggestions` array and check if each suggestion contains the word currently entered in the text area.
js
const suggestions = [
'white', 'yellow', 'blue', 'red', 'green', 'black', 'brown', 'azure', 'ivory', 'teal',
];
const matches = suggestions.filter((suggestion) => suggestion.indexOf(currentWord) > -1);

Building the suggestions

To display our suggestions, we'll loop through the `matches` array and create a new `div` element for each match. We'll set the text content of each `div` to match the suggestion and add a class to style it properly.
To replace the current word with a new one, we'll add an event listener to each suggestion `div` that listens for a `click` event. When the user clicks on a suggestion, we'll get the text content of that `div` and replace the current word in our text area with it.
Here's some sample code:
js
suggestionsEle.innerHTML = '';
matches.forEach((match) => {
const option = document.createElement('div');
option.innerText = match;
option.classList.add('container__suggestion');
option.addEventListener('click', function() {
replaceCurrentWord(this.innerText);
suggestionsEle.style.display = 'none';
});
suggestionsEle.appendChild(option);
});
This code replaces the current word with the selected suggestion and removes all suggestions from the page. The `replaceCurrentWord()` function updates the value of our text area to replace the current word with the new one.
js
const replaceCurrentWord = (newWord) => {
const currentValue = textarea.value;
const cursorPos = textarea.selectionStart;
const startIndex = findIndexOfCurrentWord();

const newValue = currentValue.substring(0, startIndex + 1) +
newWord +
currentValue.substring(cursorPos);
textarea.value = newValue;
textarea.focus();
textarea.selectionStart = textarea.selectionEnd = startIndex + 1 + newWord.length;
};
In the function above, we've set focus back on our text area and move the cursor to the position after the new word is inserted. To do this, we'll use the `selectionStart` and `selectionEnd` properties, which represent the starting and ending positions of the selected text in a text area or input field. We'll set both properties to the same value to move the cursor to the new position.

Position the suggestions element

To position the suggestions element, we can use the same technique as we did to position the caret element in the previous post. We can get the coordinates of the current cursor using the `getBoundingClientRect()` method on our caret element. Then, we can use those coordinates to set the `top` and `left` properties of our suggestions element.
Check out this code snippet for an example:
js
const rect = caretEle.getBoundingClientRect();
suggestionsEle.style.top = `${rect.top + rect.height}px`;
suggestionsEle.style.left = `${rect.left}px`;
Using this technique, we can ensure that our suggestions element is always positioned directly beneath the current word, which makes it easier for users to select from our list of suggested words.
We want our autocomplete feature to be as user-friendly as possible. One way to achieve this is by allowing users to navigate between suggested items using the arrow keys. This makes it easy for them to quickly scan through our list of suggestions without having to move their mouse or touchpad.
To make this possible, we can add event listeners for the `keydown` event on our text area. This will allow us to check whether the user has pressed one of the supported keys, such as the up and down arrow keys. If they have, we'll update a variable that keeps track of which suggestion is currently focused.
Users can also select a suggestion by pressing the Enter key or close the suggestion list by pressing the Escape key.
Here's some sample code to help you visualize this idea:
js
let currentSuggestionIndex = -1;
textarea.addEventListener('keydown', (e) => {
if (!['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(e.key)) {
return;
}

const suggestions = suggestionsEle.querySelectorAll('.container__suggestion');
const numSuggestions = suggestions.length;
if (numSuggestions === 0) {
return;
}
e.preventDefault();
switch (e.key) {
case 'ArrowDown':
// ...
break;
case 'ArrowUp':
// ...
break;
case 'Enter':
// ...
break;
case 'Escape':
// ...
break;
default:
break;
}
});
In this code snippet, we've added an event listener for the `keydown` event on our text area. We then check if the user has pressed certain keys (like down, up arrow, Enter, or Escape) using their respective `key` property.
If they have, we prevent the default action of the key (which is usually to move the cursor or scroll the page) and retrieve all our suggestion elements using `document.querySelectorAll('.container__suggestion')`. After that, we check if we have any suggestions available and update our `currentSuggestionIndex` variable based on which arrow key was pressed.
For instance, when users press the down arrow key, we execute the following code:
js
suggestions[
clamp(0, currentSuggestionIndex, numSuggestions - 1)
].classList.remove("container__suggestion--focused");
currentSuggestionIndex = clamp(
0,
currentSuggestionIndex + 1,
numSuggestions - 1
);
suggestions[focusedSuggestionIndex].classList.add("container__suggestion--focused");
To make sure that our current suggestion index is always valid, we use a handy `clamp` function. This function needs three things: a minimum value, the value we want to clamp, and a maximum value. Then, it gives us back the clamped value, which is guaranteed to be no less than the minimum and no greater than the maximum. Easy peasy.
js
const clamp = (min, value, max) => Math.min(Math.max(min, value), max);
We use a function in our code to make sure that `currentSuggestionIndex` doesn't go beyond the number of suggestions or below 0. This helps us avoid errors caused by out-of-bounds indices and ensures that our autocomplete feature works correctly.
To make the user experience even better, we highlight the currently selected suggestion by adding a special class (`container__suggestion--focused`) to it. At the same time, we remove that class from the previously selected suggestion.
You have the power to customize how the current focused item looks and feels. For instance, in this example, we added a different background color to make it stand out from the other items.
css
.container__suggestion--focused {
background: rgb(226 232 240);
}
Now that this code is in place, users can effortlessly navigate between suggestions using only their keyboard. This means our autocomplete feature is even more user-friendly and accessible.

Demo

Let's take a look at the final demo.

Conclusion

By following these simple steps, you can easily implement autocomplete functionality in a text area using JavaScript. Of course, this is just a basic example - you have the freedom to customize the suggestions, the appearance of the dropdown menu, and the behavior of the text area to fit your specific needs.
To take things up a notch, you could also add shortcuts, such as using the up and down arrows to navigate between suggestions. And when a suggestion item is selected, pressing Enter could be the same as clicking it. With a little creativity, the possibilities are endless!
If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn