views:

1318

answers:

6

Hello everyone, I am having trouble with my javascript. It seems to be acting oddly. This is what's going on. I have a form, after the user submits it, it calls a function(onsubmit event) to verify the submitted data, if something bad OR if the username/email is already in database(using ajax for this part) it'll return false and display errors using DOM. Here's the code below. What's weird about it, is that it only works when I use an empty alert('') message, without it, it just doesn't work. Thanks for the help.

//////////////////////////////////////

function httpRequest() {
    var xmlhttp;

    if (window.XMLHttpRequest) {
     // code for IE7+, Firefox, Chrome, Opera, Safari
     xmlhttp=new XMLHttpRequest();
    } else if (window.ActiveXObject) {
     // code for IE6, IE5
     xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    } else {
     alert("Your browser does not support XMLHTTP!");
    }

    return xmlhttp;
}

function validateRegForm(reg) {

    var isValidForm = true;
    var warningIcon = "";//for later in case we want to use an icon next to warning msg

    with(reg) {


     var regFormDiv = document.getElementById("registration_form");

     //Check if dynamic div exist, if so, remove it
     if(document.getElementById('disp_errors') != null) {
      var dispErrorsDiv = document.getElementById('disp_errors');
      document.getElementById('reg_form').removeChild(dispErrorsDiv);
     }   

     //Dynamically create new 'div'
     var errorDiv = document.createElement('div');
     errorDiv.setAttribute('id','disp_errors');
     errorDiv.setAttribute('style','background-color:pink;border:1px solid red;color:red;padding:10px;');
     document.getElementById('reg_form').insertBefore(errorDiv,regFormDiv);




     var xmlhttp = httpRequest();
     var available = new Array();
     xmlhttp.onreadystatechange = function() {
      if(xmlhttp.readyState == 4)
      { 
       var response = xmlhttp.responseText;
       if(response != "") {

        //Return values
        var newValue = response.split("|");
        available[0] = newValue[0]; 
        available[1] = newValue[1]; 
       }
      }
     }

     xmlhttp.open("GET","profile_fetch_reg_info.php?do=available&un="+u_username.value+"&email="+u_email.value+"",true);
     xmlhttp.send(null);


     alert(' '); ////////////WITHOUT THIS, IT DOESN'T WORK. Why?

     if(available[1] == "taken") {
      errorDiv.innerHTML += warningIcon+'Username is already taken!<br />';
      isValidForm = false;
     } else if(u_username.value.length < 4){
      errorDiv.innerHTML += warningIcon+'Username must be more than 4 characters long!<br />';
      isValidForm = false;
     } else if(u_username.value.length > 35) {
      errorDiv.innerHTML += warningIcon+'Username must be less than 34 characters long!<br />';
      isValidForm = false;
     }


     if(available[0] == "taken") {
      errorDiv.innerHTML += warningIcon+'Email address entered is already in use!<br />';
      isValidForm = false;
     } else if(u_email.value == ""){
      errorDiv.innerHTML += warningIcon+'Email address is required!<br />';
      isValidForm = false;
     } else {
      //Determine if email entered is valid
      var filter = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
      if (!filter.test(u_email.value)) {
       errorDiv.innerHTML += warningIcon+'Email entered is invalid!<br />';
       u_email.value = "";
       isValidForm = false;
      }
     }


     if(u_fname.value == ""){
      errorDiv.innerHTML = warningIcon+'Your first name is required!<br />';
      isValidForm = false;
     }

     if(u_lname.value == ""){
      errorDiv.innerHTML += warningIcon+'Your last name is required!<br />';
      isValidForm = false;
     }



     if(u_password.value.length < 4){
      errorDiv.innerHTML += warningIcon+'Password must be more than 4 characters long!<br />';
      isValidForm = false;
     } else if(u_password.value.length > 35) {
      errorDiv.innerHTML += warningIcon+'Password must be less than 34 characters long!<br />';
      isValidForm = false;
     } else if (u_password.value != u_password2.value) {
      errorDiv.innerHTML += warningIcon+'Password and re-typed password don\'t match!<br />';
      isValidForm = false;
     }


     if(u_squestion.value == ""){
      errorDiv.innerHTML += warningIcon+'A security question is required!<br />';
      isValidForm = false;
     }

     if(u_sanswer.value == ""){
      errorDiv.innerHTML += warningIcon+'A security answer is required!<br />';
      isValidForm = false;
     }

     if(u_address.value == ""){
      errorDiv.innerHTML += warningIcon+'Address is required!<br />';
      isValidForm = false;
     }

     if(u_city.value == ""){
      errorDiv.innerHTML += warningIcon+'City is required!<br />';
      isValidForm = false;
     }

     if(u_state.value == ""){
      errorDiv.innerHTML += warningIcon+'State is required!<br />';
      isValidForm = false;
     }

     if(u_zip.value == ""){
      errorDiv.innerHTML += warningIcon+'Zip code is required!<br />';
      isValidForm = false;
     }

     if(u_phone.value == ""){
      errorDiv.innerHTML += warningIcon+'Phone number is required!<br />';
      isValidForm = false;
     }

     if(isValidForm == false)
      window.scroll(0,0);

     return isValidForm;
    }

}
A: 

You're sending the request asynchronously, because of this:

xmlhttp.open(..., true);

Passing true as the third argument to open() means that the code will continue to run before the result comes back.

The alert() is giving that asynchronous request time to complete before the subsequent code runs.

I'd normally recommend moving all the code that depends on the result of the AJAX call into the callback, within the:

if(xmlhttp.readyState == 4)

block, but in your case you need the result of the call to know what to return from your validation function. In that case, you're going to have to use a synchronous call, by passing false to open(). Then your subsequent code won't run until the response has come back.

RichieHindle
What do you recommend to resolve this issue?
Erik
I think you have pass the 3rd parameter as false to make it synchronous.
Arun P Johny
@Arun, nope I had tried that, no luck.
Erik
A: 

That is because your AJAX call is asynchronous. The code following xmlhttp.send() is executed immediately. It doesn't wait for your ajax call to end (unless you put the alert).

Move all the code to be executed after the ajax call to the callback method to ensure it is run after the call is finished.

[Edit]: Aside from the problem, I think using AJAX just to validate the form and then allowing the user to submit the usual way seems a bit odd to me.

Can you divide the validation into 2 parts? One that can be done purely on the client side and the other which involves the server-side validation. You can do the second part completely on server-side upon submit and return the errors back to client.

Another option is to avoid the traditional submit altogether. Since you are doing the ajax call anyway, why don't you do the save/edit/whatever in the ajax call itself removing the need for a submit? Keep in mind though, you will still need to support the regular submit if the user has javascript disabled.

Chetan Sastry
Well the only part I'm using AJAX for is the check Username and email availability from the db.
Erik
A: 

These guys are right but I would also be sure to return the bool from the validation method so the submission of the form is cancelled if the form is not valid.

Something along the lines of:

<form onsubmit="return IsValid();">

...

</form>

You could also always return false from validateRegForm and have the xmlhttp.onreadystatechange = function() submit the form if it is valid, or otherwise display your error message.

David Christiansen
+1 Valid point. Why the downvote?
Chetan Sastry
Thanks for the suggestion David, but I should have mention I do have it as a return in the onsubmit event.
Erik
As a sidenote this may be useful advice, but is completely unrelated to the the question at hand
Adam Batkin
+1  A: 

The problem is that XMLHTTPRequest is asynchronous - it sends the request in the background and doesn't wait for it to finish.

The alert statement causes the code to wait until the user clicks OK, during which the request finishes.

You need to use the onreadystatechange event, like this:

xmlhttp.onreadystatechange = function() {
    if(xmlhttp.readyState==4) {
        // Do things
    }
};

The method you assign to this property will be called after the response is received. (and at other times, which is why you need to check that readyState is 4)

SLaks
+1  A: 

The alert() helps because that delays the processing of the remaining javascript in that function (everything from the alert() down to the bottom), leaving enough time for the AJAX request to complete. The first letter in AJAX stands for "asynchronous" which means that (by default) the response will come in at "some point in the future" but not immediately.

One fix (which you should not implement) is to make the processing synchronous (by changing the third argument of open() to be false) which will stop further processing of your script (and the entire webpage) until the request returns. This is bad because it will effectively freeze the web browser until the request completes.

The proper fix is to restructure your code so that any processing that depends on the result of the AJAX request goes in to the onreadystatechange function, and can't be in the main function that initiates the AJAX request.

The way this is usually handled is to modify your DOM (before the AJAX request is sent) to make the form readonly and display some sort of "processing" message, then in the AJAX response handler, if everything is okay (the server responded properly and validation was successful) call submit() on the form, otherwise make the form wriable again and display any validation errors.

Adam Batkin
A: 

You have to pass the third parameter to xmlhttp.open() as false, then it will work fine

Arun P Johny
Although it works, this isn't the ideal solution since it hangs up the browser for the duration of the call.
Chetan Sastry
When I tried that, it didn't work, not even with the alert().
Erik