Validating a Username via jQuery with Ajax

It all starts when John hits your website and clicks the big 'Register' link. John types his name, '_John_' into the username box, and hands over his e-mail address and password (unless you're cool and hip, and you let him sign up using his OpenID) and hit 'Submit', just like every other website he's signed up to in the past.

Except this time, somebody else called John (what are the chances, eh?) has already signed up using that username, so after waiting a few seconds John sees an error message asking him to select another username. He types a new username and tries 'Submit' again, unsure as to whether his new selection will be suitable. So we fix this problem easily - we tell your users, while they're entering their username, whether their selection is available.

To achieve this we're going to use jQuery with it's fantastic Ajax support.

To get started, we create a simple view in Django that confirms the presence of a given username. This view will be accessible at /check_username/; and will expect a username via a GET paramater.

def checkusername(request):
    from django.contrib.auth.models import User
    from django.http import HttpResponse
    username = request.POST.get('username', False)
    if username:
        u = User.objects.filter(username=username).count()
        if u != 0:
            res = "Already In Use"
        else:
            res = "OK"
    else:
        res = ""

    return HttpResponse('%s' % res)

You'll now find that accessing /checkusername/?username=john_ will return 'Already in Use' or 'OK', as required.

The next thing to do is access it from within your registration form. I use James Bennett's fantastic django-registration with a customised template. This means I don't have to alter the registration code at all! I've added a little element just next to the username field, which we'll update shortly. The username field on my form looks like this:

<dt><label for="id_username">Username:</label></dt>
<dd>{{ form.username }} <span id='username_status'></span> {% if form.username.errors %}<span class="formerror">{{ form.username.errors|join:", " }}</span>{% endif %}</dd>

We've got an empty there called usernamestatus_. Now, when the user types a username, we want to check that username via background AJAX call and update the <span%gt; appropriately.

In the header of your registration form (I use a block in my templates called {% block extrahead %} which is inside the of my base.html) you will need to add a handful of lines of JavaScript:

<script type='text/javascript' src='/media/jquery.js'></script>
<script type='text/javascript'>
var previous_username = '';
var in_ajax = 0;
function checkUsername() {
    username = $("#id_username").val();
    if ((previous_username != username) && (username != '') && (in_ajax != 1)) {
        in_ajax = 1;
        $("#username_status").html("<img src='/media/busy.gif' />");
        $("#username_status").load('/check_username/', {username: username}, function() {in_ajax = 0;});
    }
    previous_username = username;
}
$(function() {
    setInterval("checkUsername()", 1000);
});
</script>

This code is relatively simple. Firstly, it loads jquery.js, which I hope by now you've downloaded and placed into a suitable media directory. Then, using the last 3 lines, it causes the checkUsername() function to be called every second. This function does an AJAX request to our checkusername_ view and puts the response into our usernamestatus_ .

We do a few niceties here, too. Firstly, we make sure the username isn't the same as when we last checked it (no point in checking the same username every second - it would increase server load with no real benefit). We also put a 'busy' image into the status area whilst doing the AJAX call - I use a simple image from ajaxload.info.

Net result? The user gets real-time feedback on whether their username is available, so they don't have to think as hard when the page re-loads with an error message.

Note that this won't stop the user submitting a form with an invalid username - if they do submit it, they'll get an error message as per usual. All this function does is provide some nice feedback to users who wish to use it. Signing up to a website should be easy, after all.


Thanks for reading.

I'd love to hear your feedback and comments. E-mail me, ross@rossp.org, or get in touch on Twitter where I'm @RossPoulton. Chat soon!