Autocomplete Text Input

Develop a text input that suggests possible completions as the user types, pulling from a predefined list of options.

Solution

Explanation

Input and Autocomplete Logic

The input field allows users to type in the name of a programming language. The component has a state for the input value and a highlightedSuggestion, which holds the first matching suggestion from the programmingLanguages array.

As the user types, the handleChange function is called, updating the input value and filtering the array for languages that match the current input. If any match is found, the first one is displayed as the suggestion.

Handling Autocomplete Suggestions

The suggestion system works by comparing the user's input to the start of each item in the programmingLanguages array. The match is case-insensitive and the first match is set as the autocomplete suggestion, which is visually hinted to the user.

If no match is found, or the input field is empty, the highlightedSuggestion is cleared, and no hint is displayed. This ensures that irrelevant suggestions are not shown.

Handling Keyboard Events

The handleKeyDown function captures theTab key press event. If the user presses Tab and there is a suggestion available, the input is automatically filled with the suggestion.

The function also prevents the default tab behavior to avoid shifting focus when auto-completing the input. This makes the suggestion process more fluid for the user.

CSS Styling for Autocomplete

The CSS controls both the input field and the autocomplete suggestion. The autocompleteWrapper ensures the suggestion is positioned correctly and doesn't interfere with the input. The suggestion is styled to overlay on top of the input field, with part of the text displayed in light grey to indicate what will be completed.

The input text that has already been typed is set to transparent to visually merge with the suggestion. This gives the user the illusion that they are typing, while the suggestion helps guide them to complete the input.

Code

import { useState } from "react";
import styles from "./Solution.module.css";

const programmingLanguages = [
  "C",
  "C#",
  "C++",
  "Go",
  "Java",
  "JavaScript",
  "Kotlin",
  "Perl",
  "PHP",
  "Python",
  "R",
  "Ruby",
  "Rust",
  "SQL",
  "Swift",
  "TypeScript",
];

const Solution = () => {
  const [input, setInput] = useState("");
  const [highlightedSuggestion, setHighlightedSuggestion] = useState("");

  const handleChange = (value: string) => {
    setInput(value);
    const updatedList = programmingLanguages.filter((item) =>
      item.toLowerCase().startsWith(value.toLowerCase())
    );

    // Set the first suggestion for the autocomplete hint
    if (updatedList.length > 0 && value) {
      setHighlightedSuggestion(updatedList[0]);
    } else {
      setHighlightedSuggestion("");
    }
  };

  const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
    // When the Tab key is pressed
    if (event.key === "Tab" && highlightedSuggestion) {
      event.preventDefault(); // Prevent focus shift
      setInput(highlightedSuggestion); // Set the input to the full suggestion
      setHighlightedSuggestion(""); // Clear the autocomplete suggestion
    }
  };

  return (
    <>
      <label className={styles.label} htmlFor="language">
        Input your main programming language
      </label>
      <div className={styles.autocompleteWrapper}>
        <input
          onChange={(event) => handleChange(event.target.value)}
          onKeyDown={handleKeyDown}
          value={input}
          className={styles.input}
          type="text"
          id="language"
        />
        {highlightedSuggestion && (
          <div className={styles.autocompleteSuggestion}>
            <span className={styles.inputText}>{input}</span>
            <span className={styles.greyText}>
              {highlightedSuggestion.slice(input.length)}
            </span>
          </div>
        )}
      </div>
    </>
  );
};

export default Solution;

Styling

.input {
  margin-top: 10px;
  padding: 5px;
  position: relative;
  background: white;
  border: 2px solid black;
  z-index: 0;
  width: 100%;
  font-size: inherit;
  font-family: inherit;
  color: black;
}

.label {
  font-weight: bold;
}

/* Autocomplete styles */

.autocompleteWrapper {
  position: relative;
  display: inline-block;
  width: 100%;
}

.autocompleteSuggestion {
  margin-top: 10px;
  border: 2px solid transparent;
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  font-family: inherit;
  font-size: inherit;
  padding: 5px;
  width: 100%;
  z-index: 1;
}

.inputText {
  color: transparent;
  padding: 0;
}

.greyText {
  color: lightgrey;
}

Links