Dynamically Create Input Boxes with Validation
First, let's do a test: Do form values with array elements for names of INPUT boxes get a response from JavaScript when we try to read the results of the form input? How about PHP—can it get a result from this scenario? The answer: JavaScript NO and PHP YES. The test below works in JavaScript if name1 and name2 are the INPUT box names and document.form.name1.value and document.form.name2.value are used in the script. Even if the form was created dynamically with DOM createElement and setAttribute statements, it would still work in Firefox, but not IE, due to a lovely IE bug that screws up using setAttribute to define an INPUT box name. That's the bad news, but the good news is that even though it doesn't name it right for HTML and JavaScript, it DOES name it right for PHP in any browser, including IE. And you'll see that this is so even when there is no array element number, in dynamically created INPUT boxes. The example below uses the numbers, i.e., a[1]. But in the short PHP code block below that, we use a FOR loop to get 3 arrays of POSTed values that were all sent with empty array element brackets. (It's just a small chunk of code to make the point, not at all supposed to be complete.)
So anyway, var a = 'people[]'; a = a.toString(); was the only processing the name attribute got before using it with setAttribute to set the INPUT box names. An empty-brackets array element somehow gets translated by PHP into sequential numeric array values in all main browsers. Even when we created dozens of INPUT boxes with the same people[] name, and then loaded them up by filling out the form, PHP came through with flying colors! It makes a guy feel these PHP dudes actually know what they are doing!
Anyway, with the backstory revealed, let's go through the code below and see how one can create INPUT boxes dynamically including validation. To make it more realistic, we ran the INPUT box creation script from a button in a normal, NON-dynamically created form.
Here's the scenario: In a form where a user inputs a number to represent the number of people he wants to add—with usernames and passwords—to a MySQL database table, we have no way to know ahead of time how many people he will want, therefore we cannot prepare the INPUT boxes ahead of time. So we do it on the fly, but not with document.write scripts. We use DOM functions to stick the INPUT boxes we create into the document legitimately, to give the code the very best chance of succeeding in the most browsers. So createElement, setAttribute, and appendChild are utilized.
TEST SCRIPT
<?php
$b=$_POST['a'][0];$u=$_POST['a'][1];echo $b." ".$u;
?>
<html>
<head>
<script type="text/javascript">
function doit(){
var x=document.form.a[0].value; alert(x);
var y=document.form.a[1].value; alert(y);
return false}
</script>
</head>
<body>
<FORM action="12345test.php" onsubmit="return doit()" method="post" name='form'>
<INPUT id='vinb1' type="text" name="a[0]" value=''>
<INPUT id='vinb2' type="text" name="a[1]" value=''>
<INPUT type="submit" value="Send">
</FORM>
</body>
</html>
SMALL PART OF A PHP SCRIPT THAT SHOWS HOW THE LONG SCRIPT BELOW IT POSTS INPUT VALUES AS ARRAYS TO A PHP SCRIPT FOR ENTERING IN A MYSQL TABLE
$num=$_POST['num'];
for($i=0;$i<$num;$i++){
$b=$_POST['people'][$i];$u=$_POST['uname'][$i];$p=$_POST['upassword'][$i];
$j= $i + 1;
mysql_query("INSERT INTO $a (Firstname, ID, Status, Comment, Username, Password)
VALUES('$b','$j','99','','$u','$p')");
}
The first thing to do to create a form with both standard INPUT boxes and dynamically created INPUT boxes was to disable submitting the form by pressing Enter. We needed to control the input and submission process as much as possible. So we stopped the default action of this key by trapping that window event, below.
Next, we used a separate input validation script for each of the 3 input fields: user first name, username, and password. Note that if illegal characters are entered, the validation routines replace the user input with a 'SORRY! (FIX)' message, then urge them to enter the correct types of characters and press SHIFT TAB. This is needed since the validation script is triggered by the onChange event, and the onChange event occurs when a control loses the input focus and its value has been modified since gaining focus. This attribute applies to the following elements: INPUT, SELECT, and TEXTAREA. In applying it to our INPUT element, we realized that the losing of the focus (leaving the INPUT box after entering data) was one of the prerequisites for the event triggering. So when the user TABs to the next INPUT box or clicks the mouse cursor in a different INPUT box, the onChange event runs the validator. So this means that as they leave the box they'll see the alert message if they goofed, however, they'll be in a different INPUT box by now. Hence the need for the SHIFT TAB to put them back in the INPUT box that used to have bad input but now has the 'SORRY! (FIX)' message. Happily, as they enter the box, the text is selected so that as soon as they enter new content the error message automatically disappears with no user action required. So each of the 3 input fields: user first name, username, and password have their own scripts and regular expressions validators and flags.
In the FORM tag, an onsubmit="return validatepassword()" is used to force validation before submission is accepted, since the return before the function call gives the function a chance to accept or deny the validation. This is so because if the validation is "true," the function uses a return true, but if the function finds any error, the script that detects this uses a return false, since the validation is "false." The reason we could not use return false with the validator functions just described is that they were run by an onChange, not an onSubmit.
Anyway, in the validatepassword() function we first check to see if the "Add people before Registering" button in the form was clicked, which is required. If clik=1, it was clicked. If not, the validation fails. Next, if the dynamically created INPUT boxes were left blank or with bad input, their validation flags will be zero, so they get a message and a return false—the validation failed. Return false is allowed in the script because the validatepassword() function (which validates plenty besides passwords) was run by an onSubmit from the FORM tag. Even though, if their cursors have never been in the dynamically created INPUT boxes, we cannot check what they DID type in, we can use these boxes' flags to detect what they DID NOT type in, and a flag equaling zero means they created INPUT boxes that they ignored (or defiled with bad input)—a definite nono. Next there are standard regular expressions validators for user names, passwords, email addresses, etc.
Way down the script near the end, we have a button next to an INPUT box where you are supposed to indicate the number of PSB members you want, and the button runs a function, sending it the number of people desired as a parameter: AddFormFields(document.peopleForm.num.value). Let us look at that function. AddFormFields() uses a FOR loop to go through however many people are desired and creates 3 times that many INPUT boxes, since we need to get (from the administrator doing the typing) the First Name, User Name, and Password of each member. Notice we turn 'people[]' into a string and send it to the CreateFormTextInput() function where we create an INPUT box and set its attributes, including the name attribute where the empty-brackets array element name is used.
Notice that in formField.style.cssText = 'position:absolute;left:160px;top:'+t+'px;'; we give the position of the INPUT box in the form but instead of using the DOM's CSS properties, we use a sneaky cssText style. Then we use FormField.onchange=function(){validate(this);} to attach the onChange event to the INPUT box, directing it to run the validate() function. The special "this" parameter tells that function that it's the INPUT box field with the event in it that needs validation. The "return formField" at the end of CreateFormTextInput() tells us that this newly assembled INPUT element is to be taken back to the function that called it: AddFormFields().
But before we append this INPUT box to the document, we need labels for these 3 INPUT box columns. So we add them, creating a text node with bizarre looking spacing. Why not use spaces? They get collapsed into 1 space by the browsers. Why not use ? They don't work any better than the spaces do in a text node. What we used is the only thing that will work in this context: the Unicode value for a non-breaking space which is "\u00a0".
So after creating such a bizarre text node we use appendChild to attach it to the form and then attach the INPUT boxes as well, again with appendChild. The first column of INPUT boxes all have the same name attribute: 'people[]'. The next 2 columns get named 'uname[]' and 'upassword[]'. This script for dynamically creating validated INPUT boxes with the legitimate DOM method seems to work okay in the main browsers. Happily, IE's nasty little bug did NOT derail the process, since PHP seems to be able to sort out input from columns of INPUT boxes with identical empty-brackets array element names without even breaking a sweat! (Seriously—nice going, guys!)
<html>
<head>
<script type="text/javascript">
document.onkeypress = function(e){
e = e? e : window.event;
var k = e.keyCode? e.keyCode : e.which? e.which : null;
if (k == 13){
if (e.preventDefault){e.preventDefault();return false;}}
return true;}
var flag=0;var flag1=0;var flag2=0;var clik=0;var field="";
function validate(field){
var ck_userfirstname = /^[A-Za-z0-9_]{1,12}$/;
if (field.value.search(ck_userfirstname)==-1)
{field.value='SORRY! (FIX)';alert("Please only enter letters, numbers and underline for users first names, and enter 1 to 12 characters. Please press SHIFT TAB after clicking OK.");};flag=flag+1;}
function validateUsername(field){
var ck_user_name = /^[A-Za-z0-9_]{1,20}$/;
if (field.value.search(ck_user_name)==-1)
{field.value='SORRY! (FIX)';alert("Please only enter letters, numbers and underline for users usernames, and enter 1 to 20 characters. Please press SHIFT TAB after clicking OK.");};flag1=flag1+1;}
function validatePassword(field){
var ck_pw = /^[A-Za-z0-9!@#$%^&*()_]{6,12}$/;
if (field.value.search(ck_pw)==-1)
{field.value='SORRY! (FIX)';alert("Please only enter 6 to 12 letters, numbers and these for users passwords: !@#$%^&*()_\nPlease press SHIFT TAB after clicking OK.");};flag2=flag2+1;}
function validatepassword(){
if(clik==0||clik<num_people){alert("Please enter number of people to add.");return false}
if(flag==0){alert("Please only enter letters, numbers and underline for users first names, and enter 1 to 12 characters. Please press SHIFT TAB after clicking OK.");return false}
if(flag1==0){alert("Please only enter letters, numbers and underline for users usernames, and enter 1 to 20 characters. Please press SHIFT TAB after clicking OK.");return false}
if(flag2==0){alert("Please only enter 6 to 12 letters, numbers and these for users passwords: !@#$%^&*()_\nPlease press SHIFT TAB after clicking OK.");return false}
var ck_username = /^[A-Za-z0-9_]{4,30}$/;
if (document.peopleForm.username.value.search(ck_username)==-1)
{alert("Please only enter letters, numbers and underline for administrator user name, and enter 4 to 30 characters.");return false}
var ck_groupname = /^[A-Za-z0-9_]{4,20}$/;
if (document.peopleForm.groupname.value.search(ck_groupname)==-1)
{alert("Please only enter letters, numbers and underline for group name, and enter 4 to 20 characters.");return false}
var ck_password = /^[A-Za-z0-9!@#$%^&*()_]{6,12}$/;
if (document.peopleForm.admin_password.value.search(ck_password)==-1)
{alert("Please only enter 6 to 12 letters, numbers and these for administrator password: !@#$%^&*()_");return false}
var ck_email = /^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/;
if (document.peopleForm.email.value.search(ck_email)==-1)
{alert("That email address is not valid.");return false}
return true}
var num_people=0;
function CreateFormTextInput (its_name,t) {
var formField = document.createElement ("input");
formField.setAttribute ('type', 'text');
formField.setAttribute ('value', "");
formField.setAttribute ('name', its_name);
formField.setAttribute ('size', '12');
formField.style.cssText = 'position:absolute;left:160px;top:'+t+'px;';
formField.onchange=function(){validate(this);};
e = document.getElementById("form");
z=t+26;
e.style.height=z+"px";
return formField;
}
function CreateFormTextInputUsername (its_name,t) {
var formField = document.createElement ("input");
formField.setAttribute ('type', 'text');
formField.setAttribute ('value', "");
formField.setAttribute ('name', its_name);
formField.setAttribute ('size', '20');
formField.style.cssText = 'position:absolute;left:322px;top:'+t+'px;';
formField.onchange=function(){validateUsername(this);};
e = document.getElementById("form");
z=t+26;
e.style.height=z+"px";
return formField;
}
function CreateFormTextInputPassword (its_name,t) {
var formField = document.createElement ("input");
formField.setAttribute ('type', 'text');
formField.setAttribute ('value', "");
formField.setAttribute ('name', its_name);
formField.setAttribute ('size', '12');
formField.style.cssText = 'position:absolute;left:540px;top:'+t+'px;';
formField.onchange=function(){validatePassword(this);};
e = document.getElementById("form");
z=t+26;
e.style.height=z+"px";
return formField;
}
function AddFormFields (num_people){
for(i=0;i<num_people;i++){
var d= 280+(i*22);
var a = 'people[]'; a = a.toString();
var textBox = CreateFormTextInput(a,d);
var br = document.createElement("br");
document.peopleForm.appendChild(br);
if(i==0){var x=document.createTextNode('\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 First Name \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Username \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Password');}
if(i==0){document.peopleForm.appendChild(x);}
document.peopleForm.appendChild(textBox);
var a = 'uname[]'; a = a.toString();
var textBox2 = CreateFormTextInputUsername(a,d);
document.peopleForm.appendChild(textBox2);
var a = 'upassword[]'; a = a.toString();
var textBox3 = CreateFormTextInputPassword(a,d);
document.peopleForm.appendChild(textBox3);}}
</script>
</head>
<body onload='fix()'>
<div style='position:absolute;margin:0 0 0 150px'>
<form name="peopleForm" id='form' method="post" onsubmit="return validatepassword()" action="register.php" style='background-color:#ccc;border:4px solid blue;width:700px;'>
<table width="600" border="1" align="center">
<tr>
<td>Administrator's Username:</td>
<td><label>
<input name="username" type="text" id="username" size="30">
</label></td>
</tr>
<tr>
<td>Administrator's Password:</td>
<td><input name="admin_password" type="password" id="password" value="" size="30"></td>
</tr>
<tr>
<td>Group Name:</td>
<td><input name="groupname" type="text" id="groupname" value="" size="20"></td>
</tr>
<tr>
<td>Email:</td>
<td><input name="email" type="text" id="email" size="30"></td>
</tr>
<tr>
<tr>
<td>Number of people to add:</td>
<td><input name="num" type="text" id="num" size="2" value=''><input type="button" value="Add people before Registering" onclick="clik=1;AddFormFields(document.peopleForm.num.value)"></td>
</tr>
<tr>
<td> </td>
<td><label>
<input name="register" type="submit" id="register" value="Register">
</label> as Administrator of a Business PSB™ group.</td>
</tr>
</table>
</form>
</div>
</body>
</html>