1

I'm working on validating CAPTCHA input using AJAX. I realized I couldn't return a value from the success handler on the AJAX call, so I created a global variable so the success handler and the validation function could talk to eachother. But it's not working in the most mistifying way. First, the code:

jQuery('#contact').submit(function(event) {
        if (validate_form())
        {
            submit_form(event);
        }
        else
        {
            return false;
        }
    });

var valid = true;

function validate_form()
{
    valid = true;

    // test form stuff here
    // if something goes wrong valid will be set to false

    if (!valid)
    {
        alert("form is not valid, refreshing captcha");
        // refresh form
        // ....
    }
    else
    {
        alert("form is valid, checking captcha");
        check_recaptcha();
    }

    alert("finally, form is:" + valid);
    return valid;
}

function check_recaptcha()
{
    alert("checking captcha now");
    // take only recaptcha values from form
    var recaptcha_vals = jQuery(':input[name^="recaptcha"]').serialize();
    jQuery.post('recaptcha.php', recaptcha_vals, function(data) {
        alert("recaptcha post has succeeded!");
        if (data == '1\n' /*success!*/)
        {
            alert("recaptcha is correct!");
            jQuery('#recaptcha_error').empty();
        }
        else
        {
            alert("recaptcha is incorrect!");
            show_recaptcha();
            jQuery('#recaptcha_error').html("<p class=\"error\">CAPTCHA was incorrect, please try again.</p>");
            valid = false;
        }
    });
}

I tested this code with nothing in the CAPTCHA input field (an obviously incorrect value). According to the alerts, each part of the code went through as expected, except valid was still true after check_recaptcha() executes, even though I know the "incorrect" branch was followed. So question #1 is: Why isn't the event handler able to set valid to false?

Here's where it gets weird. In the above scenario, the form doesn't submit, even though validate_form() returns true (according to the alerts). However, when I comment out all the alerts and try it again with the exact same inputs, then the form DOES submit. So question #2 is: What's up with THAT? #1 is more important though.

Xaq
  • 45
  • 1
  • 7

1 Answers1

2

The point of asynchronous callbacks is that the jQuery.post() will return immediately, without waiting for a reply from the recaptcha service. So when you make your "finally" alert, the recapthcha transation is still going on, and your callback will be called on when it is finished. Then, while your alert is waiting for you to click OK, the recaptcha reply arrives and your callback runs. If you comment out the alert, your submit handler will complete before the callback gets a chance to set valid to false.

That's why you need to supply a callback in the first place, rather than just reading a return value.

Also: You are aware that checking a captcha on the client side is pointless, unless you repeat the check on the server side later, right? (Arguably it's pointless even if you repeat it on the server. The point of client-side validation is to give the user instant feedback on trouble without waiting for a network roundtrip to the server. If you wait to submit until you've completed a roundtrip to check the captcha, you have accomplished nothing but doubling the response time in the successful case. The only exception I can think of is if your form contains really large amounts of data that would take appreciable time to transfer).

hmakholm left over Monica
  • 23,074
  • 3
  • 51
  • 73
  • That is crystal clear, thank you! And yes, the php script that I'm posting to has all the code to check the answer with the recaptcha service and returns some text which indicates the outcome. – Xaq Aug 09 '11 at 19:11
  • Then why do a separate check before submitting? It does nothing but waste time. – hmakholm left over Monica Aug 09 '11 at 19:15
  • To avoid reloading the entire form, which has some state that may have been changed by jQuery, which would be lost if the form was reloaded. There are fields which are shown or hidden based on the user's choice in a dropdown menu. The machinery involved to keep everything synced up with what's showing and what's required is complicated, and I didn't want to burden HTML and PHP with keeping track of all that when it changes with each user. Also, this is my first stab at both jQuery and AJAX so it's likely that I have no idea what I'm doing. – Xaq Aug 09 '11 at 19:37
  • Also, I did read up on verifying reCAPTCHA with an http request put you have to pass along the IP of the user and I didn't think there was a way to get that data client-side. That would definitely make more sense. – Xaq Aug 09 '11 at 19:39