jQuery tutorial: Text box hints

Updated March ’08: I’ve separated the plugin to a separate jquery.hint.js file and included a fix to get around Firefox’s autocomplete

You will see a lot of web sites with search boxes have text already populated inside of the field and when you select the input text box it disappears and reappears when it’s not selected.

This tutorial will show you how can add a small amount of jQuery to add this feature to any of your web sites.

The Goal

  1. To create a jQuery plugin.
  2. Show a ‘hint’ inside the input box when it is not in focus†† (aka blurred).
  3. Hide the hint when the text input has focus.

† The hint is what I am calling the text that appears and disappears within the input box.

†† Focus is the term (in this case) to indicate the user’s cursor is in the text field. Blurred is the opposite.

Demo

Our demo shows a input box for a finance web site that allows the user to search by company name or by the symbol (ticker).

See the demo in action

How it will work

  1. We are going to get the hint from the ‘title’ attribute of the input box.
  2. When the input box has focus we will hide the hint – only if the text in the input box matches the title attribute.
  3. When the input box is blurred, and it doesn’t contain any text, we will show the text again.
  4. If the input box is blurred and does contain text, we won’t do anything.

Unobtrusive JavaScript

The first step is to design the HTML so that it will work sensibly without JavaScript turned on, i.e. we’re not going to put our hint in the input box. Since we are planning to put the hint in the title tag we’re set to code.

The Code

HTML


<form action="">
    <div><label for="search">Search:</label>
    <input type="text" name="seach" value="" id="search" title="Company name or ticker" />
    <input type="submit" value="Go" />
    </div>
</form>

Pretty simple – nothing unexpected there.

jQuery Plugin

jQuery.fn.hint = function (blurClass) {
  if (!blurClass) { 
    blurClass = 'blur';
  }

  return this.each(function () {
    // get jQuery version of 'this'
    var $input = jQuery(this),

    // capture the rest of the variable to allow for reuse
      title = $input.attr('title'),
      $form = jQuery(this.form),
      $win = jQuery(window);

    function remove() {
      if ($input.val() === title && $input.hasClass(blurClass)) {
        $input.val('').removeClass(blurClass);
      }
    }

    // only apply logic if the element has the attribute
    if (title) { 
      // on blur, set value to title attr if text is blank
      $input.blur(function () {
        if (this.value === '') {
          $input.val(title).addClass(blurClass);
        }
      }).focus(remove).blur(); // now change all inputs to title

      // clear the pre-defined text when form is submitted
      $form.submit(remove);
      $win.unload(remove); // handles Firefox's autocomplete
    }
  });
};

Here’s a breakdown of some of what’s going on:

return this.each(function() {

Ensure we are applying the plugin to all the matched elements and allowing our plugin to be chained.

var $input = jQuery(this)

Creating a cached copy of the jQuery object so we can use jQuery’s functions for testing, without the overhead of continuously making new jQuery objects.

We’re using a $ symbol as a prefix to the variable to give a visual que that the variable contains jQuery functions, rather than a plain DOM element.

$input.blur(function(){

When the element loses focus – execute the function that has been passed in. In our case, we are testing whether the field has been left blank, and if it has – we set it to the title attribute (cached earlier).

We are also adding a class called ‘blur’ to the input box so as to give the user the impression that the text that appears in the box has not been entered by them. Note that we remove this class when the element takes focus.

$input.blur()

We end up with a final call to the blur method – so that all the matched elements are set by default as blurred (i.e. with the title attribute appearing in the text already).

If you’re curious, you can read more about writing jQuery plugins.

Where to take it next

Within the plugin, you could add better validation to ensure we are only applying to input elements where type is ‘text’.

Update June ’07: You can view and use the label over plugin for an accessible version of the ‘text hints’

There are a couple of places you could look at to improve the plugin. The first that I can think of is to apply the label element to float inside the element (as seen on Digg’s search and explained over at A List Apart: Making Compact Forms More Accessible).

The second upgrade you could make would whether you can use this kind of plugin for a select input type. I haven’t thought it through, but it could be interesting.

Let me know if you have any comments or need to point out any errors.

87 Responses to “jQuery tutorial: Text box hints”

  1. Doh! I forgot to include how to apply the plugin:

    $('input:text').hint();

  2. Hey dude where’s your hints on the leave a reply form ;)
    Seriously thanks alot, planning to give your code a try.
    Thanks for the link to the ALA article also.

  3. LOL! Good point. Do as I say, not as I do ;-)

  4. Great, exactly what i needed.
    Would be nice to see how to pass arguments to functions, expecially a settings-array, and how to apply it to the function.
    But this is a great start for me.

  5. I just stumbled upon this page while looking for exactly this function. However, I know nothing about jQuery.

    Where do the various pieces go in relation to the form input? So far i’m seeing the raw text of the jQuery come up.

    Thanks

  6. thank you, I have been looking for something like this for a looong time.

    Pz

  7. Minor problem – is there supposed to be a last closing bracket in the end? I didn’t work for me until I removed it. Makes sense to me, as it doesn’t seem to close anything.

  8. Oh, one more thing. You should add “t.addClass(‘blur’);” after “t.blur()”, which causes them all to obtain the .blur class upon initiation. Use that class in your CSS to style the text while in its “hint mode”. That way you can get, for example, gray text for hints but regular black text while something else is being typed.

  9. @Steax – thanks for the catch – I’ve removed the bracket now.

    On your second point – you don’t need to, and shouldn’t add the t.addClass(‘blur’) after for these reasons:

    1) On calling t.blur() – we’ve set a custom handler to add the blur class if there’s no text.
    2) If we did add the blur as a last thing, it might blur the text if there is already content in it (i.e. Firefox keeps textboxes populated if the page is refreshed, or if it were dynamically populated).

    Thanks again.

  10. Oh, alright, gotcha. Thanks for the script. =)

  11. A minor enhancement – removes any values that match the title when the form is submitted.

    t.parents('form:first()').submit(function(){
      if (t.val() == title) {
        t.val('');
        t.removeClass('blur');
      }
    })
  12. GreyCells, where are you supposed to add that function?

  13. @Josh – I’ve added GreyCell’s suggestion in to the tutorial code so you can see where it belongs.

  14. Thanks heaps! just what I was looking for. However is there a way of applying this to a Password input field as well so that it initially appears as asterisks/dots?

  15. Thanks for the hint!!! It was really helpful. I’m beginning to study jQuery )

  16. If the input is same to the title, what will happen?

  17. @earsea – nothing! Give it a try and see.

  18. works great! I am curious if this could be editted some how to work on a textarea box?

  19. @Sam – unless I’m missing something, this does work on textareas. I’ve used it in the past, and it works exactly the same way it would on an input field…give it a try.

  20. Couldn’t this be just as easily done without a plugin?

    // #search is the id of the textbox in this example
    // This still uses the value of the title attribute
    $('#search').focus(function() {
      // See if the input field is the default
      if ($(this).val() == $(this).attr('title')) {
        $(this).val('');
      }
    })
    .blur(function() {
      if ($(this).val() == '') {
        $(this).val($(this).attr('title'));
      }
    });

  21. @Everah, absolutely (except your example doesn’t cover all the possibilities, but I understand the essence of your point).

    If you’ve got one web site/solution that uses the functionality in one place, then there’s no point in using a plugin.

    However, I personally use the ‘hint’ plugin throughout a lot of my own work, so it’s easy for me to include it in my plugins library and when I need it, in my main.js, I can just add:

    $('#search, #username, input.hint').hint(); // etc

    It just saves having to tweak the code each time I use it (i.e. in your code you’d need to change the selector it runs from).

  22. Thanks! This is just what I was looking for.

  23. Thanks Remy – just what I was looking for. Minor issue – when using firefox v2, both in my own usage and your demo example, I noticed that if you refresh the browser, the hint still appears but loses it’s grey color. When you have a lot of input boxes on a page, this looks slightly strange, as the hints stand out too much. It this a bug or just a browser peculiarity? Weirdly the labelOver plugin does not share the same issue.

  24. @Al – thanks for pointing that out.

    It’s the autocomplete feature working on Firefox that’s sticking the value in. I’ve updated the demo so that the input has the autocomplete="off" attribute which will kill the issue.

    The reason the labelOver plugin doesn’t have the same effect, is because the hint plugin actually puts a value in the input field, whereas labelOver places the label directly over the input field (so Firefox doesn’t remember it as a value).

    I hope that helps.

  25. @Remy – thanks very much – that does indeed sort it and helps my problem solving massively.

    Unfortunately due to the auto-complete attribute not being valid XHTML, plus other issues specific to my use of it (using a javascript date picker alongside, which inherits the grey text class unfortunately), I am going to try your suggestion of using the labelOver plugin, which has the benefit of being accessible too.

    But for those not fussed over XHTML compliance, this does indeed sort things!

  26. @Al – I’ve written a nice simple fix to get around the autocomplete problem, and the XHTML remains valid. You can download the plugin from here: hint.jquery.js

  27. i don’t know why but i’m getting an error ..
    $ is not defined
    $(function(){

  28. @Steve – either you’re not importing jQuery, or you’re importing the plugin before you’re importing jQuery. Put the jQuery library as the first thing you do in the JavaScript and all should be well.

  29. Is there any reason that hinting doesn’t support a dynamic title change? I.e. if the title attribute is changed via JS, hint() will continue hinting the previous title tag…

    Cheers.

  30. Going on… I thought the issue might be that when you change the title attribute the contents of the field no longer match and therefore it doesn’t see it as a hint – that you would just need to remove the existing value prior to changing the title… However that doesn’t seem to be the case.

    At this stage, this seems to have solved my issue though it would be good to know if there was an easier way…:

    Unbind blur + focus events

    $.fn.unhint = function(){
      return this.each(function () {
        var $$ = $(this);
        $$.unbind('focus').unbind('blur');
      });
    }

    On change of field execute a function called page, change title attribute, unbind, rebind and clear val()…

    
    $('#paging .custom').change(function(){
      page($(this).val());
      $(this).attr("title","Page "+pageNumber+" of n").unhint().hint().val("");
    });
    
  31. hi, is there any way to change the input title from “by name or ticker” to something else? very nice script by the way, it got me started on learning jquery. well, thanks.

  32. ok, please disregard, dumb question, i know… i guess my browser had some refresh issues, because when i closed the window and opened it again, the obvious solution worked just fine. sorry, my bad. again, nice script.

  33. One problem I see is when the user enter a text that is the same as the hint, it will get removed again. The following patch should fix this (by using the blur class as a hint, whether the input’s value is a hint or user entered text:

    
    @@ -29,14 +29,14 @@
    
                 // on focus, set value to blank if current value matches title attr
                 .focus(function () {
    -                if ($$.val() == title) {
    +                if ($$.hasClass(blurClass)) {
                         $$.val('').removeClass(blurClass);
                     }
                 })
    
                 // clear the pre-defined text when form is submitted
                 .parents('form:first').submit(function () {
    -                if ($$.val() == title) {
    +                if ($$.hasClass(blurClass)) {
                         $$.val('').removeClass(blurClass);
                     }
                 }).end()
    
  34. @wd – funny, I was thinking about exactly that problem about 30 minutes ago. I’ll that change in shortly.

    Thanks!

  35. WISH LIST ITEM:

    How difficult would it be to allow this to accept a LIST of words, and use jQuery effects to transition from word to word on a specified interval??

    I might want 6 words to get people some examples of things to search for, or our popular searches from the database. I would want them to fade in fade out and cycle through the words in the list.

    make sense?

    Awesome plugin so far, BTW.

  36. [...] Style Sheet Switcheroo 21. Safer Contact Forms Without CAPTCHA’s 22. Photo Slider Tutorial 23. Text box hints 24. 5 JavaScript Tricks Made Easy with jQuery 25. JQuery Pop-up Menu Tutorial 26. A Quick Code [...]

  37. When I open your demo, the input has class="blur" and the text inside is gray. After refresh (F5) the class is gone and text color is black.

    Please update the script so the class="blur" won’t disappear after refresh.

    Thanks!

  38. From what I can tell it doesn’t actually work in textareas, any ideas Remy?

    -k00k

  39. Hi Remy,

    Thank you for the very helpful plug-in. Quick question:

    I have scenarios where the form fails to submit, due to validation failure. Your remove() function is called the hints dissappear. If the form fails to submit, how can I prevent them from being removed, or, barring that, how can I re-add the hints to the fields?

    Thanks.

  40. Hi Remy,

    Nice work, clean and simple! After submitting data I’d like to be able to clear the inputs a get the hints back in there. Do I just cal the .hints() function again. I do not get the hints back in there after I explicitly clear the inputs following submitting data.

    Thanks, -b

  41. So I am completely new to javascript so I am somewhat lost. What I did was create the test file that was given above.

    and the jquery listed above in a seperate file..jquery.js

    
    jQuery.fn.hint = function () {  return this.each(function (){    // get jQuery version of 'this'    var t = jQuery(this);     // get it once since it won't change    var title = t.attr('title');     // only apply logic if the element has the attribute    if (title) {       // on blur, set value to title attr if text is blank      t.blur(function (){        if (t.val() == '') {          t.val(title);          t.addClass('blur');        }      });      // on focus, set value to blank if current value       // matches title attr      t.focus(function (){        if (t.val() == title) {          t.val('');          t.removeClass('blur');        }      });      // clear the pre-defined text when form is submitted      t.parents('form:first()').submit(function(){          if (t.val() == title) {              t.val('');              t.removeClass('blur');          }      });      // now change all inputs to title      t.blur();    }  });}
    

    However when I did this all I got was the normal text box; no hint inside. Please view at link listed below.
    I know there must be more to this, but I have no idea how to get it to work. I uploaded all of it at: http://www.lastoptionmedia.com/test.html and the jquery file is at: http://www.lastoptionmedia.com/jquery.js, if you would like to take a look at either of those.

    Any help would be greatly appreciated!

    Thank you,
    Brandon Hempel

  42. A wonderful plugin, Remy. One use case I can’t figure out: go to your demo page, type some text, and hit ‘Go’. Now click the back button in your browser. The hint does show/hide as it is supposed to. Not major, but something I can’t figure out. Jason

  43. awesome, tried out this plugin and it works perfect with no problems! just leaving my thanks.

  44. Thanks from me too – it seems like every time I go looking for a jQuery script I find lots of options but I usually find exactly the functionality I’m looking for on your site…

  45. Love your plugin!

    However, one question… Has anyone used this with ASP.NET? I’m trying and it seems that ASP.NET’s built in form submission does not respect this plugin trying to clear out the hint text before submitting.

    $form.submit(remove);
    …never gets fired before ASP.NET does its postback…

    Thanks for any advice.

  46. Thanks for this tutorial, exactly what I was looking for!

  47. I tried the jquery.hint.js file and it worked great except that the auto-complete fix and clearing pre-defined text was not working for me until I changed the remove function. I only had to change this.value to $input.val()

    
    function remove() {
            	if ($input.val() === title && $input.hasClass(blurClass)) {
                    $input.val('').removeClass(blurClass);
            	}
            }
    

    Thanks!

  48. Very nice plugin and got it working quickly. The only issue I’m a bit challenged by is how I use it on a input box with where type=”password” and where I’d like to show the word “password” instead of the bullets. Can anyone provide a solution?

  49. Just wanted to say thanks for the plugin, I love it and am using it on a few different websites for the college I work for.

  50. Great plugin, it does not work with jQuery 1.3 though. Here is the fix:

    t.parents("form:first()").submit(function(){

    should be:

    t.parents("form:first").submit(function(){

    Enjoy.

Leave a Reply
Not required

CODE: Please escape code and wrap in <pre><code>, doing so will automatically syntax highlight