Create an ASP.NET MVC Autofill Control, Part 2

In Part 2 of our autofill control, we attempt to create a transparent placeholder in an ASP.NET MVC textbox.

Written by Jonathan "JD" Danylko • Last Updated: • MVC •
Truck filling in a pothole (taken from YouTube)

In Part 1, we demonstrated how to create an autofill control using HTML and simple JavaScript/jQuery.

In today's post, I dig further into how to achieve an autofill with a "permanent placeholder" as my client asked.

The Situation

The issue with this concept is the user's input will be regular text (normal text as always) and the remaining company name text (the hint) will be gray.

In a TextBox, you can't change color in mid-text. You can highlight the text as we did in the previous post.

I thought "Ok, can we change the highlight to make it look transparent and gray?" After researching this idea, I came up blank.

Strike one.

Ok, Hmm...How about?...

What if I took a label and placed it exactly under the TextBox?

That may work.

The Approach

We need to contain the label inside the autofill because we'll be using both relative and absolute positioning with CSS.

The autofill container will be relative positioning while the input will be absolute.

Our HTML will have a similar layout, but include one more DOM element: the autofill-text (in bold).

<div class="form-group">
    <div class="row">
        <div class="col-md-4">
            @Html.Label("New Company", "New Company:")
            <span class="autofill">
                <span class="autofill-text"></span>
                @Html.TextBox("CompanyPlaceholder", String.Empty, new
                {
                    @class = "form-control input-sm"
                })
            </span>
            <p class="help-block">Enter the company name (i.e. Apple, Microsoft).</p>
        </div>
    </div>
</div>

We want the text to be it's regular color in the textbox (black?) with the ".autofill-text" label shown below it in a gray color.

This will require some CSS for our positioning of elements.

<style>
    .autofill {
        positionrelative;
    }

    .autofill,     .autofill input {         backgroundnone;         displayblock;         padding5px 10px;     }
    .autofill .autofill-text {         font-weightnormal;         color#BBB;         height30px;         font-size12px;         line-height1.5;         /* strictly for placement purposes */         padding-left1px;     }
    .autofill input {         left0;         positionabsolute;         /* strictly for placement purposes */         top1px;     } </style>

A couple of warnings with this approach.

  • The .autofill .autofill-text class needs to duplicate your target textbox's height, line-height, padding, font style, font weight...everything to exactly match your textbox's specifications. This text will be placed behind the textbox. I recommend using Google's Chrome DevTools, Firefox's Dev Tools, or Edges Dev Tools (all accessed using F12) to find the CSS properties of a textbox and copy them into the .autofill-text class.
  • Both classes (".autofill .autofill-text" and ".autofill input") may require some tweaking to make the hinted text (.autofill-text) line up to the textbox. I've already placed some positioning comments in the CSS (/* strictly for placement purposes */).

As I mentioned, we are containing the absolute and relative positioning to a closed off DOM element. This shouldn't affect any other element on the page.

The Killing Blow

Naturally, we need JavaScript for this to work (again!).

In this post, we are only changing the autofill-text by setting the HTML inside of it.

// 2. Use a grayed out "placeholder"
var company2 = $("#CompanyPlaceholder");

$(company2).delayKeyup(function () {
    $(".autofill-text").html("");
    var partialCompanyName = $(company2).val();
    // if there's nothing there, don't do anything. if (partialCompanyName.length === 0) {         return false;     }
    // grab company names based on the partial     $.getJSON("/api/Services/" + partialCompanyName)         .done(function (data) {             if (data) {                 var returnedCompany = data[0];
                var upperLowerVendor = returnedCompany.substr(0, partialCompanyName.length);
                $(company2).val(upperLowerVendor);                 $(".autofill-text").html(returnedCompany);             }         })         .fail(function () {             console.log("error");         }); }, 1000);

We slice off a substring of the valid company returned based on the length of the user input and set it as the value in the textbox.

Finally, we place the company in the .autofill-text using the html(). The CSS handles the rest.

You should see the following:

Animation of autofill in progress

But there are some UI problems I didn't anticipate.

Overlapping Characters

If someone's really fast on the keyboard and they backspace fast enough and then type a letter, the letter overlaps the hint's character making it look a little..uhh..weird.

We are using the keyup event for our delay. How can we fix this?

By simply clearing out the .autofill-text when a user presses a key.

$(company2).keypress(function () {
    $(".autofill-text").html("");
});

Issue resolved.

Plus it looks cleaner.

Losing Focus Loses It's Value (in a manner of speaking)

When a user types a partial company name and presses tab to move forward with their choice, the textbox would keep only the partial text in the textbox.

I was racking my brain trying to determine if I should use a "if this key...do this."

But then I thought, don't fall into the trap of detecting the tab key. While it can be done, there is a simpler way to fix this.

Use the onBlur event when the user leaves the textbox.

$(company2).blur(function () {
    var value = $(".autofill-text").html();
    if (value !== "") {
        $(company2).val(value);
    }
});

Grab the value from the autofill-text and place it into the company textbox when focus is lost.

Conclusion

I've been trying to come up with a different way to achieve this in CSS and JavaScript. This seems to be the only way to achieve this feat.

If someone has a better approach, I would appreciate any input (no pun intended) or feedback regarding this technique.

So far, it seems to be working across one of my applications.

Is there a better approach? Post your comments below and let's discuss.

ASP.NET 8 Best Practices on Amazon

ASP.NET 8 Best Practices by Jonathan Danylko


Reviewed as a "comprehensive guide" and a "roadmap to excellence" with over 120 Best Practices for ASP.NET Core 8, Jonathan's first book by Packt Publishing explores proven techniques for every phase of the SDLC.

Learn industry-standard concepts to improve your coding, debugging, and deployment of ASP.NET Core websites.

Order now on Amazon.com button

Picture of Jonathan "JD" Danylko

Jonathan "JD" Danylko is an author, web architect, and entrepreneur who's been programming for over 30 years. He's developed websites for small, medium, and Fortune 500 companies since 1996.

He currently works at Insight Enterprises as an Architect.

When asked what he likes to do in his spare time, he replies, "I like to write and I like to code. I also like to write about code."

comments powered by Disqus