If you’re like me, you have certain form fields that need to allow users to add an option. For example, if you wanted to ask a user filling out a form for their university, you can’t realistically expect to have a table with all the possibilities from around the world. Instead, you can have what I call a select box other field (a.k.a. list box), which is a select box (single or multiple) that includes an accompanying text box field for a user to add a new entry to whatever table is being used to generate the select box options. Interestingly, I have been using such a field for years, but a quick Google search turned up little of reference value.
In the past I wrote special execution code that checked to see if the text box field had any input, performed some basic validation (error checking), and then added the text box text if appropriate. Now that AJAX has come along, I thought why not use it to allow the user addition to be instantaneous. Below I explain how I accomplished my goal. For the impatient among you, I give you the code first.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | <script> // --------------------------- // --- Browser Support Code // --------------------------- function GetXmlHttpObject() { var xmlHttp=null; try { // Firefox, Opera 8.0+, Safari xmlHttp=new XMLHttpRequest(); } catch (e) { // Internet Explorer try { xmlHttp=new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); } } return xmlHttp; } // --------------------------- // --- ajaxAddOther // --------------------------- function ajaxAddOther (table,field,value,otherid,selectid) { var statusid = selectid+'-status'; ajaxRequest=GetXmlHttpObject(); if (ajaxRequest==null) { alert ("Your browser does not support AJAX!"); return; } var sel = document.getElementById(selectid); var other = document.getElementById(otherid); var othervalue = other.value; // Create a function that will receive data sent from the server ajaxRequest.onreadystatechange=function() { if(ajaxRequest.readyState==4) { var split = ajaxRequest.responseText.split(" "); var otherid = split[0]; var start = ajaxRequest.responseText.indexOf("<"); var response = ajaxRequest.responseText.substr(start); // if we had a successful message, add the "other" text as new selected option if (response.match("Added")) { // if option field and option display are different we assume the value is the next insert id which is returned from the php function, o/w just use the option display for display and value if (otherid!="" && field!=value) { sel.options[sel.options.length] = new Option(othervalue,otherid,false,true); } else { sel.options[sel.options.length] = new Option(othervalue,othervalue,false,true); } } document.getElementById(statusid).innerHTML=response; } else { document.getElementById(statusid).innerHTML='<span class="working">Adding</span>'; } } var url = "ajax.php?function=addother&table="+table+"&field="+field+"&othervalue="+othervalue; ajaxRequest.open("GET",url,true); ajaxRequest.send(null); } </script> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?php // ---------------------------------------------------------------------------- // --- ajax.php // ---------------------------------------------------------------------------- include_once ("includes/db.php"); $db = new db(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST); $function = $_GET["function"]; $table = $_GET["table"]; $othervalue = $_GET["othervalue"]; $field = $_GET["field"]; switch ($function) { case "addother": // --- only add if doesn't already exist, so check first... $sql = "SELECT * FROM $table WHERE $field = '$othervalue'"; $get = $db->get_row($sql); if ($get) { $result = $othervalue . " already exists"; } else { $sql = "INSERT INTO $table ($field) VALUES ('$othervalue')"; $insert = $db->query($sql); $insert_id = mysql_insert_id(); $result = "Added " . $othervalue; } $result = $insert_id . ' <span style="color:green;">'.$result.'</span>'; echo $result; break; } ?> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | <?php // ---------------------------------------------------------------------------- // --- selectbox_other (AJAX) // --- NOTES: basic single choice dropdown with ability to add other entry // ---------------------------------------------------------------------------- function selectbox_other($name="", $list="", $size=1, $option_value="", $option_display="", $default_value="", $event="") { $other_name = $name . "_other"; $value = stripslashes($_POST[$name]); if (!$default_value) $default_value = $value; $other_value = stripslashes($_POST[$other_name]); if (substr(strtolower($list),0,6)=="select") { $temp1 = strpos(strtolower($list),"from") + 5; $temp2 = substr($list,$temp1); $temp3 = strpos($temp2," "); $table = substr($temp2,0,$temp3); $result = mysql_query($list); $options = sprintf("<option value = ""></option>n"); while ($myrow = mysql_fetch_array($result)) { ($myrow[$option_value]==$default_value || $myrow[$option_display]==$default_value) ? $selected = "selected="selected"" : $selected=""; if ($selected) { $selected_options .= sprintf("<option value = "%s" $selected>%s</option>n",cleanup($myrow[$option_value]),cleanup($myrow[$option_display])); } else { $options .= sprintf("<option value = "%s">%s</option>n",cleanup($myrow[$option_value]),cleanup($myrow[$option_display])); } } } else { if (!is_array($list)) $list = explode(",",$list); foreach($list as $key=>$field) { if (stristr($field,"=>")) { $key = substr($field,0,strpos($field,"=>")); $field = substr($field,strpos($field,"=>")+2); } ($key==$default_value) ? $selected = "selected="selected"" : $selected=""; if ($selected) { $selected_options .= sprintf("<option value="%s" $selected>%s</option>n",cleanup($key),cleanup($field)); } else { $options .= sprintf("<option value="%s">%s</option>n",cleanup($key),cleanup($field)); } } } $selectid = str_replace("_","-",$name); $otherid = str_replace("_","-",$other_name); $statusid = $selectid."-status"; printf("<select id="%s" name="$name" size="$size" %s>n",$selectid,$event); echo $selected_options; echo $options; printf("</select>n"); echo "<br />n"; printf("<input> printf("<input> printf("<input type=\"text\" id=\"%s\" name=\"%s\" size=\"$other_size\" value=\"%s\" maxlength=\"125\" />\n",$otherid, $other_name,$other_value); printf("<input type=\"button\" value=\"Add\" onclick=\"ajaxAddOther('%s','%s','%s','%s','%s','%s')\" />",$table,$option_display,$option_value,$otherid,$selectid,$encoding); $help = "You only need to fill out the text box if the selectbox doesn't contain the choice you are looking for."; printf("<a href="javascript:alert('%s')"><img src="/images/info.gif" alt="Need to Know More?" height="12" width="12" align="bottom" hspace="2" border="0" /></a>n",$help); echo '<br /><div style="margin:5px; 0 5px 0;">status: <span id="'.$statusid.'" style="color:#999; border:1px dotted #999; padding:2px 5px 2px 5px;">nothing added yet</span></div>'; } ?> |
Sample Screenshot
JavaScript Functions
To begin the explanation, I include two JavaScript functions. Naturally, you could put these in a separate .js file if you prefer. The first function, GetXmlHttpObject
is just a standard AJAX enabling function that you can find all over the Web.
The second function, ajaxAddOther
, is what we will call when someone presses the Upload button. Note that it accepts five parameters or passed variables. The first is the database table from which we get the select box options. The second is the field from the table that is used to display in the select box. The third parameter is the value that is used in the select box. The fourth parameter is the id value for the “other” text box. The final parameter is the id value for the select box itself.
So, what the ajaxAddOther
function does is pretty simple. It opens a PHP page, which I am calling ajax.php
. Next, it changes the status field on our form. This is done twice, both using code that looks like:
document.getElementById(statusid).innerHTML=...
The first time, we are using class which we have defined as “working” to display a progress .gif file. This will be displayed while our ajax.php
file is working. Once it finishes, we will again updated the status field with the results, currently either that the “other” text input was successfully added or that it already exists. If you want, you can add more validation code and produce more possible status responses (e.g., that the inputted text contains profanity, etc.). In case it’s not obvious, note that we are using the getElementByID
feature to manipulate the HTML of our status field. That’s why we passed the id value of that field to our function (as otherid
).
Finally, if the “other” text was added successfully, our JavaScript function adds it to the select box display as a selected item at the bottom of the list. Since our response text from the ajax.php
file includes the word “Added” if it is successful, we just use the code:
if (response.match("Added")) { }
to see if we had a successful add. If so, we actually add the new select box option via the line:
sel.options[sel.options.length] = new Option(othervalue,otherid,false,true);
or
sel.options[sel.options.length] = new Option(othervalue,othervalue,false,true);
In case you aren’t familiar with that code, the first variable (othervalue
) is what will actually be displayed in the select box, while the second variable (otherid
or othervalue
) is what will be POSTED when the form is submitted. The third and fourth variables specify whether the new option is a default and selected, respectively.
The reason there are two different new Option
lines is related to the field
and value
parameters that the function accepts. Typically, people will either make the value an id field (integer) and the display a text field or they will use one text field for both. We need to examine these two parameters to see if they are the same or not. In either case, we will add the new “other” text input as the display in the select box. If they are the same, we will add that text input as the value also. If they are different however, we will assume that the value is an integer id field and, in our ajax.php
file, we will use the mysql_insert_id()
php function to decide what that value should be.
You may also want to note that we are using a variable we define as sel
. Just like the status field, we set that variable using getElementByID
and the selectid
parameter we pass to the function. Since we have to use it several times, it is easier to define it as a variable. Also, note that the sel.options.length
is how we specify that the option should be added at the end of the list. With imagination, you could make that the top, but beware that if you specify a number in the list (like 0 for top) it will actually replace the option currently in that place. So, instead you would have to create a loop to go through all the options and redisplay them. For me, I am content with adding it at the end.
ajax.php File
It isn’t really that important, but you might want to note that we are using the GET method to open our ajax.php
file. We could just as easily use the POST method, but GET works fine for what we want to accomplish. Either way, that file will check to see if the text that a user is trying to enter already exists in the database table which is used to populate the select box. If the text doesn’t exist, it adds it to the database and returns a success message. If the text already exists in the database then the page just creates a message to that effect.
Form Field Function
Finally, we use a function called selectbox_other
to create the form field. We pass some variables to this function. Specifically, the first is the name of the select box itself. The second is the instructions for how to populate the select box. Basically, there are two possibilities. The first is to use a string containing the option and value list in a format like option=>value
. This is not the most likely method, but could be useful for those who aren’t using a database. For most others however, our list variable will be in the form of a SQL statement, like SELECT * FROM universities ORDER BY university
. The third variable specifies the size of the select box. The fourth variable is the option value to be used. This is used with a SQL statement list variable and represents the field that we will get the value from. Likewise, the fifth variable specifies the field we will use to get the display value. The next variable is any default value you may want to specify. We check this value as a match to each select box options that we generate to decide if that option should be marked as selected. Our last variable is used to specify any kind of JavaScript action you want to specify for your select box (e.g., use the onchange
method to update a different field on your form).
Some things to note:
- We use the first four lines to set the default values based on what was POSTED when the form was submitted. This is useful in the case that our form didn’t pass the validation after being submitted so is being displayed again. We don’t want to lose the selections the user made.
- The if-then-else statement is used to distinguish between whether we are populating the select box via a string containing the option values or via a database SQL query. If the latter, we use some string manipulation to figure out what table is being used (since the table name will logically follow the FROM part of the SQL statement).
- As stated above, for each option we generate, we check if it is a default value or not. If so, we set the
selected="selected"
flag for that option. - After the if-then-else code, we define the id values for the various field elements (select box, “other” text input, and the status display div).
- Next we actually display the select box and text input field from the variables we created beforehand. We then add a button field that the user will click to add the new “other” text.
- The last lines of our function are just usability issues. I like to include a little information image that can be clicked to provide usage information for those filling out the form. When someone clicks on the “i” image (see illustration above) a simple JavaScript alert box will display the message defined in the
$help
variable. - As stated above, the last thing we include is a status field, which initially shows that the user hasn’t added any entries yet. One he or she does the status message will change to the response provided in our
ajax.php
file.
Error checking
The basic error checking I do right now is simply to see if a user is trying to add a value that already exists. You can modify the PHP function to perform whatever error checks you deem appropriate (profanity, length, presence of potential injection code, etc.). In fact, if anyone develops some useful generic security checks to prevent SQL injection or similar threats, please let me know and I will add it myself to this article.
Conclusion
Well, that’s it. A bit wordy, I know. But, I tried to err on the side of explaining too much rather than not enough. I hope you found this custom form field and my explanation of it useful. You may like to know that this is one of the many form elements included in my phpAddEdit free software. If you want to design powerful forms from an interactive menu rather than developing them yourself the hard way, I encourage you to check it out.
Click to Add the First »