Parent – Child Select Lists Revisited: Validating Selected Options Via jQuery And PHP

Recently received via e-mail, in response to my previous blog entry, “Using AJAX To Data Bind A Child Drop Down List Based On The Selected Option Of A Parent Select Control“:

Would you be interested in modifying your solution in 2 ways. One would be to have it contain a country downdown as well. So it would be parent, child, child. I looked into attempting it myself, but my head began spinning as I began looking at all the code. The next option that would be nice, would be to have some sort of Dropdown Validation. Once the state is selected and the city is not yet selected, i am able to hit the submit button. So, it would be nice to see a message that says “Please fill choose a city”. …

Thank you,

Brian

I’m actually going to address this in two posts, because the first request — nested parent-child select lists — will take a bit more time to demo and describe than I have at the moment.

However, form validation — namely, ensuring the user chooses valid parent and child values from each select list — is something I can describe quickly and without extensive coding. And it’s a subject I should address, since my previous post noted that server-side validation was important to this solution, but I didn’t describe how to do it.

jQuery Form Validation

The old-school way to validate a form in JavaScript is to invoke the onsubmit event of the form element.

That is, we simply create a JavaScript function that checks whether the selected options within the parent and child lists have a non-empty-string value. If either contained an empty value, we would pop up an alert and shift focus to the select that needs a valid option chosen.

But we’ve already incurred the overhead of jQuery, so why not spice things up a bit? Rather than using an ugly old alert, let’s really draw the user’s attention to elements that need correcting.

In this case, we’ll turn the list with a bad value pink, and append on a message asking the user to select a proper option.

To do that, we first need to add a couple of span elements to our form, one following each of the select elements, to receive the error message when appropriate.

Why span elements? Because div elements are properly used to group sections of markup; that’s why you use them to control layout. And a p or other text element would require extensive formatting to get to line up properly in relation to other form elements. A span is intended to mark HTML for some sort of special consideration; and that’s exactly what we are doing here, showing text under special conditions.

We also need to add a new event handler for the child list; it calls a yet-to-be-defined function called checkOption(), which takes as its argument the id of the select. I have also added a handler for the form’s onsubmit event, which will also be described momentarily. Finally, I have added a submit button, so the form can be posted.

<form id="form1" name="form1" method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>" onsubmit="return checkForm();">
    <label>
        Select a state:&nbsp;
        <select id="state" name="state" onchange="bindCity()">
            <!-- php code to insert parent records goes here -->
        </select>
        <span id="statemsg" class="warning"></span>
    </label>
    <br />
    <label>
        Select a city:&nbsp;
        <select id="city" name="city" disabled="disabled" onchange="checkOption('#city')">
            <option>Select a state ...</option>
        </select>
    </label>
    <span id="citymsg" class="warning"></span>
    <br />
    <input type="submit" id="submit" name="submit" value="Submit" />
</form>

Note that each new span gets an id attribute that starts with the id of one of the lists and ends in “msg.” That’s because we want to be able to efficiently associate each span with its attendant select.

If we use the id of the select and append some other string to it — e.g., “state” + “msg” = “statemsg”, “city” + “msg” = “citymsg” — then we can easily use the same JavaScript function to handle validation of both lists.

The JavaScript Functions

We’ll need three JavaScript functions to complete this task, and we need to amend the bindCity() function slightly to accommodate this validation scheme.

The first step is writing a function that will clear the error message span for either list, and set the background color of the select in question back to white.

function clearMessage(s) {
	//clear status message for element
	$(s + "msg").html('');
	$(s).css("background-color", "white");
}

Note that at line 3, we concatenate the argument of the function — in this case, the id of the select in question — with “msg”, in order to get the id of the span we want to clear.

The checkOption() function, previously noted, does the actual validating. First, it clears the error message for the select in question; next, it sees if the selected option has an empty string for a value. If the value is empty, then we set the background color of the select to pink, add an error message, and disable the submit button.

function checkOption(s) {
	clearMessage(s);
	//invalid choice
	if($(s).val() == "") {
		//place error message, colorize select list, disable submit button
		$(s).css("background-color", "pink");
		$(s + "msg").html("&laquo; Please make a valid choice");
		$("#submit").attr("disabled", true);
		return false;
	}
	//element OK, enable submit button
	$("#submit").removeAttr("disabled");
	return true;
}

We need one more function. Because we’re using the onchange event handler to trigger validation for each list, it’s possible the user will simply submit the form immediately after page load, without making a selection; it’s also possible he will submit the form after selecting a proper parent option, but not a valid child option.

So, it turns out we still need a handler for the form’s onsubmit event. This function will call checkOption() for each of the selects; if both pass, the form is submitted; otherwise, the submit button is disabled and the form is not processed.

function checkForm() {
	//check both select lists for valid values
	var ok1 = checkOption('#state');
	var ok2 = checkOption('#city');
	if(ok1 && ok2) {
		return true;
	}
	else {
		//bad option in one or both lists, disable submit button
		$("#submit").attr("disabled", true);
		return false;
	}
}

Finally, we need to tweak the bindCity() function to use our new form validation scheme. We do that by calling clearMessage(), then using checkOption() to see if the state select has a non-empty value. If so, we go ahead and update the child list.

function bindCity() {
	//disable child select list
	$("#city").attr("disabled", true);
	//clear error message if it exists
	clearMessage("#city");

	//check selection
	if(checkOption("#state")) {
		//clear child select list's options
		$("#city").html('');

		//querystring value is selected value of parent drop down list
		var qs = $("#state").val();
		//show message indicating we're getting new values
		$("#city").append(new Option('Getting city list ...'));
		//declare options array and populate
		var cityOptions = new Array();
		$.get("citylist.php?statecode=" + qs, function(data) {
				eval(data);
				if(cityOptions.length > 0) {
					addOptions(cityOptions);
				}
			}
		);
	}
	else {
		$("#city").val() = '';
	}
}

And with that, we have a fully functional, jQuery-powered, pretty-in-pink validation script.

PHP Validation

While it’s a lot less glamorous, validating our inputs with PHP is far more important than a jQuery validation script. Remember, users can not only disable JavaScript, they can add, modify and delete any JavaScript on / to your page. Therefore, we need to ensure any form values we get back from a page are in range — that is, contain values we anticipate getting.

The easiest way to do that is with regular expressions.

A quick aside on sanitizing form values in PHP: PHP 5.2.0 and later ships with data filtering extensions built in. These filters and sanitizers are exceedingly handy, and at some point I will demonstrate using them. For now, because this problem is so limited in scope, I’m going to use PCRE.

We know the value of state should be two capital letters, and the value of city should be a mix of capital and lower-case letters, with an occasional apostrophe, period or space. So, a couple of regular expressions nested in an if-else statement is all we need for validation

	if(isset($_POST['submit'])) {
		if(!preg_match('/^[A-Z]{2}$/', $_POST['state'])) {
			echo "<p><strong>You did not select a proper state option. Please try again.</strong></p>\n";
		}
		elseif(!preg_match('/^[A-Za-z\'\.\s]{3,25}$/', $_POST['city'])) {
			echo "<p><strong>You did not select a proper city option. Please try again.</strong></p>\n";
		}
		else {
			echo "<p>You selected $_POST[city], $_POST[state].</p>\n";
		}
	}

And that’s all there is to it. You can see a working demo here:

http://www.dougv.com/demo/ajax_parent_child_select_1

To test just the PHP part of the script, simply disable JavaScript.

I distribute all code under the GNU GPL Version 3. There’s no download file this time; when I do the multiple child select list demo, I’ll provide a download file that encompasses this entry.

All links in this entry on delicious: http://delicious.com/dhvrm/parent-child-select-lists-revisited-validating-selected-options-via-jquery-and-php

6 thoughts on “Parent – Child Select Lists Revisited: Validating Selected Options Via jQuery And PHP

  1. Pingback: Parent – Child Select Lists Revisited: Multiple Parent – Child Select Lists Via PHP, MySQL And jQuery » dougv.com « The Web home of Doug Vanderweide

  2. Abdullahi M. A

    I run the downloaded file on my Ubuntu and it works fine, however when I attempted running it on my windows 7 and WAMPSERVER it refuses to run even though both are running apache 2, php5

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Current ye@r *