0

I have a form I am implementing some custom validation on. This is the block of JavaScript that handles the final check before the form is submitted:

$('.enquiry-form-container form').submit(function (e) {
        e.preventDefault();
        var invalid = false;
        var isblank = false;
        //Loop through each input and check if valid or empty
        $('.validate').each(function () {
            if ($(this).hasClass('invalid')) {
                isInValid($(this));
                invalid = true;
            } else {
                //Any fields are blank
                if ($(this).val() === "") {
                    $(this).addClass('blank');
                    isblank = true;
                } else {
                    $(this).addClass('valid');
                    isValid($(this));
                    $(this).removeClass('blank empty');
                }
            }
        });

        if (!invalid & !isblank){ //SEND
            $(this).find(":submit").prop("disabled", true); //Prevent submit to prevent duplicate submissions
            $(this).submit();
        } else { //DONT SEND

        }
    });

Each time I fill out the form and attempt to submit I get the following error in the console:

Uncaught RangeError: Maximum call stack size exceeded(…)

I understand that this can happen for a number of reasons, usually an infinite loop. Can anyone see where I am going wrong in the above code? Is the .submit() function calling the submit() method again... If so how can I resolve this and send form if it validates?

Just for full clarity, here is my isInValid() and isValid() functions.. They are used to add or remove the appropriate classes so I can style the inputs differently depending on the input.

//VALID INPUT
    function isValid(input) {
        input.addClass('valid');
        input.removeClass('invalid empty blank');
        input.parent().parent().next('.hint').css('visibility', 'hidden');
    }
    //INVALID INPUT 
    function isInValid(input) {
        input.addClass('invalid');
        input.removeClass('valid empty blank');
        input.parent().parent().next('.hint').css('visibility', 'visible');
    }
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
Javacadabra
  • 5,578
  • 15
  • 84
  • 152
  • Or it indicates that infinite recursion, specially you are calling submit inside submit. – Hakan Fıstık Dec 13 '16 at 15:00
  • @HakamFostok ok so I guess then I should control sending the form with a `boolean` and return true or false depending? – Javacadabra Dec 13 '16 at 15:01
  • I think it's `$(this).submit()` . Maybe all your inputs aren't blank or invalid so it is getting stuck in an infinite recursion. – philantrovert Dec 13 '16 at 15:01
  • Generally, you just need to cancel the event in `if/then` branches that indicate a validation problem. You are doing it right away (even if your form is good). If you don't hit those branches, the form submits as it normally would. This removes the need for you to manually indicate that you want the form submitted. – Scott Marcus Dec 13 '16 at 15:03
  • why not put this in a click handler on the submit button instead of the submit event? – Cruiser Dec 13 '16 at 15:04
  • @Cruiser because a form can be submitted via other methods than a button click and you'd miss the validation logic if that were to happen. – Scott Marcus Dec 13 '16 at 15:05
  • I think you could try the following: 1- remove `e.preventDefault();` from the beginning of the function. 2- put it (the `e.preventDefault();`) in the `else` statement (the place you want to prevent the form from submitting) 3- Remove the `$(this).submit();` from the `if` statement – Hakan Fıstık Dec 13 '16 at 15:06
  • 2
    @HakamFostok Yup. That's what I was getting at. – Scott Marcus Dec 13 '16 at 15:06
  • @ScottMarcus, as I understand it you can submit a form with JS outside of a click handler, but wouldn't this just be in other code the OP writes? Is there a way for a user to submit a form without clicking a submit button? I'm genuinely interested in learning why this would be preferred over a button click event. – Cruiser Dec 13 '16 at 15:12
  • In the right configuration, a form can be submitted by the user just pressing ENTER. If that were the case, your `button.click` event handler would be bypassed. It's always advisable to deal with form validation in the form's `submit` event callback. – Scott Marcus Dec 13 '16 at 15:16
  • @ScottMarcus that makes sense, thanks. – Cruiser Dec 13 '16 at 15:20

3 Answers3

5

Generally, you just need to worry about cancelling an event in the if/then branches of your validation logic that indicate a problem. If you don't hit those branches, the form submits as it normally would. This removes the need for you to manually indicate that you want the form submitted.

See comments inline below for details:

$('.enquiry-form-container form').submit(function (e) {
        var invalid = false;
        var isblank = false;
        // Loop through each input and check if valid or empty
        $('.validate').each(function () {
            if ($(this).hasClass('invalid')) {
                isInValid($(this));
                invalid = true;
                e.preventDefault();
                return;
            } else {
                // Any fields are blank
                if ($(this).val() === "") {
                    $(this).addClass('blank');
                    isblank = true;
                    e.preventDefault();
                    return;
                } else {
                    $(this).addClass('valid');
                    isValid($(this));
                    $(this).removeClass('blank empty');
                }
            }
        });

        // If we've gotten this far, the form is good and will be submitted.   

        // No need for an if/then/else here because you've already trapped 
        // the conditions that would prevent the form from being submitted
        // above.

        // Prevent submit to prevent duplicate submissions
        $(this).find(":submit").prop("disabled", true); 
    });

It's also a good idea to separate your validation code into its own function, so a reworked example would be:

$('.enquiry-form-container form').submit(function (e) {
  // You only need to worry about cancelling the form's submission
  // if the form is invalid:
  if (!validate()) {
    e.preventDefault();
    return;
  }

  // If it is valid, you don't need to interfere in that process, but
  // you can certainly do other "valid" operations:

  // Prevent submit from being clicked to prevent duplicate submissions
  $(this).find(":submit").prop("disabled", true);  
});

function validate() {
  // This function doesn't worry about cancelling the form's submission. 
  // Its only job is to check the elements, style them according to 
  // their validity (and, in a perfect world, the styling would be off-
  // loaded to anther function as well) and return whether the form is 
  // valid or not.

  var invalid = false;
  var isblank = false;

  // Loop through each input and check if valid or empty
  $('.validate').each(function () {
    if ($(this).hasClass('invalid')) {
      isInValid($(this));
      invalid = true;   
    } else {
      // Any fields are blank
      if ($(this).val() === "") {
        $(this).addClass('blank');
          isblank = true;
      } else {
        $(this).addClass('valid');
        isValid($(this));
        $(this).removeClass('blank empty');
      }
    }
  });

  // If invalid or isblank is true, there was a problem and false
  // should be returned from the function
  return !invalid || !isblank;
}
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • 1
    @Sébastien I appreciate the up votes, but some of edits you are making are arbitrary (removing blank lines in my code snippets) and may actually not be what I wanted as far as spacing goes. Please limit edits to corrections, not preference in code formatting. – Scott Marcus Dec 30 '17 at 00:53
  • OK sorry I have been enjoying your answers, but I'm afraid I may have done some damage now... I'll just stop for now. – Sébastien Dec 30 '17 at 00:55
  • @Sébastien No worries. Again, thanks for the up votes! – Scott Marcus Dec 30 '17 at 00:56
  • 1
    I was really shocked about how some good detailed posts had so few upvotes and they do deserve them. – Sébastien Dec 30 '17 at 00:57
1

I think the main problem for you is calling submit() from inside the handle of the submit. the better way to do this is cancel the request just when you see that there is invalid data.

$('.enquiry-form-container form').submit(function (e) {
        e.preventDefault();
        var invalid = false;
        var isblank = false;
        //Loop through each input and check if valid or empty
        $('.validate').each(function () {
            if ($(this).hasClass('invalid')) {
                isInValid($(this));
                invalid = true;
            } else {
                //Any fields are blank
                if ($(this).val() === "") {
                    $(this).addClass('blank');
                    isblank = true;
                } else {
                    $(this).addClass('valid');
                    isValid($(this));
                    $(this).removeClass('blank empty');
                }
            }
        });
        if (!invalid & !isblank){ //SEND
            $(this).find(":submit").prop("disabled", true); //Prevent submit to prevent duplicate submissions
            return true;
        } else { //DONT SEND
            return false;
        }        
    });
Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
Raghav Rangani
  • 843
  • 7
  • 23
1

I think the main problem for you is calling submit() from inside the handle of the submit. the better way to do this cancels the request just when you see that there is invalid data.

$('.enquiry-form-container form').submit(function (e) {
    // remove the e.preventDefault();
    var invalid = false;
    var isblank = false;
    //Loop through each input and check if valid or empty
    $('.validate').each(function () {
        if ($(this).hasClass('invalid')) {
            isInValid($(this));
            invalid = true;
        } else {
            //Any fields are blank
            if ($(this).val() === "") {
                $(this).addClass('blank');
                isblank = true;
            } else {
                $(this).addClass('valid');
                isValid($(this));
                $(this).removeClass('blank empty');
            }
        }
    });

    if (!invalid & !isblank){ //SEND
        $(this).find(":submit").prop("disabled", true); //Prevent submit to prevent duplicate submissions
        //$(this).submit(); // this should be removed
    } else { //DONT SEND
           e.preventDefault();
    }
});
Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131