Halhelms
SIGN UP FOR MY NEWSLETTER

www.savorgold.com is top on wow gold and runescape gold and World of Warcraft gold provider list for trusted services. Their reputation seems to growing by the minute, which isn't surprising because they are one of the safest sellers of Gold. Delivery speed and customer service are very good. They aslo are giving some bonus items depending on the amount of gold you purchase.

 
 
Halhelms

Recent Comments

Recent Entries

RSS

Learning jQuery: Day 4 - A Simple Username Checker

I received an email recently that went like this:

"Hal, love your series on jQuery and ColdFusion, but I wonder could you show a really simple example just to get us started?"

Well...sure! Let's take a common situation and see if we can make the user experience better. We've probably all used (and written) countless registration pages that ask for your username and email. It's a particular pet peeve of mine when sites make me click "Submit" -- only to find out that my preferred username ("bradpitt") is already taken. Often, any information I've entered is lost. Time to stop that.

Let's begin with a very simple signup page:

<form id="myform">
   Username: <input type="text" id="username" name="username"><span id="msg"></span><br />
   Password: <input type="password" id="password" name="password">
</form>

At the bottom of the page, let's append some jQuery magic:

...form here...

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">

<script type="text/javascript">
   $('#username').change(function(){
      $('#msg').empty();
      $.post(
         'UsernameChecker.cfc?method=checkUsername',
         {username : this.value},
         function(response){
            if (response > 0) {
               $('#msg').html('Sorry, already taken');
               $('#username').attr('value', '').focus();
            };
         }
      );
   });
</script>

"Wait!" (I can hear your thoughts.) "That doesn't look so simple!"

Let's dissect it to understand it. Along the way, we'll have a chance to see several jQuery features.

The first line brings in the base jQuery library from Google. (You can also import it from your own site, if you prefer.)

Next, we write our own script. The first line of that script uses the $('#id_of_element_to_be_matched') pattern. It wraps a DOM element with the id of "username" with a jQuery object. We could get the DOM element by itself with some simple JavaScript -- document.getElementById('username') -- but that would lack the jQuery goodness we're after.

So, what do we want to do with this element? When the value changes, we want to do check whether the username entered already exists on the server. If so, we'll alert the user in the span tag with an id of "msg", remove the offending username, and focus the cursor back to the username field.

If the user has already entered a bad username, the "msg" will already be filled, so let's start by removing any existing message with this code:

$('#msg').empty();

As you can see, we used that same selector pattern, applying it this time to the DOM element with an id of "msg". The "empty" method belongs to jQuery.

Now, it's time to involve ColdFusion in this great effort. We do this by using an Ajax call to a CFC method: checkUsername, located in a UsernameChecker CFC. (We'll see that in a moment.) The "$.post" is jQuery shorthand for the Ajax communication with the server using the "post" method. (We could have also used "get".)

In addition to posting to a specific file (you can also post to a .cfm file), I'm sending an argument called "username" with the value of whatever username was entered. The format is JSON. As you can see, it's pretty simple: a name/value pair, separated by a colon. The code, "this.value", provides us with the actual username entered. (If you're a bit confused by what "this" stands for, it points to the DOM element we started with -- the one with an id of "username". Because of the extremely dynamic nature of JavaScript, "this" is one of the harder things to pin down as to exactly what it's referring to. We'll have to pick that advanced topic up in another post.)

After supplying the data to ColdFusion, ColdFusion will return a value (the number of records found matching the username). When that response is returned, I have provided an anonymous function (so-called because it has no name) that I want to run.

function(response){
   if (response > 0) {
      $('#msg').html('Sorry, already taken');
      $('#username').attr('value', '').focus();
   };
}

It takes as its sole argument the response from ColdFusion. If the response is greater than 0, we've found that username in our database already and will fill the "msg" span with a pithy condolence.

Next, we again select the DOM element with an id of "username", wrap it in a jQuery object, and remove its "value" attribute. What, then, are we to make of the next bit: ".focus()"?

This introduces to a nice feature in jQuery: chaining. Having cleared the value in the "username" text box, jQuery returns that element for any more calls we might like to make on it. We have just one: setting the cursor in that text box.

Now, that wasn't too bad -- and like everything, it gets easier and easier with practice until it will feel like second nature. As for the UsernameChecker CFC, it looks like this:

<cfcomponent displayname="UsernameChecker" output="false">

<cffunction name="checkUsername" access="remote" output="false" returnformat="json">
   <cfargument name="username" required="true">
   <cfset var q = "">
   <cfquery datasource="OurDsn" name="q">
   SELECT username FROM users WHERE username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.username#">
   </cfquery>
   <cfreturn q.recordcount>
</cffunction>

</cfcomponent>

Note the use of the 'access="remote"' for our method. That allows us to call a CFC directly from a URL (or via Ajax in our case). As long as our argument name matches the name in the JSON data sent via jQuery, there's nothing else we need to do. Now, we have a choice of return formats to send the recordcount in. If you don't specify anything, ColdFusion will wrap anything you return in a WDDX packet. There are some other ways to handle this, but I recommend you return values in JSON. Very nicely, ColdFusion makes this very easy; we need only append the 'returnformat="json"' to our cffunction tag.

And that's it. Working with jQuery and JavaScript can be confusing at first, but I think you'll find (as I did), that it more than repays the effort by allowing us to make the user experience richer.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Raymond Camden's Gravatar Few notes:

1) I wrote a blog entry using this type of app a while ago for CF8-based Ajax. Rob Brooks-Bilson dinged me - correctly I think - for the security implications. Basically this lets people look up usernames, very quickly, which could then be used for attack. While I'm not completely convinced it as big a deal as others may think, it is probably worth noting. I believe someone on my blog recommended a slight 'slowdown' effect to the CFC - add a sleep() call that would delete things by 400 ms or so. Wouldn't be very noticeable to a normal user, but would slow down a hacker.

2) Ok, now I'm going to get -very- picky. Anyone reading this comment should keep that in mind. :) Technically your frond end code is treating the result from the CFC as plain text, not JSON. Your code works because the JSON version of a number is the number itself. But I think it would be better to use a returnType of plain here. Since your front end considers the result 'as is', your server side should also return the value 'as is'.
# Posted By Raymond Camden | 4/6/09 12:08 PM
Ryan Miller's Gravatar This is great stuff, Hal. Thanks for taking the time to do this series.
# Posted By Ryan Miller | 4/6/09 12:18 PM
Hal's Gravatar @Ray I'm not sure about the security implications either, but I'll defer to Ray on this.

As for the JSON, yes, you're right, but I actually recommend always returning stuff as JSON. Why? Because when people begin with this stuff (and this series is meant for beginners), more choices means more confusion. As the series progresses, we're going to be returning a JSON struct and telling people 'Before, we were using returnformat="plain", but now use returnformat="json"' causes, IMHO, unnecessary confusion. So, I'm cheating a bit, but I think it's for a good cause: making things easier to learn. Later, people can apply greater sophistication in their returnformats.
# Posted By Hal | 4/6/09 2:15 PM
Andy's Gravatar Thanks Hal - love all of your posts and insights... a quick completely off-topic note: I love printing off blog entries such as this for offline reading, and your print link make this very handy indeed.

The good news is that your print function does not cutoff all of the code (like so many others do) but I also love following the comments section as they often add so much to the topic and your print function _does_ cut those out.

A small nit-pick, but seeing as you were writing about making user-experiences better...
# Posted By Andy | 4/6/09 4:30 PM
John Fitzgerald's Gravatar Thanks for the awesome tutorial. I was looking around at jquery and CF8 ajax for this exact thing.

I assume you wouldn't really have just a UsernameChecker.cfc in production but probably some collection of cffunctions in a more generic cfc. Am I right in that assumption or do most people make their cfc's that granular?
# Posted By John Fitzgerald | 4/8/09 3:13 AM
Hal's Gravatar @John,

Right you are, John. This is just demo code to isolate just what I wanted to explain. here. And thanks for the kind words.
# Posted By Hal | 4/8/09 6:54 AM
gammajack's Gravatar Hal,

In your example code, you have a script block that links to the jquery.js file stored google. You failed to close that block, and it was causing malfunction when I tried to run the code. I added a /script tag to the end of the line, and the problem was fixed.
# Posted By gammajack | 4/28/09 2:58 PM
Jeff's Gravatar Stumped entirely. I've set this up as a demo and I'm getting this error:

Error casting an object of type <b>java.lang.Integer cannot be cast to java.lang.String to an incompatible type. This usually
indicates a programming error in Java, although it could also mean you
have tried to use a foreign object in a different way than it was designed.

java.lang.Integer cannot be cast to java.lang.String
# Posted By Jeff | 2/4/11 6:30 PM
 
   
Clicky Web Analytics