Show Suggestions on Typing using Javascript

Show Suggestions on Typing using Javascript

This is a short tutorial on how to show suggestions when the user types specific keyword in a textbox.

Showing suggestions on typing is something most app does. Here, I'm not talking about search text boxes, which always show you suggestions. (Google search box)

Let's assume that the user is typing in a <textarea>, and we need to show some suggestions when they are typing a username starting from @ (@Supun)

This requires some work to be done. Here's a good approach that I found effective.

  • We have a <textarea> element which takes user input.
  • We listen to the keyup event to check if user is typing what we need.

This article is written in Vanilla JS. You can use a library to manipulate DOM. But, I don't know any popular JS library that provides functions to get and set cursors. So, using Vanilla JS for that would be fine. Let's start!

Before we start, we need to helper functions to deal with cursor positions. (To get and set)

function getCursorPosition(el) {
    var start = 0, end = 0, normalizedValue, range, textInputRange, len, endRange;
    if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
        start = el.selectionStart;
        end = el.selectionEnd;
    } else {
        range = document.selection.createRange();

    if (range &amp;&amp; range.parentElement() == el) {
        len = el.value.length;
        normalizedValue = el.value.replace(/\r\n/g, "\n");

        // Create a working TextRange that lives only in the input
        textInputRange = el.createTextRange();
        textInputRange.moveToBookmark(range.getBookmark());

        // Check if the start and end of the selection are at the very end
        // of the input, since moveStart/moveEnd doesn't return what we want
        // in those cases
        endRange = el.createTextRange();
        endRange.collapse(false);

        if (textInputRange.compareEndPoints("StartToEnd", endRange) &gt; -1) {
            start = end = len;
        } else {
            start = -textInputRange.moveStart("character", -len);
            start += normalizedValue.slice(0, start).split("\n").length - 1;

            if (textInputRange.compareEndPoints("EndToEnd", endRange) &gt; -1) {
                end = len;
            } else {
                end = -textInputRange.moveEnd("character", -len);
                end += normalizedValue.slice(0, end).split("\n").length - 1;
            }
        }
    }
}
return {
    start: start,
    end: end
};

}

function setCursorPosition(input, start, end) { if (arguments.length < 3) end = start; if ("selectionStart" in input) { setTimeout(function() { input.selectionStart = start; input.selectionEnd = end; }, 1); } else if (input.createTextRange) { var rng = input.createTextRange(); rng.moveStart("character", start); rng.collapse(); rng.moveEnd("character", end - start); rng.select(); } }

Then, the textarea element which we are going to listen to. There's no problem if it is a input element. And, the suggestion view where we are going to show the suggestions in.

<textarea id="txt"></textarea>
<div id="suggestions"></div>

Now, some useful variables.

var txt = document.getElementById("txt"),
    suggestions = document.getElementById("suggestions"),
    regex = /@([a-zA-Z0-9]*)$/; // the regex we are going to match

// some fake data var userData = [ { name: "Supun Kavinda", username: "SupunKavinda" }, { name: "John Doe", username: "JohnDoe" }, { name: "Anonymous", username: "Anonymous" } ];

Add the keyup event listener to the textarea.

// listen for @...
txt.addEventListener('keyup', handleKeyUp);

Then, our handler. Here we will match the string before cursor with /@[a-zA-Z0-9]*$/. If matched, we can suggestions of the users taken from a database. (Here I will use some fake data for users)

function handleKeyUp() {
   closeSuggestions();
   var cursor = getCursorPosition(txt),
      val = txt.value,
      strLeft = val.substring(0, cursor.start);

var match = val.match(regex);

if (match) { // fetch suggestions var username = match[1]; findSuggestions(username);
} }

Finding, showing and closing suggestions...

function findSuggestions(username) {

var matched = [];

userData.forEach(function(data) { var dataUsername = data.username, pos = dataUsername.indexOf(username);

if (pos !== -1) {
    matched.push(data);
}

});

// you can also sort the matches from the index (Best Match)

if (matched.length > 0) { showSuggestions(matched); }

}

function showSuggestions(matched) { // DOM creation is not that hard if you use a library ; suggestions.style.display = "block"; suggestions.innerHTML = "";

matched.forEach(function(data) { var wrap = document.createElement("div"); suggestions.appendChild(wrap);

var nameView = document.createElement("span");
nameView.innerHTML = data.name;
nameView.className = "name-view";

var usernameView = document.createElement("span");
usernameView.innerHTML = "@" + data.username;
usernameView.className = "username-view";

wrap.appendChild(nameView);
wrap.appendChild(usernameView);

// add the suggested username to the textarea
wrap.onclick = function() {
    addToTextarea("@" + data.username + " ");
}

}); }

function closeSuggestions() { suggestions.style.display = "none"; }

The function to add the username to the textarea.

function addToTextarea(valueToAdd) {

var cursor = getCursorPosition(txt), val = txt.value, strLeft = val.substring(0, cursor.start), strRight = val.substring(cursor.start);

// remove the matched part
strLeft = strLeft.replace(regex, "");

txt.value = strLeft + valueToAdd + strRight;

// (textarea, positionToAdd)
setCursorPosition(txt, strLeft.length + valueToAdd.length);

txt.focus();

closeSuggestions();

}

Finally, let's add some CSS.

#txt {
  resize:none;
  width:450px;
  height:100px;
  border-radius:5px;
  border:1px solid #eee;
}
#suggestions {
  /* It's better to contain textarea
  and suggestions in a div and make this suggestions view
  absolute positioned */
  box-shadow:0 0 25px rgba(0,0,0,0.05);
  width:450px;
}
#suggestions > div {
  padding:10px;
}
.name-view {
  margin-right:5px;
}
.username-view {
  color: #aaa;
}

You are done :)

Thanks for reading ! learn more...

☞ The Complete JavaScript Course 2019: Build Real Projects!

☞ JavaScript Bootcamp - Build Real World Applications

☞ The Web Developer Bootcamp

☞ JavaScript Programming Tutorial - Full JavaScript Course for Beginners

☞ Best JavaScript Frameworks, Libraries and Tools to Use in 2019

☞ What JavaScript Framework You Should Learn to Get a Job in 2019?

☞ Best JavaScript Frameworks, Libraries and Tools to Use in 2019

☞ Microfrontends — Connecting JavaScript frameworks together (React, Angular, Vue etc)

☞ Do we still need JavaScript frameworks?

javascript typescript web-development

What's new in Bootstrap 5 and when Bootstrap 5 release date?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Random Password Generator Online

HTML Color Picker online | HEX Color Picker | RGB Color Picker

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

Best Web and App Development Company in Ahmedabad

Contact DataPierce for any questions or queries you have about website development, designing, marketing projects or any small or enterprise software development.

Mobile App Development Company India | Ecommerce Web Development Company India

Best Mobile App Development Company India, WebClues Global is one of the leading web and mobile app development company. Our team offers complete IT solutions including Cross-Platform App Development, CMS & E-Commerce, and UI/UX Design.