jQuery – Ensuring Unique Usernames in Real Time

A question came up yesterday on the cf-talk mailing list. The question involved a user who is filling out a form and choosing a user name. The poster wanted to check in “real time” (after the blur event on the username input) that the name is unique.

As Steve “Cutter” Blades points out, this can be accomplished using Jörn Zaefferer’s Validation Plugin for jQuery. This plugin is essentially the defacto standard for doing validations via jQuery. It can be implemented very easily to do the most basic validations, but it can also be extended to do any number of complex validations.

That being said, I thought it still might be worthwhile to see a quick example of how to build out that specific functionality from scratch. How can we use jQuery to give a user real-time feedback as to whether or not a username is available? Start with the markup below:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title></title>

	<style type="text/css">
		body	{ font-family:arial,verdana,helvetic; font-size:12px; }
		label	{ width:80px; font-weight:bold; display:block; margin-right:8px; padding-top:3px; float:left; }

		.formcontainer { display:block; float:left; clear:both; margin-bottom:8px; }

		#usernameResponse	{ color:red; font-weight:bold; padding-left:8px; display:none; }
	</style>
</head>

<body>

<form>
	<div class="formcontainer">
		<label>User Name:</label>
		<input type="text" name="username" id="username" />
		<span id="usernameResponse">Sorry, that username is taken&lt;/span>
	</div>

	<div class="formcontainer">
		<label>Password:</label>
		<input type="password" name="password" id="password" />
	</div>

	<div class="formcontainer">
		<input type="submit" id="submit" value="Submit" />
	</div>
</form>

</body>
</html>

We’ve got a standard form, with a few styles applied. After the username input field, there’s a span with a message indicating “Sorry, that username is taken”. By default that message is hidden via CSS. The class “usernameResponse” has a property of display:none.

Now we are going to add the jQuery:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js">&lt;/script>
<script>
	$( document ).ready( function() {
		$( '#username' ).blur( function() {
			$.ajax({
				type: "POST",
				url: "checkUser.cfc?method=checkUsername&returnformat=plain",
				data: "username=" + this.value,
				success: function( response ) {
					if ( response == 0 ) {
						$( '#usernameResponse' ).css( 'display', 'none' );
						$( '#submit' ).attr( 'disabled', '' );
					} else {
						$( '#usernameResponse' ).css( 'display', 'inline' );
						$( '#username' ).select();
						$( '#submit' ).attr( 'disabled', 'disabled' );
					}
				}
			});
		});
	});
</script>

The first line includes the jQuery library from Google. Then the fun begins.

The field that triggers the event has an ID attribute of “username”, and we want the event to trigger on blur. When the blur event happens, we call the built in .ajax() method. The method takes a few parameters:

  • type: In this case, we’re going to POST data to the server. GET is an option as well.
  • url: The URL to post to. We’re sending data to a CFC method (the method is passed as a URL parameter and the method’s access attribute is set to “remote”).
  • data: key/value pair of data to post. We’re sending a variable of “username” and passing the value of the username input field.
  • success: On a successful post, a callback function to fire. The function takes an argument (response) which is the value returned from the post. In this case it’s a recordcount. The method simply does a query to check if the username exists, and returns the recordcount. If it’s 0 (no matching records), ensure that the span with the error message is hidden and make sure the submit button is enabled. Otherwise, if it’s any value other than 0 (the username exists), display the error message and disable the submit button.

That’s it. Bear in mind that any client-side validation should be performed as a courtesy to the user only. Client side validation should NEVER be considered a substitute for server side validation. If the user doesn’t have JavaScript enabled, this form should still degrade nicely. The span with the error message will be hidden by default (via CSS), and since the blur() event will never fire, there’s no indication to the user that they’re “missing out” on anything. But on the server side, you would still need to do the proper validation to ensure a unique username.

If you want to run the sample, it involves 2 files (posted below for your copying-and-pasting pleasure). A cfm file and a CFC. There’s no ColdFusion happening in the .cfm, so it could just as easily be an .html for the purposes of this example. The CFC simulates a database call by creating a query via queryNew(). Also, for the purposes of this example, the CFC should be in the same directory as the calling .cfm page.

The names John, Paul, George, and Ringo will trigger the error message.

CheckUser.cfc:

<cfcomponent output="false">

	<cffunction name="getAllUsernames" access="private" returntype="query" output="false">

		<cfset var qGetAllUsernames = queryNew('userID,username') />

		<cfset queryAddRow(qGetAllUsernames, 4) />

		<cfset querySetCell(qGetAllUsernames, 'userID', 1, 1) />
		<cfset querySetCell(qGetAllUsernames, 'userName', 'John', 1) />
		<cfset querySetCell(qGetAllUsernames, 'userID', 2, 2) />
		<cfset querySetCell(qGetAllUsernames, 'userName', 'Paul', 2) />
		<cfset querySetCell(qGetAllUsernames, 'userID', 3, 3) />
		<cfset querySetCell(qGetAllUsernames, 'userName', 'George', 3) />
		<cfset querySetCell(qGetAllUsernames, 'userID', 4, 4) />
		<cfset querySetCell(qGetAllUsernames, 'userName', 'Ringo', 4) />

		<cfreturn qGetAllUsernames />
	</cffunction>

	<cffunction name="checkUsername" access="remote" output="false">
		<cfargument name="username" type="string" default="" />

		<cfset var qAllUsernames = getAllUsernames() />

		<cfquery name="qCheckUsername" dbtype="query">
			SELECT * FROM qAllUserNames WHERE upper(username) = &lt;cfqueryparam value="#ucase(arguments.username)#" cfsqltype="cf_sql_varchar" />
		</cfquery>

		<cfreturn qCheckUsername.recordcount />
	</cffunction>

</cfcomponent>

checkUser.cfm:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<title></title>

	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
	<script>
		$( document ).ready( function() {
			$( '#username' ).blur( function() {
				$.ajax({
					type: "POST",
					url: "checkUser.cfc?method=checkUsername&returnformat=plain",
					data: "username=" + this.value,
					success: function( response ) {
						if ( response == 0 ) {
							$( '#usernameResponse' ).css( 'display', 'none' );
							$( '#submit' ).attr( 'disabled', '' );
						} else {
							$( '#usernameResponse' ).css( 'display', 'inline' );
							$( '#username' ).select();
							$( '#submit' ).attr( 'disabled', 'disabled' );
						}
					}
				});
			});
		});
	</script>

	<style type="text/css">
		body	{ font-family:arial,verdana,helvetic; font-size:12px; }
		label	{ width:80px; font-weight:bold; display:block; margin-right:8px; padding-top:3px; float:left; }

		.formcontainer { display:block; float:left; clear:both; margin-bottom:8px; }

		#usernameResponse	{ color:red; font-weight:bold; padding-left:8px; display:none; }
	</style>
</head>

<body>

<form>
	<div class="formcontainer">
		<label>User Name:</label>
		<input type="text" name="username" id="username" />
		<span id="usernameResponse">Sorry, that username is taken</span>
	</div>

	<div class="formcontainer">
		<label>Password:</label>
		<input type="password" name="password" id="password" />
	</div>

	<div class="formcontainer">
		<input type="submit" id="submit" value="Submit" />
	</div>
</form>

</body>
</html>