Handling form validation and submission with JQuery

I’m having a little trouble understanding the order of events that take place when I click a submit button and then perform some validations and if all that is okay, proceed with form submission. I have a form with several submit buttons, two of which I will discuss.

One is an Update button that allows elements of the form to be edited and submitted to be saved. It does not require all the information to be filled out but if certain elements are filled out, like a telephone number, it is validated.

The second is a submit button that indicates the form is complete and is ready for 100% validation and if it passes it will be submitted and the status will be changed.

So what I have tried to do without success is something like the following:

$('#submitupdate').click(function(e) {
	e.preventDefault();
	var allok = valrequired();

	if (allok) {
		$('#form-modpo').submit();
	}
});

The form is created using

   "<form id=\"form-modpo\" name=\"form-modpo\" action=\"" . C_WSERVER . "/Process_PO\" method=\"post\">\n"

And the button using

"<input type=\"submit\" id=\"submitupdate\" name=\"submit\" value=\"Update\">\n"

Id does the validation okay and gives me the correct return value but it never actually submits the form. Thanks

Progress. Part of my problem is the way the calls are being processed by javascript and unless I string them together inside one another they just process is whatever order happens. In my case it is important that they process in order. The other problem still there is that form.submit() kind of assumes one submit button.

So I will try a click() type of event rather than submit and see if that helps. I would still like to get any feedback regarding this if anyone cares to chime in. Thanks

Not sure I understand what is going on. I have the following two lines

console.log('submit');
$('#form-modpo').submit();

In the console I see “submit” but the form never submits. Isn’t that what submit() is supposed to do, force the form to submit? Or is it just listening for the form to submit and then you tell it what to do? If it is the latter, how do I get the form to submit since I have to use e.preventDefault on the button click event or it just bypasses everything and submits the form without doing any validation.

Again, I want to handle this more on a procedural level but have yet to find a way to control the sequencing of events and functions.

I would suggest that you instead allow the form submit to occur, and only prevent the default action if things aren’t all ok.

$('#submitupdate').click(function(e) {
    var allok = valrequired();

    if (!allok) {
        e.preventDefault();
    }
});

That way you don’t have to try and re-enable something that you have already prevented.

1 Like

Thanks for the input and that is the way I would expect it to work. I swear I have tried that but will try again in the morning to be sure. One other thing and thus may be causing part of the problem. In the validations I make an Ajax call to the server.

I will report back how it works. Thanks again.

Had to try it tonight so I can sleep. Ya, that doesn’t work. Here is the actual code that is running

	$('#submitupdate').click(function(e) {
		if ($('#form-modpo input[name="poname"]').val() == "") {
			$('#form-modpo input[name="poname"]').val(dbponame);
			$('#form-modpo input[name="poname"]').addClass('valmsgborder');
			$('.displaypopage #valponame').replaceWith('<div id="valponame" class="valmsg">PO Name is a required field, you cannot delete it</div>');
			e.preventDefault();
		} else {
			var ponameval = $('#form-modpo input[name="poname"]').val();
			var passdata = {"poname": ponameval};
			$.ajax({
				url:'/js/PoPage_sub.php', 
				asynch: false,
				data: passdata, 
				type: 'POST', 
				success: function(data) {
					var jsonobj = jQuery.parseJSON(data);
console.log(jsonobj);
					if (jsonobj[0] == false) {
						$('#form-modpo input[name="poname"]').addClass('valmsgborder');
						$('.displaypopage #valponame').replaceWith('<div id="valponame" class="valmsg">The Name \"' + $('#form-modpo input[name="poname"]').val() + '\" already exists for this company, please enter another name</div>');
						$('#form-modpo input[name="poname"]').val(dbponame);
						e.preventDefault();
					} else {
						$('#form-modpo input[name="poname"]').removeClass('valmsgborder');
						$('.displaypopage #valponame').replaceWith('<div id="valponame" class="valmsg"></div>');
						if ($('#form-modpo input[name="poname"]').val() != dbponame) {
							$('#hdrquotename').replaceWith('<span id=\"hdrquotename\">' + $('#form-modpo input[name="poname"]').val() + '</span>');
						}
					}
				}, 
			});
		}
	});

It’s pretty simple. The first part checks to see if it is blank. If it is, it works as expected and the js handles the validation and the messaging, the form never gets submitted. The second part checks in the db to see if the name exists on another document. If it does, it returns false so the click should be rejected.

But it’s not. The form is being submitted and the message that I am getting is the one generated on validation on the php side. Of that I am sure. So it appears that even though async is false, it is bypassing the return of the ajax and just moving on to submit the form. Unless I am missing something completely, which is entirely possible also.

You have a e.preventDefault() on line 22, which get’s called AFTER the form has successfully submitted. You need that to be outside the success callback and before the $.ajax.

In fact since both if branches call the preventDefault you only need it once at the top. So this:

$('#submitupdate').click(function(e) {
	e.preventDefault();	// Moved to the top

	if ($('#form-modpo input[name="poname"]').val() == "") {
		$('#form-modpo input[name="poname"]').val(dbponame);
		$('#form-modpo input[name="poname"]').addClass('valmsgborder');
		$('.displaypopage #valponame').replaceWith('<div id="valponame" class="valmsg">PO Name is a required field, you cannot delete it</div>');
	} else {
		var ponameval = $('#form-modpo input[name="poname"]').val();
		var passdata = {"poname": ponameval};
		$.ajax({
			url:'/js/PoPage_sub.php', 
			asynch: false,
			data: passdata, 
			type: 'POST', 
			success: function(data) {
				var jsonobj = jQuery.parseJSON(data);
				console.log(jsonobj);
				if (jsonobj[0] == false) {
					$('#form-modpo input[name="poname"]').addClass('valmsgborder');
					$('.displaypopage #valponame').replaceWith('<div id="valponame" class="valmsg">The Name \"' + $('#form-modpo input[name="poname"]').val() + '\" already exists for this company, please enter another name</div>');
					$('#form-modpo input[name="poname"]').val(dbponame);
				} else {
					$('#form-modpo input[name="poname"]').removeClass('valmsgborder');
					$('.displaypopage #valponame').replaceWith('<div id="valponame" class="valmsg"></div>');
					if ($('#form-modpo input[name="poname"]').val() != dbponame) {
						$('#hdrquotename').replaceWith('<span id=\"hdrquotename\">' + $('#form-modpo input[name="poname"]').val() + '</span>');
					}
				}
			}, 
		});
	}
});

I’ve tried this also. If you put the e.preventDefault at the top the form never submits. The idea is that if it passes the first validation testing for a blank value and then goes through the ajax validation which queries the database to see if the name already exists, then I want to let the click() process.

I only want to preventDefault if it fails both validations. Does that make sense?

And on my full example the first validation works fine. If the field is blank, the preventDefault works just fine and the validation is handled by the js code. The problem comes in the .ajax validation in that the click() event proceeds right past the .ajax code and does not wait for it to process. And that is the same whether asynch is true or false.

It sounds like you are using the wrong event in this case. If you are wanting to control whether the form is submitted or not, it really needs to be the submit event from which you do that.

What happens when you change the click event to the submit event instead?

No, it behaves exactly the same way as with submit. And to be sure, I just tested it again. Let me be clear.

The first part of the validation is just to see if the field is blank. If it is, there is a preventDefault in that block of code. If I use the click event on the submit button or the submit event on the form, either way the form is changed from within the js and the form is not submitted because if the preventDefault.

The ajax code is to check the name against the database to see if it exists. If it does, it returns false and I want the preventDefault to stop the form submission. but it does not. If the name exists in the database the form is submitted anyhow. It never has a chance to get to the prevent default because I assume the js just continues on executing while the ajax call is being made. I would have thought that that is what the asynch property is for. But it doesn’t matter which way that is set, true or false.

So brief answer, both submit and click behave exactly the same way. It is the .ajax call that is the problem.

One other point. It would also seem that this would work fine if I used the preventDefault at the beginning as suggested by Oz if there was a way to force the form submission from a certain button. I tried using

$(#form).submit() 

but that does nothing that I can see. Is there a way to force a form submission? Not listed to see when it has submitted, but to cause it to submit.

In this case, because the ajax event using being used you should not even risk submitting the form until the ajax method is happy with the results.

Use the click event and immediately prevent the default, so that later on when the ajax method is happy with things it can separately trigger the submit event on the form.

That’s what I’m trying to do but have yet to find a way to do it. Wait until the ajax has done it’s thing and given me a true or false and then I iwill either let the form submit or stop the submit.

No, that is not the appropriate way to think about things, for when the ajax request occurs there is no waiting to choose to let the form submit or not. The ajax request is a separate process that occurs outside of the normal flow of the code.

Instead, you need to always prevent the default click action in the function that performs the ajax request.
Later on in the success function of the request, when a response comes back, that is when you choose to trigger the submit event of the form.

Here’s an example of the type of logic that’s involved.

function responseHandler(data) {
    if (...) { // data is okay
        $('#form-modpo').submit();
    }
}

$('#submitupdate').on('click', function (evt) {
    evt.preventDefault();

    if (...) { // any problems
        // handle them
    } else {
        $.ajax({
            // ...
            success: responseHandler
        });
    }
});

Most people place the response handler directly within the code, which tends to muddy the understanding of what’s going on.

With the above it’s easier to see that the click event is always prevented, stopping the form from submitting via that button. The form is only submitted when the ajax request is successful with the appropriate kind of data.

okay, something else is going on here. I simplified this to the lowest level because it’s just not making sense with what everyone is saying about how it should work and how I think it should work. So here is what I tried.

function responseHandler(data) {
	alert('in handler');
	alert(data);
	$('#form-modpo').submit();
}

$('#submitupdate').click(function(e) {
	e.preventDefault();
	alert('in click');
	var ponameval = $('#form-modpo input[name="poname"]').val();
	var passdata = {"poname": ponameval};
	alert(ponameval);
	alert(passdata['poname']);
	$.ajax({
		url:'/js/PoPage_sub.php', 
		data: passdata, 
		method: 'POST', 
		success: responseHandler(data),
		error: alert(textStatus),
		completed: alert(textStatus)
	});
});

and here is the PoPage_sub.php on the other side

<?php

	$retval = 1;	
	echo $retval;

When I execute this I see the alerts for “in click”, panemeval and passdata[‘poname’] and they are all fine every time. Then I see nothing. No additional alerts after that. So it looks like the ajax is either not getting processed for some reason or is getting lost. I welcome any pointers on this as to what to try next.

If you execute a function as you currently are with alert, it’s only the return value from it that is assigned, which tends to be undefined.

It has to be actual functions that are assigned to the error and completed sections, so the following should work for you instead:

$.ajax({
    ...
    error: function (textStatus) {
        alert('Error: ', textStatus);
    },
    success: function (textStatus) {
        alert('Success: ', textStatus);
    }
});

ok, thanks again for the input; and the patience. :blush: I finally got it.

I’ll post the full code below but basically in the click event I put a e.preventDefault and a setTimeout function that has a callout to another function that checks the values of the validation variables, which are global, and then calls the submit if they are all true. That gives the .ajax time to process and set the value of the validation variable. The timeout seems to work fine at 300 milliseconds and probably could be shorter.

The calls to the validation functions come from the focusout events and also from the click event if a field is blank, which would indicate they never visited the field.

So far it seems to work flawlessly. Famous last words of course.

var ponameok = true;
var projecttagok = true;

$('#form-newquote input[name="poname"]').focusout(function() {
    valponame();
});

$('#form-newquote input[name="projecttag"]').focusout(function() {
    valprojecttag();
});

function submitFormCheck() {
    if (ponameok == true && projecttagok == true) {
        $('#form-newquote').submit();
    }
}
    
$('#form-newquote input[type="submit"]').click(function(e) {
    e.preventDefault();
    if ($('#form-newquote input[name="poname"]').val() == "") {
        valponame();
    }
    if ($('#form-newquote input[name="projecttag"]').val() == "") {
        valprojecttag();
    }

    setTimeout(function() {
        submitFormCheck()}, 
        300);
});

function valponame() {
    var retval = true;
    if ($('#form-newquote input[name="poname"]').val() == "") {
        $('#form-newquote input[name="poname"]').css('border', '2px solid red');
        $('.mainoptions #valponame').replaceWith('<div id="valponame" class="valmsg">Quote Name is a required field</div>');
        ponameok = false;
    } else {
        var ponameval = $('#form-newquote input[name="poname"]').val();
        var passdata = {"poname": ponameval};
        $.ajax({
            url:'/js/MainPage_sub.php', 
            asynch: false,
            data: passdata, 
            type: 'POST', 
            success: function(data) {
                var jsonobj = jQuery.parseJSON(data);
                if (jsonobj[0] == false) {
                    ponameok = false;
                    $('#form-newquote input[name="poname"]').css('border', '2px solid red');
                    $('.mainoptions #valponame').replaceWith('<div id="valponame" class="valmsg">This Quote Name already exists for this company, please enter another name</div>');
                } else {
                    ponameok = true;
                    $('#form-newquote input[name="poname"]').css('border', '2px solid green');
                    $('.mainoptions #valponame').replaceWith('<div id="valponame" class="valmsg"></div>');
                }
            }
        });
    }
}

function valprojecttag() {
    if ($('#form-newquote input[name="projecttag"]').val() == "") {
        $('#form-newquote input[name="projecttag"]').css('border', '2px solid red');
        $('.mainoptions #valprojecttag').replaceWith('<div id="valprojecttag" class="valmsg">Project Tag is a required to enter a quote</div>');
        projecttagok = false;
    } else {
        $('#form-newquote input[name="projecttag"]').css('border', '2px solid green');
        $('.mainoptions #valprojecttag').replaceWith('<div id="valprojecttag" class="valmsg"></div>');
        projecttagok = true;
    }
}

Woah - what on earth! You are setting yourself up for a grand failure whenever there are networking delays!

Instead of using the setTimeout, make use of the success event from the ajax request. When the response successfully comes back the success method will be triggered. That is where you can then check that everything is right with the form.

You can easily achieve this by moving the submitFormCheck out of the setTimeout and instead placing it at the end of the success method. That way your code will be much more robust and not vulnerable to problems of networking and timing issues.

I’ll try again but when I tried that before it never worked. In any event the worst that happens is it bypasses the Ajax and submits the form which is fine. All the code is there to validate it and process it.

But let me ask. Why would a $(#form).submit() not work. I have one where a log entry shows right before the submit and then right after but the submit never executes.