不可见的 reCaptcha:无法进行有效的 AJAX 调用,进入无限循环

Invisible reCaptcha: can't make a working AJAX call, getting infinite loop

表单有 JS 验证并且工作正常。然后我不得不通过工作验证实现不可见的 reCaptcha。
代码如下所示:
index.html

//In the head I've

<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://google.com/recaptcha/api.js?onload=initRecaptcha&render=explicit"></script>

//then form comes

<form enctype="multipart/form-data" method="POST" id="myForm" class="callback">                                    
    <div class="inputs">
        <label class="input input-name required">
            <span class="placeholder">Name</span>
            <input type="text" name="name">
        </label>
        <label class="input input-phone required">
            <span class="placeholder">Phone</span>
            <input type="text" name="phone" maxlength="12">
        </label>
        <label class="input input-mail">
            <span class="placeholder">Email</span>
            <input type="text" name="email">
        </label>
    </div>

    <div class="check">
        <label>
            <input class="check-agree" type="checkbox" name="agree" checked="checked" value="1">
            <span class="label">I agree <span class="agree-text js-agree-text">with terms and conditions</span></span>
        </label>
        <div id="status"></div>
    </div>
    <div class="submit-wrapper">
        <label class="submit">
            <input class="submit-form-button js-submit-form" type="submit" value="Submit">
        </label>            

   </div>       
    <div id="form-recaptch"></div>
</form>


//and in the end
<script type="text/javascript" src="js/form.js"></script> 

接下来是 form.js

var submit_form_outer;

var initRecaptcha = function(){
    if ( document.getElementById("form-recaptch") ) {

        recaptcha = grecaptcha.render("form-recaptch", {
            'sitekey' : 'code',
            'theme'    : 'dark',            
            'badge'    : 'inline',
            'size'     : 'invisible',
            'callback' : submit_form_outer
        });
    }
}

$(function () {        

    $('.callback').submit(function (e) {
        submit_form($(this));
        e.preventDefault();
        /*stop refresh*/
        e.stopPropagation();
    });

    /*More Functions*/

    function validateEmail(form) {
        var email = form.find('[name="email"]');
        var re = /.+@.+\..+/g;
        if( re.test(email.val()) || email.val() === ""){
            return true;
        } else {
            email.closest('.input').addClass('error');
            return false;
        }
    }
    function hasErrors (form) {
        var required = form.find('.required');
        var has_errors = false;
        required.each(function(){
            if(!$(this).find('input').val()){
                $(this).addClass('error');
                has_errors = true;
            }
        });
        return has_errors;
    }


    function submit_form() {

        if( hasErrors($("form#myForm")) || !validateEmail($("form#myForm"))){
            return false;
        }
    grecaptcha.execute();

    var container = $("#form-recaptch").parents("form");

    $.ajax({
        method: "POST",
        url: "ajax.php",
        data: container.serialize(),

        success: function (responseObj) {
                container.submit();                
                $('#myForm input').val('');
                $('#myForm textarea').val('');                            
                $('#status').html(responseObj);                             
        },
        error: function (xhr, error) {
          console.debug(xhr); 
          console.debug(error);
          $("#status").html("Failed.");
        },        

    });        
    }

    submit_form_outer = submit_form;

    function Form () {
        /*...*/


        /*More Functions*/
        $('.form').on('click', '.js-agree-text', function () {
            showAgreeBox();
            openFormPanel();
            return false;
        });

        $('.form').on('click', '.js-no-agree', function () {
            agreeNo();
            return false;
        });


        $('.form').on('click', '.js-yes-agree', function () {
            agreeYes();
            return false;
        });

        $('.check-agree').change(function() {
            var form = $(this).closest('.form');
            if($(this).is(":checked")) {
                form.find('.js-submit-form').removeClass('disabled');
            } else {
                form.find('.js-submit-form').addClass('disabled');
            }

        }
        /*More Functions*/
        this.callback = function () {

            showThankBox();
            openFormPanel();

            $.fn.fullpage.setAllowScrolling(false);

        }

    }
    var mainform = new Form ();
});

并且 ajax.php 与 phpMailer:

<?php
$captcha;

if ( isset($_POST['g-recaptcha-response']) ) {
    $captcha = $_POST['g-recaptcha-response'];
}

if( !$captcha ){
    $response = array (
        'status' => 'error',
        'info' => 'Please check the the captcha form.'
    );
    echo json_encode($response);
    exit;
}


if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Get the form fields and remove whitespace.
    $name = strip_tags(trim($_POST["name"]));
            $name = str_replace(array("\r","\n"),array(" "," "),$name);
    $email = filter_var(trim($_POST["email"]), FILTER_SANITIZE_EMAIL);
    $phone = trim($_POST["phone"]);
}


$secretKey = "key";

$ip = $_SERVER['REMOTE_ADDR'];

$fields = array(
        'secret'    =>  $secretKey,
        'response'  =>  $captcha,
        'remoteip'  =>  $ip
    );
    $ch = curl_init("https://www.google.com/recaptcha/api/siteverify");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($fields));
    $responseKeys = json_decode(curl_exec($ch));
    curl_close($ch);


if(intval($responseKeys["success"]) !== 1) {
    $response = array (
        'status' => 'spam',
        'info' => 'You look like a spam.'
    );

    echo '<span style="color:red;">'.json_encode($response).'</span>';
    exit;
} else {

    require_once dirname(__FILE__) . '/phpmailer/PHPMailerAutoload.php';    

    $mail = new PHPMailer;

    if(!$mail->send()) 
    {
        echo "Mailer Error: " . $mail->ErrorInfo;
    } 
    else 
    {
        echo "Message has been sent successfully";
    }   



}

?>

所以,问题是 CAPTCHA 甚至不检查表格。
例如,我有另一个网站,配置非常相似,但有其他验证,所以每次我按下提交按钮时,都会显示验证码图像,我应该通过测试。
在这种情况下,什么也没有显示。只出现 {"status":"error","info":"Please check the the captcha form."} 消息,表示已跳过且未执行验证码测试。

我想保留工作表单验证并使所有内容协同工作。 欢迎任何 modifications/optimizations。

UPDATE:这是新的 form.js 代码,现在我在 [=16= 之间的某处得到无限循环] 并且输入 validation 已停止工作:

var submit_form_outer;

var initRecaptcha = function(){
    if ( document.getElementById("form-recaptcha") ) {

        recaptcha = grecaptcha.render("form-recaptcha", {
            'sitekey' : '',
            'theme'    : 'dark',
            'type'     : 'image',
            'badge'    : 'inline',
            'size'     : 'invisible',
            'callback' : submit_form_outer
        });
    }
}

$(function () {      

    submit_form_outer = submit_form; 

    $('.callback').submit(function (e) {        
        submit_form($(this));
        e.preventDefault();
        /*stop refresh*/        
    /*    e.stopPropagation();*/
    });



    var phone = $('[name="phone"]'),
        input = $('input[type="text"], textarea');



    phone.click(function () {
        $(this).parent().removeClass('error');
        if ($(this).val().length == 0) {
            $(this).val('+');
        }
    });
    phone.focusout(function () {
        if ($(this).val().length < 2) {
            $(this).val('');
        }
    });

    phone.bind("change keyup input click", function() {
        if (this.value.match(/[^0-9+ ]/g)) {
            this.value = this.value.replace(/[^0-9+ ]/g, '');
        }
    });

    input.focus(function () {
        $(this).parent().removeClass('error');
        $(this).parent().find('span').addClass('focused');
    });
    input.focusout(function(){
        if ($(this).val() == ''){
            $(this).parent().find('span').removeClass('focused');
        }
    });

    function validateEmail(form) {
        var email = form.find('[name="email"]');
        var re = /.+@.+\..+/g;
        if( re.test(email.val()) || email.val() === ""){
            return true;
        } else {
            email.closest('.input').addClass('error');
            return false;
        }
    }
    function hasErrors (form) {
        var required = form.find('.required');
        var has_errors = false;
        required.each(function(){
            if(!$(this).find('input').val()){
                $(this).addClass('error');
                has_errors = true;
            }
        });
        return has_errors;
    }


    function submit_form() {

        if( hasErrors($("form#myForm")) || !validateEmail($("form#myForm"))){
            return false;
        } 


    var container = $("#form-recaptcha").parents("form");


    $.ajax({
        method: "POST",
        url: "ajax.php",
        data: container.serialize(),

        success: function (responseObj, textStatus, xhr) {                            

            console.log('success');
            console.log(responseObj);

            container.submit();                 
            $('#myForm textarea').val('');                            
            $('#status').html(responseObj);

           /*if(xhr.status === '200'){
                $("#status").html('200');               
            }
            else{
                console.log('else');
                grecaptcha.reset();
            }*/         

        },
        error: function (xhr, error) {      
          console.log('error');     
          console.debug(error);
          console.debug(xhr);           
          $("#status").html("Failed.");
          grecaptcha.reset();
        },        

    });
        grecaptcha.reset();
    }



    function Form () {

        function openFormPanel () {
            $('body').addClass('form-panel-opened form-panel-close-callback');
            $.fn.fullpage.setAllowScrolling(false);
        }
        function closeFormPanel () {
            $('body').removeClass('form-panel-opened');
            $('.button-menu').removeClass('active');
            window.setTimeout(function () {
                $('body').removeClass('form-panel-close-callback');
                $.fn.fullpage.setAllowScrolling(true);
            }, 700);
        }

        function showAgreeBox () {
            $('.form .agreement-box').addClass('show');
            $('.form .thank-box').removeClass('show');
            $('.agreement .scrollable').scrollTop(0);
        }
        function showFormBox () {
            $('.form .agreement-box').removeClass('show');
            $('.form .thank-box').removeClass('show');
        }
        function showThankBox () {
            $('.form .agreement-box').removeClass('show');
            $('.form .thank-box').addClass('show');
        }

        function agreeYes(){
            if($('body').hasClass('fp-viewing-2')){
                closeFormPanel();
            } else {
                showFormBox();
            }
            $('.js-submit-form').removeClass('disabled');
            $('.check-agree').prop('checked', true);
        }
        function agreeNo(){
            if($('body').hasClass('fp-viewing-2')){
                closeFormPanel();
            } else {
                showFormBox();
            }
            $('.js-submit-form').addClass('disabled');
            $('.check-agree').prop('checked', false);
        }


        $('.form').on('click', '.js-agree-text', function () {
            showAgreeBox();
            openFormPanel();
            return false;
        });

        $('.form').on('click', '.js-no-agree', function () {
            agreeNo();
            return false;
        });


        $('.form').on('click', '.js-yes-agree', function () {
            agreeYes();
            return false;
        });

        $('.check-agree').change(function() {
            var form = $(this).closest('.form');
            if($(this).is(":checked")) {
                form.find('.js-submit-form').removeClass('disabled');
            } else {
                form.find('.js-submit-form').addClass('disabled');
            }
        }).mouseover(function () {
            $(this).addClass('hover');
        }).mouseout(function () {
            $(this).removeClass('hover');
        });

        $('.form').on('click', '.js-submit-form', function () {
            var form = $(this).closest('.form');
            var agree = form.find('.check-agree').prop('checked');
            if(!agree) return false;
        });     

        $('.form').on('click', '.js-submit-form', function () {
            if( !hasErrors($("form#myForm")) ||       validateEmail($("form#myForm"))){
            if ( grecaptcha.getResponse() !== 0 ) {
                grecaptcha.execute(); 
            }   
        } 

    });

        function resetForm () {
            $('.form input[type="text"], .form textarea').each(function () {
                $(this).val('');
            });
            $('.form .placeholder').removeClass('focused');


        }

        this.callback = function () {

            showThankBox();
            openFormPanel();
            $.fn.fullpage.setAllowScrolling(false);

        }
    }
    var mainform = new Form ();
});

所以经过长时间的挖掘,我发现问题出在 success 方法中。更确切地说,我在其中放置了 container.submit();,这是导致无限循环的问题。
所以,最后的 form.js 看起来像:

var submit_form_outer;


var initRecaptcha = function(){
    if ( document.getElementById("form-recaptcha") ) {

        recaptcha = grecaptcha.render("form-recaptcha", {
            'sitekey' : '6',
            'theme'    : 'dark',
            //'type'     : 'image',
            'badge'    : 'inline',
            'size'     : 'invisible',
            'callback' : submit_form_outer
        });
    }
}

$(function () {      

    submit_form_outer = submit_form; 

    $('.callback').submit(function (e) {        
        submit_form($(this));
        e.preventDefault();
    });


    /*some functions*/

    function validateEmail(form) {
        var email = form.find('[name="email"]');
        var re = /.+@.+\..+/g;
        if( re.test(email.val()) || email.val() === ""){
            return true;
        } else {
            email.closest('.input').addClass('error');
            return false;
        }
    }
    function hasErrors (form) {
        var required = form.find('.required');
        var has_errors = false;
        required.each(function(){
            if(!$(this).find('input').val()){
                $(this).addClass('error');
                has_errors = true;
            }
        });
        return has_errors;
    }       


    function Form () {

        /*more functions and validations*/

        $('.form').on('click', '.js-submit-form', function () {
            var form = $(this).closest('.form');
            var agree = form.find('.check-agree').prop('checked');

            if ((!agree) || ( hasErrors($("form#myForm")) || !validateEmail($("form#myForm"))))         
                return false;
            else {
                if ( grecaptcha.getResponse() !== 0 ) {
                    grecaptcha.execute(); 
                }   
            }
        });     


        function resetForm () {
            $('.form input[type="text"], .form textarea').each(function () {
                $(this).val('');
            });
            $('.form .placeholder').removeClass('focused');


        }


    }

    function submit_form() {

        if( hasErrors($("form#myForm")) || !validateEmail($("form#myForm"))){
            return false;
        } 


        var container = $("#form-recaptcha").parents("form");


        $.ajax({
            method: "POST",
            url: "ajax.php",
            data: container.serialize(),

            beforeSend: function(e) {

                $("#status").html("working...");
            },
            success: function (responseObj, textStatus, xhr) {                            

                $('#myForm').trigger("reset");
                $('#status').html('sent');
                showThankBox();
                grecaptcha.reset();        

            },
            error: function (responseObj, xhr, error) { 
              $("#status").html("error.");
              grecaptcha.reset();
            },        

        });
            grecaptcha.reset();
    }   

    var mainform = new Form ();
});