5
\$\begingroup\$

This is my first ever attempt at Ajax form validation, and everything works as expected, the final true example will hold a lot more input fields than in my testing scripts which are below.

The file is expected to get a lot bigger as in my testing I have only used 3 input fields, how can I improve on this:

HTML file

<form action="#" method="post">
<fieldset>
<legend>Login Details</legend>
<label>Your Email:</label><input id="email" name="email" type="text" onblur="return ajax ('email');" />
<label>Password:</label><input id="password" name="password" type="text" onblur="return ajax ('password');" />
</fieldset>
<fieldset>
<legend>Account Details</legend>
<label>Your Username:</label><input id="username" name="username" type="text" onblur="return ajax ('username');" />
</fieldset>

<input class="submit" type="submit" value="Create Account" />
</form>

As you can see in the event of an onblur on the input fields I call the function ajax. I also send with the function a parameter which is exact to the id. Here is my function:

JavaScript

function ajax (id) {

var value = $('#' + id).val();
$('#' + id).after('<div class="loading"></div>');

if (id === 'email') {

    $.post('http://www.example.com/ajax.php', {email: value},

    function (response) {
        $('#email_error, #email_success').hide();
        setTimeout(function(){
            $('.loading').hide();
                finish_ajax (id, response);
        }, 1000);
    });
}

if (id === 'password') {

    $.post('http://www.example.com/ajax.php', {password: value},

    function (response) {
        $('#password_error, #password_success').hide();
        setTimeout(function(){
            $('.loading').hide();
                finish_ajax (id, response);
        }, 1000);
    });
}

if (id === 'username') {

    $.post('http://www.example.com/ajax.php', {username: value},

    function (response) {
        $('#username_error, #username_success').hide();
        setTimeout(function(){
            $('.loading').hide();
                finish_ajax (id, response);
        }, 1000);
    });
} 

return false;
}

function finish_ajax (id, response) {

$('#' + id).after(response).fadeIn(2000);
} 

Here is what I have tried to do:

  1. With the id that I send with the function I assign the value of the input field to a variable named value.

  2. Using the same id I then assign a loading div which contains a gif image in my css.

  3. Depending on the id send the correct request with the correct name so I can determine in my php file the correct validation is done on each of the input fields. Here is my php file.

PHP file

if (isset($_REQUEST['email'])) {

$q = $dbc -> prepare("SELECT email FROM accounts WHERE email = ?");
$q -> execute(array($_REQUEST['email']));

if (!filter_var($_REQUEST['email'], FILTER_VALIDATE_EMAIL)) {
    echo '<div id="email_error" class="error">This is not a valid email!</div>';
}

elseif ($q -> rowCount() > 0) {
    echo '<div id="email_error" class="error">Email already owns an account!</div>';
}
else {
    echo '<div id="email_success" class="success">Success!</div>';
}
}

elseif (isset($_REQUEST['password'])) {

if (strlen($_REQUEST['password']) < 6) {
    echo '<div id="password_error" class="error">Your password is not long enough.</div>';
}

else {
    echo '<div id="password_success" class="success">Password Success!</div>';
}
}

elseif (isset($_REQUEST['username'])) {

$q = $dbc -> prepare("SELECT username FROM accounts WHERE username = ?");
$q -> execute(array($_REQUEST['username']));

if (strlen($_REQUEST['username']) < 3) {
    echo '<div id="username_error" class="error">Has to be at least 3 characters</div>';
}   

elseif ($q -> rowCount() > 0) {
    echo '<div id="username_error" class="error">Username already taken</div>';
}
else {
    echo '<div id="username_success" class="success">Username available</div>';
}
}

else {

header('Location: http://www.projectv1.com');
exit();
}

Here I check the name of the request and perform the correct validation.

I have tried to do this as best I can and there may be many errors in it performance-wise which I am unaware of.

\$\endgroup\$
3
\$\begingroup\$

Here's a few things I can observe at first glance:


HTML

  1. form elements don't accept input fields as direct child’s, so you should place your submit button inside a wrapper element.

  2. I don't know about your visual aspect, but you should use CSS to format your elements and keep HTML to a minimum.

  3. Your password field should have the type password as to allow the browser to obfuscate the characters being typed.

  4. If you're using jQuery, you're better of going with the .blur() event instead of giving onblur attribute to all elements subjected to validation.

Your HTML form would become something like:

<form id="myForm" action="#" method="post">
  <fieldset>
    <legend>Login Details</legend>
    <label>Your Email:</label>
    <input id="email" name="email" type="text" />
    <label>Password:</label>
    <input id="password" name="password" type="password" />
  </fieldset>
  <fieldset>
  <legend>Account Details</legend>
    <label>Your Username:</label>
    <input id="username" name="username" type="text" />
  </fieldset>
  <p>
    <input class="submit" type="submit" value="Create Account" />
  </p>
</form>


JQUERY

Your jQuery currently uses the same code over and over, the best option is always to make scripts as generic as possible, to minimize the amount of code that as to be downloaded and interpreted by the browser. Also, in future maintenance, you'll find it easy to deal with a small generic code then a large element specific one.

  1. As mentioned above, with the jQuery .blur() function, you can bind the event to all inputs inside the target form and start creating a more generic validation solution.

  2. Instead of having an if statement to ascertain the element subject to validation, send to the PHP file the field collected and its value.

  3. No need for specific error elements for each input subjected to validation, use classes and target then thru the input id attribute, that in conjunction with a generic class for messages gives your the target element for the user messages.

  4. If you are appending the element .loading to the form, you should also remove it when not needed. If the user keeps triggering the blur in the same input field, the element .loading keeps being added and you end up with tons of them.

Your jQuery code to validate the above form would become something like:

$('#myForm input').blur(function() {

  var id = $(this).attr("id");
  $('#' + id).after('<div class="loading"></div>');

  $.post('http://www.example.com/ajax.php', {field:id, value:value}, function (response) {
    $('.'+id+'.msg').hide();
    setTimeout(function() {
      $('.loading').remove();
      finish_ajax (id, response);
    }, 1000);
  });

});

function finish_ajax (id, response) {
  $('#' + id).after(response).fadeIn(2000);
} 


PHP

Your PHP code falls under the same issues as the jQuery code, that is, repeating several chunks of code that latter on will prove difficult to maintain.

  1. If you're using a POST method, check for it and the specific variables necessary to execute the script, otherwise, bailout.

  2. Perform some cleanup to the values before using them.

  3. Since you're querying the same table, having the field passed along with the value, proves useful to have a single database line to maintain instead of several across the script.
    You can choose to mascaraed the field name if your really want to obfuscate what's being done.

  4. When using many If elseif else statements, the best option is to migrate to a switch case statement. Easy to maintain, read and "upgrade".

  5. If the HTML that this script is outputting is the same, changing only the text and a class, best is to make use of variables to store the desirable values and have one single like with the script output.
    Will prove useful when dealing with problems, having to "upgrade" the script, etc.

Your PHP code would become something like:

if (isset($_POST["field"]) && isset($_POST["value"])) {

  $field = cleanStr($_POST["field"]);
  $value = cleanStr($_POST["value"]);

  $q = $dbc -> prepare("SELECT ".$field." FROM accounts WHERE ".field." = ?");
  $q -> execute(array($value));

  $msgType = "error";

  switch($field) {

    case "email": {
      if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
        $message = "This is not a valid email!";
      } elseif ($q -> rowCount() > 0) {
        $message = "Email already owns an account!";
      } else {
        $message = "Email success!";
        $msgType = "success";
      }
    }
    break;

    case "password": {
      if (strlen($_REQUEST['password']) < 6) {
        $message = "Your password is not long enough.";
      } else {
        $message = "Password success!";
        $msgType = "success";
      }
    }
    break;

    case "username": {
      if (strlen($_REQUEST['username']) < 3) {
        $message = "Has to be at least 3 characters";
      } elseif ($q -> rowCount() > 0) {
        $message = "Username already taken";
      } else {
        $message = "Username available!";
        $msgType = "success";
      }
      break;
    }

    default:
      echo "The field could not be identified!";
      break;
  }

  echo '<div class="'.$field.' msg '.$msgType.'">'.$message.'</div>';

} else {
  header('Location: http://www.projectv1.com');
  exit();
}


function cleanStr($str) {
    return filter_var(trim($str),FILTER_SANITIZE_STRING);
}

The above notes should give you an overall improvement on your form validation, leading to a valid code and a better solution for future maintenance.

I haven't tested any of it, but it should be working.


Validating Code:

This links should prove useful as to validate your code and warn you about common mistakes:

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.