¿Cómo puedo procesar la validación del formulario HTML antes de la validación de reCAPTCHA?

12 minutos de lectura

Avatar de usuario de Julio Guerra
julio guerra

Integré el nuevo marco oculto reCAPTCHA (v2) que por defecto verifica al usuario con el click evento del botón enviar. Pero este evento se desencadena antes de la validación del formulario HTML incorporado. Estoy buscando una manera de hacerlo en el orden esperado: validación de formulario primero, reCAPTCHA después.

Avatar de usuario de Julio Guerra
julio guerra

Tienes que hacerlo programáticamente gracias a una nueva v2 grecaptcha método: grecaptcha.execute() para que recaptcha no reemplace el evento de clic predeterminado del botón que impedía la validación del formulario HTML5 predeterminado.

La ruta del evento es:

  1. Enviar evento de clic en el botón: validación de formulario integrada del navegador
  2. Evento de envío de formulario: llamada grecaptcha.execute()
  3. devolución de llamada reCAPTCHA: enviar el formulario
$('#form-contact').submit(function (event) {
    event.preventDefault();
    grecaptcha.reset();
    grecaptcha.execute();
  });

function formSubmit(response) {
  // submit the form which now includes a g-recaptcha-response input
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://www.google.com/recaptcha/api.js"></script>
<form action="?">
  <div class="g-recaptcha" 
       data-sitekey="your-key"
       data-size="invisible"
       data-callback="formSubmit">
  </div>
  <button type="submit">Submit</button>
</form>

  • Con este código, el formulario nunca se enviará porque impide la acción predeterminada (que es enviar). Si desea enviar, no debe evitar la acción debido a una condición, como mi respuesta a continuación, verifique allí.

    – giovannips

    10 de marzo de 2017 a las 21:04

  • @giovannipds este código se está ejecutando en nuestro sitio web y se envía oO grecaptcha.execute() lo envía CUANDO la prueba de formulario reCAPTCHA se realiza correctamente.

    – Julio Guerra

    14/03/2017 a las 21:56


  • @JulioGuerra tienes razón, pero desafortunadamente no usa la validación predeterminada del navegador. Supongo que el mío a continuación lo hace. Intenta comprobarlo.

    – giovannips

    9 junio 2017 a las 18:25

  • formSubmit() tiene que estar vacio?!

    – Negro

    20 de noviembre de 2017 a las 22:18

  • @Black no necesariamente. Él la documentación dice “La respuesta del usuario, g-recaptcha-response, será la entrada para su función de devolución de llamada”. Cambié el ejemplo para incluirlo en lugar de ignorarlo (porque también puede leerlo desde una entrada oculta en el formulario, una vez validado).

    – Julio Guerra

    21 de noviembre de 2017 a las 13:57

Aquí está mi solución para obtener la validación de HTML5 + Recaptcha invisible:

HTML:

<form id="my-form">
    <!-- Your form fields ... -->
    <div class="g-recaptcha"
        data-sitekey="..."
        data-callback="submitMyForm"
        data-size="invisible">
    </div>
    <button type="submit">Submit</button>
</form>

JS:

var myForm = $('my-form');

function submitMyForm () {
    myForm.trigger('submit', [true]);
}

$(function () {
    myForm.on('submit', function (e, skipRecaptcha) {
        if(skipRecaptcha) {
            return;
        }

        e.preventDefault();
        grecaptcha.execute();
    });
  })

Hola tengo una solución de trabajo aquí. Trabajando con Recaptcha invisible.

jQuery(document).ready(function() {
    var commentform = jQuery("#commentform");
    commentform.on("click", "#submit-comment", function(e) {
      if(commentform[0].checkValidity()) {
        e.preventDefault();
        grecaptcha.execute();
      }
    });
});

function submitCommentForm(data) {
    document.getElementById("commentform").submit();
}
<form action="blaba.php" method="post" id="commentform" class="comment-form">
  <div class="form-submit">
    <div data-callback="submitCommentForm" data-sitekey="yourkey" class="g-recaptcha" data-size="invisible">
    <button id="submit-comment">Leave a comment</button>
  </div>
</form>

USD Avatar de usuario de Matt
USD mate

Tuve este problema porque el método predeterminado parece anular la validación del formulario html5. También quería que todo el código fuera genérico en lugar de codificar cualquier función/nombre de elemento. Al final, se me ocurrió el siguiente código usando la API v3:

HTML

<form method="post" action="?" class="ui-recaptcha" name="my_form_name">
   ...
   <input type="submit" value="Submit">
</form>
<script src="https://www.google.com/recaptcha/api.js?render={key}" async defer></script>

Javascript (estoy usando jQuery pero sería bastante fácil de adaptar a vanilla js)

$('.ui-recaptcha').submit(e => {

    var form = e.target;

    if( $(form).data('recaptcha-done') )
        return;

    e.preventDefault();
    grecaptcha.execute('{key}', {'action': $(form).attr('name')}).then(token => {

        $(form).append($('<input>').attr({'type': 'hidden', 'name': 'g-recaptcha-response', 'value': token}));
        $(form).data('recaptcha-done', true);
        $(form).submit();
    });
});

Encontré eso solo llamando submit como en algunos ejemplos anteriores, me causó un bucle, lo que tendría sentido ya que el controlador de recaptcha se ejecuta en el submit evento.

Esto ejecuta recaptcha para cualquier ui-recaptcha formulario, pasa el formulario name atributo como la acción que se puede ver en la consola reCaptcha, y luego inserta el token en el formulario. Una vez que se ejecuta, establece un atributo de datos en el formulario para que la llamada recursiva para enviar no intente ejecutar recaptcha nuevamente.

Avatar de usuario de PigBoT
CerdoBoT

Aquí está mi solución.

  • Utiliza reCaptcha v3 (invisible) documentos
  • Utiliza la validación de formulario HTML5 nativo
  • Utiliza JS puro
  • Utiliza procesamiento POST estándar (se puede modificar a AJAX)

Agregue tantos formularios como sea necesario, simplemente cambie el ‘UNIQUE_FORM_ID’ en los dos lugares y actualice POST_URL para el formulario. Asegúrese de utilizar su propia clave en las ubicaciones de ‘RECAPTCHA_SITE_KEY’.

<form id="UNIQUE_FORM_ID" method="post" action="POST_URL">
    <!-- ** Notice ** this hidden input field that will later send our g-recaptcha token back to our server -->
    <input type="hidden" name="g-recaptcha-response" value="">
    <!-- Add other hidden nonce fields -->

    <!-- Required field -->
    <input name="fullname" type="text" placeholder="Full Name" required>

    <!-- Submit button -->
    <!-- ** Notice ** the 'form' attribute; using SAME value as it's parent's form id, above. -->
    <!-- ** Notice ** the 'onclick' attribute; be sure to pass event -->
    <button type="submit" form="UNIQUE_FORM_ID" onclick="formSubmitBtn(event)">Send</button>
</form>

<!-- Only add scripts once -->
<!-- ** Notice ** to manually call grecaptcha, our site key must be included when loading api.js using the 'render' query param -->
<script src="https://www.google.com/recaptcha/api.js?render=RECAPTCHA_SITE_KEY"></script>
<script>
    /**
     * Handles form submissions for Google recaptcha v3.
     * Allows for HTML5 form validation to complete before processing.
     */
    function formSubmitBtn($event) {
        /**
         * Checks the validity of the form.
         * Return if invalid; HTML5 validation errors should display.
         */
        if (!$event.target.form.checkValidity()) {
            return;
        }
        /**
         * Form is client-side valid; taking over the remainder of processing.
         */
        $event.preventDefault();
        grecaptcha.ready(function() {
            grecaptcha.execute("RECAPTCHA_SITE_KEY", { action: 'submit' }).then(function(token) {
                /**
                 * Adds the token g-recaptcha-response token to our hidden form element.
                 * ** Notice ** we our referencing the specific form's input element by name here (do not use IDs).
                 */
                $event.target.form.elements['g-recaptcha-response'].value = token;
                /**
                 * Use the form API directly to submit the form.
                 */
                $event.target.form.submit();
            });
        });
    }
</script>

  • Me encontré con tu publicación para una solución similar. ¿Está destinado a ser puramente del lado del cliente? El token no parece estar poblado. En segundo lugar, ¿hay algún motivo por el que decidiste no utilizar addEventListener p.ej document.addEventListener('DOMContentLoaded', function () { document.getElementById('html-submitt') .addEventListener('submit', formSubmitBtn); });

    – Motivado

    10 de enero de 2021 a las 3:47


  • Esto tiene la intención de ser flexible. No hay dependencia del servidor, si eso es lo que estás preguntando. Por supuesto, deberá usar sus claves de API privadas para enviar el token de respuesta a Google para obtener los valores de recaptcha. ¿Reemplazaste RECAPTCHA_SITE_KEY con su clave específica? Estoy ejecutando este código en un entorno de producción y me funciona. No veo una razón para agregar detectores de eventos y consumir recursos y correr el riesgo de fugas de memoria por no eliminar detectores de eventos, etc. (según el uso). La única función admitirá formas ilimitadas. Feliz de ayudar más. ¿Fragmento de código?

    – PigBoT

    10 de enero de 2021 a las 20:59

  • El código se ejecuta literalmente con una clave recaptcha actualizada. Cuando se envía el formulario, realiza la validación HTML5 nativa (que es una ventaja). Luego captura los valores, por ejemplo, el nombre. Sin embargo, no devuelve un token en la entrada oculta. Está en blanco. La razón para usar detectores de eventos es que los eventos de clic en línea están bloqueados por políticas de seguridad de contenido.

    – Motivado

    10 de enero de 2021 a las 21:59

  • No soy un experto en CSP. Muchas preguntas, pero no el lugar en estos comentarios. 😛 Suena como una solución razonable para su situación. Hay tantos lugares que podrían salir mal con el cambio, especialmente alrededor del $event. ¿Sigue saliendo como se esperaba? ¿Se crea el token a partir de la execute ¿método? ¿Está adjuntando el valor del token al formulario correcto? Si haces un codepen o algo similar, podría ayudar a depurar.

    – PigBoT

    11 de enero de 2021 a las 15:28

  • No estoy seguro de si Codepen o algo similar tiene la opción de implementar una política de CSP, ya que esto tiende a ser del lado del servidor. Feliz de compartir la política de seguridad de contenido si lo desea. ¿Quiere decir que está llegando a través de la carga? En caso afirmativo, no, no hay ningún token en carga. Tampoco se genera ningún token cuando se envía el formulario. El valor oculto está vacío.

    – Motivado

    11 de enero de 2021 a las 21:34

Avatar de usuario de Zoran
Zoran

 let siteKey = "...";
 $("form").submit(function (eventObj) {
        var myForm = this;
        eventObj.preventDefault();
        grecaptcha.execute( siteKey, {
            action: "submit"
        })
            .then(function (token) {
                $('<input />').attr('type', 'hidden')
                    .attr('name', "g_recaptcha_response")
                    .attr('value', token)
                    .appendTo(myForm);
                myForm.submit();
            });
    });

Esto ejecutará recapcha, esperará la respuesta, agregará el atributo oculto g_recaptcha_response a cualquier formulario cuando el navegador intente enviarlo y luego lo envíe. Necesita la variable global siteKey

  • Me encontré con tu publicación para una solución similar. ¿Está destinado a ser puramente del lado del cliente? El token no parece estar poblado. En segundo lugar, ¿hay algún motivo por el que decidiste no utilizar addEventListener p.ej document.addEventListener('DOMContentLoaded', function () { document.getElementById('html-submitt') .addEventListener('submit', formSubmitBtn); });

    – Motivado

    10 de enero de 2021 a las 3:47


  • Esto tiene la intención de ser flexible. No hay dependencia del servidor, si eso es lo que estás preguntando. Por supuesto, deberá usar sus claves de API privadas para enviar el token de respuesta a Google para obtener los valores de recaptcha. ¿Reemplazaste RECAPTCHA_SITE_KEY con su clave específica? Estoy ejecutando este código en un entorno de producción y me funciona. No veo una razón para agregar detectores de eventos y consumir recursos y correr el riesgo de fugas de memoria por no eliminar detectores de eventos, etc. (según el uso). La única función admitirá formas ilimitadas. Feliz de ayudar más. ¿Fragmento de código?

    – PigBoT

    10 de enero de 2021 a las 20:59

  • El código se ejecuta literalmente con una clave recaptcha actualizada. Cuando se envía el formulario, realiza la validación HTML5 nativa (que es una ventaja). Luego captura los valores, por ejemplo, el nombre. Sin embargo, no devuelve un token en la entrada oculta. Está en blanco. La razón para usar detectores de eventos es que los eventos de clic en línea están bloqueados por políticas de seguridad de contenido.

    – Motivado

    10 de enero de 2021 a las 21:59

  • No soy un experto en CSP. Muchas preguntas, pero no el lugar en estos comentarios. 😛 Suena como una solución razonable para su situación. Hay tantos lugares que podrían salir mal con el cambio, especialmente alrededor del $event. ¿Sigue saliendo como se esperaba? ¿Se crea el token a partir de la execute ¿método? ¿Está adjuntando el valor del token al formulario correcto? Si haces un codepen o algo similar, podría ayudar a depurar.

    – PigBoT

    11 de enero de 2021 a las 15:28

  • No estoy seguro de si Codepen o algo similar tiene la opción de implementar una política de CSP, ya que esto tiende a ser del lado del servidor. Feliz de compartir la política de seguridad de contenido si lo desea. ¿Quiere decir que está llegando a través de la carga? En caso afirmativo, no, no hay ningún token en carga. Tampoco se genera ningún token cuando se envía el formulario. El valor oculto está vacío.

    – Motivado

    11 de enero de 2021 a las 21:34

Quería el mismo comportamiento, pero usando el nuevo recaptcha, el invisible. Después de mirar un poco de código y probar algunas cosas, me metí en esto. La principal diferencia es que este también utiliza la validación predeterminada del navegador:

var contact_form;
$(function() {
    contact_form = $('#contact-form');
    contact_form.submit(function (event) {
        if ( ! contact_form.data('passed')) {
            event.preventDefault();
            grecaptcha.execute();
        }
    });
});
function sendContactForm(token) {
    contact_form.data('passed', true);
    contact_form.submit();
}

Básicamente, almacena el objeto de formulario jquery en una var global, incluido, utiliza sendContactForm como la devolución de llamada, pero cuando lo llama el recaptcha, establece una variable de datos llamada passed, lo que permite que no se obstaculice la forma. Es exactamente el mismo comportamiento que normalmente haría recaptcha, pero con esa condición.

Actualizar: volver a mirar mi código a la derecha me recuerda que probablemente necesite una forma de restaurar los datos pasados ​​a false después de la ejecución de grecaptcha. Considere que si implementará esto.

  • exactamente el mismo flujo

    – Julio Guerra

    14/03/2017 a las 22:02

  • No, si recuerdo bien, su respuesta anterior no usa los valores predeterminados de validación del navegador, la mía sí. Considere probar ambos antes de dejar -1. Gracias.

    – giovannips

    9 de junio de 2017 a las 18:22

¿Ha sido útil esta solución?