jQuery.submit() form data not submitted.

OVERVIEW

You can use jQuery to validate form input data before a form submit takes place. Disabling the form inputs when the user clicks submit, seems like a good way to prevent form input changes by the user while validation takes place. But subtle problems can happen if you use the disabled attribute to do this. Look at this code as a example…

$( 'form' ).on( 'submit', function ( event ) {

   /* Prevent submit in case form inputs are invalid. */
   event.preventDefault();

   /* Disable all form inputs. */
   $( 'form :input' ).prop( 'disabled', true );

   /* Post form input data for validation. */
   $.post(
      'https://mysite.com/ajax-validator.php',
      {
         action: 'validateForm',
         item1: $( 'form input[name="item1"]' ).val(),
         item2: $( 'form input[name="item2"]' ).val(),
         nonce: $( 'form input[name="nonce"]' ).val()
      },
      function ( result ) {
         if ( result ) {
            if ( result.success ) {

               /* Form inputs valid, submit form. */
               /* Keep for inputs disabled to prevent changes. */
               $( 'form' ).submit();

            } else if ( result.error ) {

               /* Form inputs not all valid, display error messages. */
               alert( 'Error: ' + result.error );

               /* Enable form inputs AFTER user closes alert. */
               $( 'form :input' ).prop( 'disabled', false );
            }
            return;
         }
      } );
} );

In the javascript example above, all of the form’s inputs are first disabled. Then those form input values are sent by an AJAX request to the web server for validation. Finally, a callback function will either submit the validated form, or display an error message if any form input is invalid.

But this code does not work. The web server receives the form inputs in the AJAX call, and validates the input data. The AJAX callback function does receive the validation results. But when $( ‘form’ ).submit(); executes, the server receives no form input data. The reason for this lies in the documentation for the disabled attribute

It may seem confusing that jQuery can access disabled values, but form submission can not. But it makes sense, since form submission is an HTML feature that is limited by the disabled attribute, while jQuery is javascript, which has access to the entire Document Object Model (DOM).

You could simply remove the disabled attribute from the form inputs before submitting the form in the AJAX callback, like so…

function ( result ) {
   if ( result ) {

      /* Enable form inputs BEFORE proceeding. */
      $( 'form :input' ).prop( 'disabled', false );

      if ( result.success ) {

         /* Form inputs valid, submit form. */
         $( 'form' ).submit();

      } else if ( result.error ) {

         /* Form inputs not all valid, display error messages. */
         alert( 'Error: ' + result.error );

      }
      return;
   }
}

However, this means our enabling form inputs before form submission. We wanted to keep the form inputs disabled during validation, and while the form data is being sent to the server, and until the page reloads with the server’s response to the submitted form.

SOLUTION ONE

One possible solution is hinted at further into the documentation for the disabled attribute

You can simply use the readonly attribute instead of the disabled attribute on form inputs, and the $( ‘form’ ).submit(); call will work…

$( 'form' ).on( 'submit', function ( event ) {

   /* Prevent submit in case form inputs are invalid. */
   event.preventDefault();

   /* Set readonly attribute on all form inputs. */
   $( 'form :input' ).prop( 'readonly', true );

   /* Post form input data for validation. */
   $.post(
      'https://mysite.com/ajax-validator.php',
      {
         action: 'validateForm',
         item1: $( 'form input[name="item1"]' ).val(),
         item2: $( 'form input[name="item2"]' ).val(),
         nonce: $( 'form input[name="nonce"]' ).val()
      },
      function ( result ) {
         if ( result ) {
            if ( result.success ) {

               /* Form inputs valid, submit form. */
               /* Keep form inputs readonly to prevent changes. */
               $( 'form' ).submit();

            } else if ( result.error ) {

               /* Form inputs not all valid, display error messages. */
               alert( 'Error: ' + result.error );

               /* Enable form inputs AFTER user closes alert. */
               $( 'form :input' ).prop( 'readonly', false );
            }
            return;
         }
      } );
} );

SOLUTION TWO

Instead of using disabled or readonly attributes to prevent changes to form inputs, we can create a <div> layer over the form. The user can not click on any form inputs covered by the <div> layer. However, they could use the tab key to get to each input to alter it. To prevent this, you need to intercept keydown events too…

$( 'form' ).on( 'submit', function ( event ) {

   /* Prevent submit in case form inputs are invalid. */
   event.preventDefault();
   
   /* Set flag to intercept key presses */
   window.processingForm = true;

   /* Create translucent layer over form. */
   let $form = $( 'form' ).first();
   let offset = $form.offset();
   $( '<div>', {
      id: 'mask'
   } )
      .css( {
         'top': offset.top,
         'left': offset.left,
         'height': $form.height(),
         'width': $form.width(),
         'background-color': 'rgba(255, 255, 255, 0.25)',
         'z-index': '999'
      } )
      .insertBefore( $form );

   /* Post form input data for validation. */
   $.post(
      'https://mysite.com/ajax-validator.php',
      {
         action: 'validateForm',
         item1: $( 'form input[name="item1"]' ).val(),
         item2: $( 'form input[name="item2"]' ).val(),
         nonce: $( 'form input[name="nonce"]' ).val()
      },
      function ( result ) {
         if ( result ) {
            if ( result.success ) {

               /* Form inputs valid, submit form. */
               $( 'form' ).submit();

            } else if ( result.error ) {
            
               /* Stop intercepting key presses. */
               window.processingForm = false;

               /* Form inputs not all valid, display error messages. */
               alert( 'Error: ' + result.error );

               /* Unmask the form. */
               $( 'div#mask' ).remove();
            }
            return;
         }
      } );
} );

/* Intercept keydown events during form processing. */
$( 'form' ).on( 'keydown', function ( event ) {
   if ( window.processingForm ) {
      event.preventDefault();
   }
} );

SOLUTION THREE

Another solution is to avoid calling $( ‘form’ ).submit(); at all. Instead, use the full power of AJAX and have the web server both validate and send back the results of a successful form submission for display. Then replace the form with the result. In this case, using the disabled attribute is not a problem since jQuery can access the values of disabled inputs…

$( 'form' ).on( 'submit', function ( event ) {

   /* Prevent submit in case form inputs are invalid. */
   event.preventDefault();

   /* Disable all form inputs. */
   $( 'form :input' ).prop( 'disabled', true );

   /* Post form input data for validation. */
   $.post(
      'https://mysite.com/ajax-validator.php',
      {
         action: 'validateForm',
         item1: $( 'form input[name="item1"]' ).val(),
         item2: $( 'form input[name="item2"]' ).val(),
         nonce: $( 'form input[name="nonce"]' ).val()
      },
      function ( result ) {
         if ( result ) {
            if ( result.success && result.content ) {

               /* Form successfully processed, display results. */
               $( 'form' ).replaceWith( result.content );

            } else if ( result.error ) {

               /* Form inputs not all valid, display error messages. */
               alert( 'Error: ' + result.error );

               /* Enable form inputs AFTER user closes alert. */
               $( 'form :input' ).prop( 'disabled', false );
            }
            return;
         }
      } );
} );