0

I want some javascript + ajax to check username availability on fly. And I have some troubles...

Let's imagine the situation when we're checking username availability on blur event.

What if we're getting such scenario...

  • a) user entered username "kir"
  • b) focuses on another element
  • c) ajax request started (requesting availability of "kir" username)
  • d) user focuses on username and enter one more letter "kirA" (response from c) isn't recieved yet)
  • e) focuses on another element
  • f) ajax request started (requesting availability of "kirA" username)
  • g) we're getting answer from c) and telling user that username is available, but it not (because we're telling about "kir", but not "kirA").

What should I do to avoid such situation? Use some kind of randomly generated tokens to check validity of answer?

Thank you

Kirzilla
  • 16,368
  • 26
  • 84
  • 129

7 Answers7

5

You should the XHR when you create a new one.

var ongoingRequest = null;

function test() {
   if(ongoingRequest != null) {
      ongoingRequest.abort();
   }

   ongoingRequest = $.ajax( ... );
}

You could add extra functionality to validate once more when you get the response:

$.ajax({
   ...
   data: { query: query },
   success: (function(q) {
       return function(result) {
          if( $('#myTxt').val() != q) {
             // the textbox value is not the same as the query was
             // at the time of creating this response!
             return false;
          }

          ...
       }
   })(query)
});
David Hedlund
  • 128,221
  • 31
  • 203
  • 222
  • 1
    ahh... your edit is really nice! caching the value of the textbox on clientside and not transmit it... yes... i could have come up with this :) –  Jul 13 '10 at 09:02
5

You need some sort of mapping between the XHR request object and the username query it contained. Or in the server's response to the AJAX query, include the username as well. A response may look like:

{
    username: "kir",
    available: false
}

In the callback, verify if the current textbox value is same as the returned username. If yes, then show the error, otherwise just ignore that response.

Anurag
  • 140,337
  • 36
  • 221
  • 257
  • http://stackoverflow.com/questions/3235517/check-username-availability-on-fly/3235551#3235551 is by far better. nothing additional will be sent/received from server - everything is done on clientside –  Jul 13 '10 at 09:03
  • @Andreas - To me, it doesn't feel like adding the username is overloading the response, but more like a complete response instead. Likewise in an autocomplete query, including the query in the response is a common pattern and a few extra bytes is generally not considered a significant overhead unless you're operating at Google scale. – Anurag Jul 13 '10 at 09:28
  • in this minified request you are right. all i was talking about: be careful, and know what you are doing! –  Jul 13 '10 at 10:04
  • @Andreas - you raised a valid point. We should not clutter up the response just for client-side processing when better alternatives exist. – Anurag Jul 13 '10 at 10:32
3

you just need the right scope of the async-request-variable!
if you create a new request, just check if there's already one, if yes: set it to null/cancel it, and go on!

another variant would be: block the ui, load a spinner-icon, block the input, ... (not that nice solutions...)

edit:
i think ajax/... is a nice thing, but when it comes down to adding more and more properties to a request, you should be a bit careful. even more when you can come up with a nicer solution (and you don't need any explicit ui-interaction like userA is not available)

  • 1
    Example at http://stackoverflow.com/questions/446594/kill-ajax-requests-using-javascript-using-jquery – Nick Jul 13 '10 at 08:54
1

This is what I use for that purpose:

JAVASCRIPT:

$(document).ready(function() {
    $("#username").blur(function() {
        //remove all the class add the messagebox classes and start fading
        $("#username_status").removeClass().addClass('username_status').text('Checking username').fadeIn("slow");
        //verify entered username
        $.post("path to php file",{ user_name:$(this).val() } ,function(data) {
            // username not avaiable
            if (data=='no') {
                $("#username_status").fadeTo(200,0.1,function() {
                    $(this).html('Username in use').addClass('username_status_error').fadeTo(900,1);
                });
            // username available
            }else if (data=='yes') {
                $("#username_status").fadeTo(200,0.1,function() {
                    $(this).html('username available').addClass('username_status_ok').fadeTo(900,1);
                });
            // username field empty
            }else if (data=='empty') {
                $("#username_status").fadeTo(200,0.1,function() {
                    $(this).html('username field is empty').addClass('username_status_error').fadeTo(900,1);
                });
            // username check error
            }else{
                $("#username_status").fadeTo(200,0.1,function() {
                    $(this).html('fail to check username').addClass('username_status_error').fadeTo(900,1);
                });
            }
        });
    });
});

PHP FILE TO POST WITH AJAX:

<?php
    require_once ('db_connect.php'); // include the database connection
    if ($_POST['user_name']!="") {
        // check if username is available from login table
        $query = sprintf(" select * from login where username = '%s' ", mysql_real_escape_string($_POST['user_name']));
        if (mysql_query($query)) {
            if (mysql_num_rows(mysql_query($query))>0) {
                echo "no";
            }else{
                echo "yes";
            }
        }else{
            echo "error";
        }
    }else{
        echo "empty";
    }
?>

AND HTML FORM:

<form name="login-form" id="login-form" class="clients_form" method="post" action="form post link">
  <ol>
    <li>
      <label for="login_usr" class="OF_name">Username:<span class="mandatory">*</span><br></label>
      <input tabindex="1" accesskey="u" name="username" type="text" maxlength="30" id="username">
    </li>
    <li>
      <label for="login_pwd" class="OF_email">Password:<span class="mandatory">*</span><br></label>
      <input tabindex="2" accesskey="p" name="password" type="password" maxlength="15" id="password">
    </li>
    <li class="buttons">
      <input class="btn" tabindex="3" accesskey="l" type="submit" name="cmdlogin" value="Login">
      <input class="btn" name="cancel" type="button" value="Cancel" onclick="location.href='cancel link'">
    </li>
  </ol>
  <br><br><br>
</form>

This is a simple sample that I've created to show you, but should be working fine...

When the user leaves the username field, the AJAX will post the PHP file to verify database, if the user returns to the username field, again, wend it leaves it, Ajax will post.

Hope this helps!

Zuul
  • 16,217
  • 6
  • 61
  • 88
  • damn.. never seen a more specific generic solution ... :( –  Jul 13 '10 at 09:09
  • This doesn't address the problem of several subsequent request and getting their responses in an asynchronous order, which is all the question was about. also, you don't really need to post the value to your server to check if it's an empty string =) – David Hedlund Jul 13 '10 at 09:12
  • he has to, if he wants to be a defensive guy! anyone could run his script and do some injection... –  Jul 13 '10 at 10:03
  • @Andreas Niedermair: the code is still vulnerable to injection. checking for empty strings doesn't change any of that. all i meant was that if the string is empty, you know that before you submit the AJAX request, and can sort of spare yourself the roundtrip. – David Hedlund Jul 13 '10 at 10:45
  • @All, this is a sample code to verify if the username is available, over and over and over... Changes may be needed, but I'm not really developing for Kirzilla, I'm just providing some help :) – Zuul Jul 13 '10 at 11:10
  • @david: that was what i meant ... he's not that defensive :) –  Jul 13 '10 at 11:12
  • Must add, that from my answer and the other answers present, I don't really see any significant difference... (just that my answer is a working example) :) Never the less, just trying to help out Kirzilla! – Zuul Jul 13 '10 at 11:12
0

You could disable the input while the ajax is in progress.

zaf
  • 22,776
  • 12
  • 65
  • 95
0

Or, you can do "g) we're getting answer from c) and telling user that username kir is not available"

Piskvor left the building
  • 91,498
  • 46
  • 177
  • 222
0

Anurag's first suggestion (keeping the request data along with the query) is more optimal for the general case thatn returning the username. If you send several long parameters, you don't want them to unnecessarily use your bandwith and increase response time.

Quick example, keeping the record of what you send:

function checkUserName(){
    var uName=$('#uname').val(); // get it immediately stored into the variable
    $.ajax( {url: '...', data: {username: uName}, success: function(data){
        if(data.is_available)
            alert(uName + " is available."); // this instance of the variable is still in scope, even the checkUserName function has been re-called since
    });
}

This code can still be optimized but you get the idea. If you're using this kind of thing often, you should read about scope in javascript (closures, etc).

instanceof me
  • 38,520
  • 3
  • 31
  • 40