Ajax Form Validation

Hello people,

I’m working on the following form where i am validating using an Ajax call.

Take a look at the following page

If you click in the required fields and then click out of them it should validate them. Let’s take “First Name” for example. If you click into the text box and then click out of it without filling in any details it will show you a “Enter Details” image…

This is fine, but when i then fill in the details and click away again i am having a problem where the “Enter Details” does not change?

You can locate the ajax JS file here

The Join Now button is disabled by default, it should only be enabled when all the Required fields have been entered.

The ajaxValidatephp code page is shown below:


<?

/*
 * This is the PHP script to validate the form over AJAX
 * Validations types possible:
 *
 * - alpha
 * - alphanum
 * - date
 * - email
 * - number
 *
 */


// Start the main function
StartValidate();

function StartValidate() {
	
	// Assign some var's for the requests
	$required = $_GET["required"];
	$type = $_GET["type"];
	$value = $_GET["value"];

	// This is the function to check if a field is even required or not
	// So it's useful if you only want to check if it isn't empty
	validateRequired($required, $value, $type);

	switch ($type) {
            case 'number':
                    validateNumber($value);
                    break;
            case 'alphanum':
                    validateAlphanum($value);
                    break;
            case 'alpha':
                    validateAlpha($value);
                    break;
            case 'date':
                    validateDate($value);
                    break;
            case 'email':
                    validateEmail($value);
                    break;
	}
}

// The function to check if a field is required or not
function validateRequired($required, $value, $type) {
	if($required == "required") {
            // Check if we got an empty value
            if($value == "") {
                    echo "false";
                    exit();
            }
	} 
        else
        {
            if($value == "") {
                    echo "none";
                    exit();
            }
	}
}

// I use regular expressions in order to check a field's input, you can
// get most of them at the Regex Library at http://www.regexlib.com
// There you can check your own regular expressions, too

// Validation of an Email Address
function validateEmail($value) {
	if(ereg("^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$", $value, $regs)) {
		echo "true";
	} else {
		echo "false";
	}
}

// Validation of a date
function validateDate($value) {
	if(ereg("^(([1-9])|(0[1-9])|(1[0-2]))\\/(([0-9])|([0-2][0-9])|(3[0-1]))\\/(([0-9][0-9])|([1-2][0,9][0-9][0-9]))$", $value, $regs)) {
		echo "true";
	} else {
		echo "invalid";
	}
}

// Validation of characters
function validateAlpha($value) {
	if(ereg("^[a-zA-Z]+$", $value, $regs)) {
		echo "true";
	} else {
		echo "invalid";
	}
}

// Validation of characters and numbers
function validateAlphanum($value) {
	if(ereg("^[a-zA-Z0-9]+$", $value, $regs)) {
		echo "true";
	} else {
		echo "invalid";
	}
}

// Validation of numbers
function validateNumber($value) {
	if(ereg("^[0-9]+$", $value, $regs)) {
		echo "true";
	} else {
		echo "invalid";
	}
}

?>

Can anybody see where i am going wrong?

Even something to point me in the right direction will be much appreciated.

Many thanks,

I’ve managed to solve the initial problem.

Only issue with this now is that the Join Now button enables it self when correcting one field :mad:

If I’m interpreting what your code is doing correctly, you’re enabling the ‘Join Now’ button if there are no errors in the vError array, and you check that if whatever you’re currently validating is valid. The problem is that you start the array off empty, so if the user simply enters their first name, which validates correctly, the join button will be enabled because there won’t be any errors listed in the array.

To fix this, initialize the array to have an error for each of the required fields, which will then be eliminated one by one as the user fills out the form correctly. You may also want to display the error images in those fields as well to start off with, but that’s up to you.

Any chance you could give me a pointer on where this needs to be done? I’m not so sure how to go about doing this,

Do you mean setting it to vError = “”; at the top?

No, I actually mean the opposite. Like I said, because your vError array starts out empty, if the user only enters information into one field, your code will test that field, and if it’s ok, it then checks if the vError array is empty. Since it’s empty, your code assumes that everything is filled in properly, so it enables the ‘Join Now’ button.

Since that isn’t what you want, you need to first initialize the array with all the IDs of the required fields. So, at the top of your code where you define vError, instead of doing this:


vError = [];

It should look like this:


vError = ["billing_first_name", "billing_surname", "email", "billing_address_1", "billing_city", "billing_postcode", "password", "password2"];

Alternatively, since I see that you’re referencing jQuery at the top of the page (though it should probably be updated from 1.3 to 1.6), you can do the following instead, which will always insert an error for every required field, even if you add or take away one or more of them:


vError = [];
$("#registrationForm input:text.required").each(function() {
  vError.push(this.id);
});

Now, with the vError array initialized with an error for each of your required fields, the user must fill in all of them correctly before the ‘Join Now’ button is enabled.

Also, while your declaration of the global variable vId within the function validateIt is valid and successfully makes it available to other functions, it’s easier to read the code if all global variables are declared initially at the top of the script, and maybe with some documentation as to what it’s being used for. Though I don’t think it even needs to be global, I’m guessing the only reason it is is because you can’t pass it to handleHttpResponse in this statement since you’re only allowed to specify the name of the function:


http.onreadystatechange = handleHttpResponse;

A way around that is doing this instead:


var handler = function() {
  handleHttpResponse(vId);
};
http.onreadystatechange = handler;

And then of course telling the handleHttpResponse function it needs to accept a parameter, which you can then use in place of the global variable inside the function.

Also (sorry, this is getting long) you can simplify the code that attaches JavaScript to your input fields using jQuery. Instead of setting the window.onload attribute and using the function getForm(), you can do the following:


$(document).ready(function() {
  $(":input").blur(function() { validateIt(this); });
});

You can put anything else that should be done when the page loads into that document.ready function as well.

Let me know if you have any other questions/problems.

Hello again,

Firstly, thank you so much for helping me out with this i really appreciate it.

OK i have added your code, only thing is now, when i enter the First Name, Last Name, Email Address, Phone Day, Phone Eve the Join Now button becomes enabled :confused:

Not sure as to why this is, could you possibly take a look at the ajax Javascript file as i may have put something in the wrong place?

Do let me know if you spot anything :smiley:

Much appreciated and thanks again…

It looks fine besides one thing: make the handleHttpRequest function take a parameter:


function handleHttpRequest(vId) {
...
}

I was getting an error since vId wasn’t defined since it wasn’t specified as a parameter.

Also, I discovered that the jQuery selector I gave you for getting all the required fields isn’t working, it isn’t selecting any of them. I played around with a few different selectors, and this is the only one that selected all of them correctly:

#registrationForm input[class*=required]”

So put that in place of the other one, which was: “#registrationForm input:text.required”.

Also, in the document.ready function, that selector should probably be “#registrationForm :text” rather than just “:input”, since you only want to attach that event handler when text inputs lose the focus, not every input on the page.

Let me know if these changes work.

You do mean the handleHttpResponse here don’t you? I have tried with your code but still it does not seem to work. Just so you know, in the <head> tag you will notice i do this:


        $('#joinbutton').attr('disabled', 'disabled');
        $('#joinbutton').attr("src", "<?=__SITE_PATH?>images/join-now-button-disabled.png");
        $('#joinbutton').css("cursor", "default");

So that the Join-now button is disabled by default. Should i just do this in the CSS or leave this as is?

Nevertheless the changes i have made to the ajaxFormValidate file don’t seem to be working as intended. Another thing i am cautious about…


$("#registrationForm input[class*=required]").each(function() {
  vError.push(this.id);
});

There are other classes apart from “required”. If we look back at the registration page we will see there are the following:

  1. required
  2. alpha
  3. email
  4. number
  5. alphanum

Would the above makee a difference to the way anything is working from the code you have suggested?

Sorry about this by the way, really appreciate the help :slight_smile:

Oops yes, I meant handleHttpResponse.

Also, a couple things I noticed:

  1. You shouldn’t validate the address line using the type “alphanum”, which makes it only able to contain letters and numbers. Addresses contain spaces and punctuation, too.

  2. In the handleHttpResponse function, when you add an id to the vError array, make sure you only add it if it isn’t in there already. Right now, if I click in and out of one of the required fields five times, its id ends up in the array five times, meaning that if I end up do eventually typing it in correctly, the only way I can remove it from the array completely is by typing it correctly five times. To fix this, just check whether the id is already present in the array (vError.indexOf(vId) will do the trick).

You can leave the stuff in the head as is.

The other classes don’t matter, all that matters are the ones with a class of “required”, regardless of whatever other classes those elements have.

If you make the two changes I noted above, I think it should work. Again, let me know if it doesn’t.

Did you mean like this:


function handleHttpResponse(vId) {
        
    if(http.readyState == 4) {

        if(http.responseText == "none") {
            document[vId].src = siteURL + "images/blank.gif";
        }

        if(http.responseText == "false") {
            document[vId].src = siteURL + "images/error_missing.png";
            vError.indexOf(vId);
        }

        if(http.responseText == "invalid") {
            document[vId].src = siteURL + "images/error_invalid.png";
            vError.indexOf(vId);
        }

        if(http.responseText == "emailexists") {
            document[vId].src = siteURL + "images/cushion.jpg";
            vError.indexOf(vId);
        }
        
        if(http.responseText == "true") {

            document[vId].src = siteURL + "images/error_tick.png";

            // We do a check if our element is in the error array, and if
            // so, we can delete it from the array
            if(vError.indexOf(vId) != -1) {

                var aId = vError.indexOf(vId);
                vError.splice(aId, 1);

                if(vError.length < 1) {
                    $('#joinbutton').removeAttr("disabled");
                    $('#joinbutton').attr("src", siteURL + "images/join-now-button.png");
                    $('#joinbutton').css("cursor", "pointer");
                }
                
            }
        }
    }

I have taken the vError.push(vId); out… But its now acting weird with some fields, think i may may have made changes in the wrong place :confused:

Sorry, that’s my fault, I wasn’t specific enough.

When you call the indexOf method on an array, it returns the index of what you pass to it if the array contains it, or -1 if it doesn’t.

So, what I meant was when you’re about to add an error to the vError array, check to see if it’s already in there using the indexOf method. For example, here’s what you would do in the first check:


if (http.responseText == "false") {
  document[vId].src = siteURL + "images/error_missing.png";
  if (vError.indexOf(vId) == -1) {
    vError.push(vId);
  }
}

Then do the same type of check in the other places that follow, where you check for values of “invalid” and “emailexists”. Also, shouldn’t you be adding an error if the response is “none”? For a required field, wouldn’t a blank input be considered invalid?

That seems to work now :slight_smile:

I had to add this in the Document.Ready as the type=“password” was not being picked up:


$("#registrationForm :password").blur(function() { validateIt(this); });

Works perfectly now, only thing i found is when i changed the address1 class to “required” it doesn’t pick it up. I noticed it’s the only class of this type so maybe the required class on its own does not work? :eek:

Thanks again for your help so far :slight_smile:

Hello again,

I have altered the code slightly to make the AJAX validation functionality re-usable…

All still works as expected but i have realized that input elements with class of type “required” does not work still.

:frowning:

That’s because you need to have another class so the PHP script knows how to validate the data. If you only give an input field the class “required”, the type parameter that you pass to the PHP script is “”, which the PHP doesn’t take into account, so you don’t get any kind of a response that you can work with, which is why the address isn’t working.

When I said not to use “alphanum” for the address, I meant that something else should be used in its place. So, in the address1 element, you should put the class “address” after “required”, separated by a comma. Then, in the PHP script, modify the switch statement by adding the following case to the end of it:


case 'address':
  validateAddress($value);
  break;

Then, just like you have the other validate functions defined, you’ll need to define the validateAddress function as well:


function validateAddress($address) {
  if (preg_match('/^[a-zA-Z0-9\\., ]+$/', $address)) {
    echo "true";
  } else {
    echo "false";
  }
}

Notice how I used preg_match here to test the address against the regular expression and not the ereg function, as the other validation functions have done. That’s because the ereg function is deprecated, so it’s safer to use preg_match. To make your other validation functions safe, just replace the name of the ereg functions with preg_match, and get rid of the third argument to those function calls ($regs) and you’ll be all set.

That should fix the address problem.

Also, I noticed that you’re simply checking the password with ‘alphanum’. Since you probably want your users to have passwords of a certain length, defining a ‘password’ validation type and adding a function in the PHP script that checks for a minimum length would probably be a good idea, just follow the steps I described for adding a validator for the address. You also probably don’t want to limit what the user can enter for a password to just letters and numbers, as some people like to use special characters to make their passwords more difficult to crack. If you choose to stay with just the alphanumerical characters for valid passwords, then make sure to make a note of that near the password fields so that the user knows what they’re allowed to enter.

Doing the same for the first and last names would be good too, since your code currently doesn’t allow for a last name of “O’Neil” since it currently only allows for letters.

I seem to have broken it somehow :frowning: It was working before and i’ve come to it now and it’s not working… Please help…

I was debugging the javascript and tried doing this:


$("#validateForm input[class*=required]").each(function() {
  alert("Hello");
  vError.push(this.id);
});


And i got no alert i think there may be a problem there?

Not sure what i’ve done to break this, i’ve been staring at the same code for too long now and can’t spot anything…

Can you please help? :confused:

I figured it out…

When i changed the code so that i could use the validation in more than one place… I didn’t realise that one of the include files is a popup (When you click on the Login link)

That popup has a similar form and also has email and password. I think it was conflicting with that…

Did you make any other changes besides what I told you to do? I can’t seem to figure it out either, though the selector I gave you is picking up two elements rather than 0, so two alert boxes pop up for me when I run code to have alerts come up showing every id that matches. Your code that shows alerts with “Hello” doesn’t run though.

Like I said earlier, it can’t hurt to update your version of jQuery to make sure that having an older version isn’t what’s causing the problem. In the line of code where you include jQuery in the head section:


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

Change the “1.2.6” to “1.6.2”.

Also, delete the inclusion of jQuery version 1.3.2, there’s no reason to have more than one version loaded.

Oh ok, yeah, that would do it. I’d still encourage you to update your version of jQuery though, since newer versions tend to be faster, have more features, fewer bugs, and better cross-browser support.

Thanks pal :slight_smile:

I will do the jQuery update now :slight_smile: I’m just getting into jQuery it’s absolutely brilliant :smiley:

Ok one thing i have noticed is that the Submit Button sometimes becomes enabled when all of the fields are not filled in…

Would you say there is a better method to take care of this? For e.g. not even showing the Submit button and showing it only when all the fields have been entered? What would be best from a users perspective?

:rolleyes:

Also for the password preg_match, would this do the trick?


function validatePassword($value) {
  if (preg_match("/^[a-z0-9_-]*$/i", $value)) {
    echo "true";
  } else {
    echo "invalid";
  }
}