Lee Babin
This article is excerpted from Chapter 3 of Beginning Ajax with PHP: From Novice to
Professional with permission from Apress.
Professional with permission from Apress.
Auto-Complete
A nice feature that I first noticed as being received
positively by the Internet community is the auto-complete feature in Gmail.
Basically, when you??re entering the e-mail address of the person you??re sending
a message to, Gmail searches your list of contacts (using Ajax) and
automatically drops down a listing of all matches. You are then free to click
one of the dropped-down objects to fill it into the requested field. The whole
code integration is seamless and makes for a handy feature.
positively by the Internet community is the auto-complete feature in Gmail.
Basically, when you??re entering the e-mail address of the person you??re sending
a message to, Gmail searches your list of contacts (using Ajax) and
automatically drops down a listing of all matches. You are then free to click
one of the dropped-down objects to fill it into the requested field. The whole
code integration is seamless and makes for a handy feature.
The next example will show you how to do the same thing??although it??s not
quite as in-depth as the Gmail solution. Basically, I have built a way to feed a
list of objects through an array (a database solution would be more effective,
but that is outside of the scope of this example and will be shown later in the
book), and then display them based on what the user has entered. The user can
then click the name to fill out the field (hence the auto-completion).
quite as in-depth as the Gmail solution. Basically, I have built a way to feed a
list of objects through an array (a database solution would be more effective,
but that is outside of the scope of this example and will be shown later in the
book), and then display them based on what the user has entered. The user can
then click the name to fill out the field (hence the auto-completion).
I have expanded on the previous example by assuming that a user may want to
enter a reminder for the particular day in question on the calendar. The system
allows the user to enter their name and their task by clicking on an individual
day. Ideally, once the task is entered, the system will then save the task to
the database. For now, though, you are merely concentrating on the auto-complete
feature; saving the actual information will be handled in a later chapter.
enter a reminder for the particular day in question on the calendar. The system
allows the user to enter their name and their task by clicking on an individual
day. Ideally, once the task is entered, the system will then save the task to
the database. For now, though, you are merely concentrating on the auto-complete
feature; saving the actual information will be handled in a later chapter.
Have a look at the following example, which integrates an auto-complete
feature and a pop-up form using Ajax. Pay attention to the style.css and functions.js files, which have changed.
feature and a pop-up form using Ajax. Pay attention to the style.css and functions.js files, which have changed.
/* style.css */ body { font-family: verdana, arial, helvetica; font-size: 11px; color: #000000; } .formclass { position: absolute; left: 0px; top: 0px; visibility: hidden; height: 0px; width: 0px; background: #A2BAFA; border-style: solid; border-width: 1px; border-color: #000000; } .autocomp { position: absolute; left: 0px; top: 0px; visibility: hidden; width: 0px; } .taskboxclass { position: absolute; left: 0px; top: 0px; visibility: hidden; width: 0px; } .calendarover { text-align: center; background: #CAD7F9; width: 15px; } .calendaroff { text-align: center; background: #A2BAFA; width: 15px; } .calendartodayover { text-align: center; background: #FECE6E; width: 15px; } .taskchecker { width: 150px; background-color: #FFBC37; border-style: solid; border-color: #000000; border-width: 1px; } .tcpadding { padding: 10px; } .calendartodayoff { text-align: center; background: #FFBC37; width: 15px; } //functions.js function createform (e){ theObject = document.getElementById("createtask"); theObject.style.visibility = "visible"; theObject.style.height = "200px"; theObject.style.width = "200px"; var posx = 0; var posy = 0; posx = e.clientX + document.body.scrollLeft; posy = e.clientY + document.body.scrollTop; theObject.style.left = posx + "px"; theObject.style.top = posy + "px"; //The location we are loading the page into. var objID = "createtask"; var serverPage = "theform.php"; var obj = document.getElementById(objID); xmlhttp.open("GET", serverPage); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { obj.innerHTML = xmlhttp.responseText; } } xmlhttp.send(null); } function closetask (){ theObject = document.getElementById("createtask"); theObject.style.visibility = "hidden"; theObject.style.height = "0px"; theObject.style.width = "0px"; acObject = document.getElementById("autocompletediv"); acObject.style.visibility = "hidden"; acObject.style.height = "0px"; acObject.style.width = "0px"; } function findPosX(obj){ var curleft = 0; if (obj.offsetParent){ while (obj.offsetParent){ curleft += obj.offsetLeft obj = obj.offsetParent; } } else if (obj.x){ curleft += obj.x; } return curleft; } function findPosY(obj){ var curtop = 0; if (obj.offsetParent){ while (obj.offsetParent){ curtop += obj.offsetTop obj = obj.offsetParent; } } else if (obj.y){ curtop += obj.y; } return curtop; } function autocomplete (thevalue, e){ theObject = document.getElementById("autocompletediv"); theObject.style.visibility = "visible"; theObject.style.width = "152px"; var posx = 0; var posy = 0; posx = (findPosX (document.getElementById("yourname")) + 1); posy = (findPosY (document.getElementById("yourname")) + 23); theObject.style.left = posx + "px"; theObject.style.top = posy + "px"; var theextrachar = e.which; if (theextrachar == undefined){ theextrachar = e.keyCode; } //The location we are loading the page into. var objID = "autocompletediv"; //Take into account the backspace. if (theextrachar == 8){ if (thevalue.length == 1){ var serverPage = "autocomp.php"; } else { var serverPage = "autocomp.php" + "?sstring=" + ?? thevalue.substr (0, (thevalue.length -1)); } } else { var serverPage = "autocomp.php" + "?sstring=" + ?? thevalue + String.fromCharCode (theextrachar); } var obj = document.getElementById(objID); xmlhttp.open("GET", serverPage); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { obj.innerHTML = xmlhttp.responseText; } } xmlhttp.send(null); } function setvalue (thevalue){ acObject = document.getElementById("autocompletediv"); acObject.style.visibility = "hidden"; acObject.style.height = "0px"; acObject.style.width = "0px"; document.getElementById("yourname").value = thevalue; }
Now, let??s have a look at what has changed since the last example. Quite a number of functions have been added. The first is called createform. The createform function dis-plays a hidden div beside where the cursor is currently located, and then loads in a file called theform.php through Ajax. This function uses mostly JavaScript to get the job done (through hidden and visible style aspects), but Ajax comes into play to load the file. The code for the theform.php file (basically a simple entry form) is shown in the following snippet:
<div style="padding: 10px;"> <div id="messagebox"></div> <form action="" method="post"> Your Name<br /> <input id="yourname" style="width: 150px; height: 16px;"¥ type="text" value="" onkeypress="autocomplete(this.value, event)" /><br /> Your Task<br /> <textarea style="height: 80px;"></textarea><br /> <div align="right"><a href="javascript:closetask()">close</a></div> </form> </div>
The next set of functions mostly do cleanup work and fetch
requests. The closetask function ??closes,?? or
effectively hides the task window should the user decide they no longer wish to
enter a task. The findPosX and findPosY functions return the current x and y positions of
the field you want to assign the auto-complete functionality to.
requests. The closetask function ??closes,?? or
effectively hides the task window should the user decide they no longer wish to
enter a task. The findPosX and findPosY functions return the current x and y positions of
the field you want to assign the auto-complete functionality to.
The next two functions, autocomplete and setvalue, are the two that do the actual auto-complete.
Basically, the function autocomplete checks for the
currently inputted string (using the onkeypress event) and passes said string to a file called autocomp.php via Ajax. There is quite a bit of code in
place to make this function as browser-compliant as possible??dealing with key
presses and events from browser to browser can be tricky.
Basically, the function autocomplete checks for the
currently inputted string (using the onkeypress event) and passes said string to a file called autocomp.php via Ajax. There is quite a bit of code in
place to make this function as browser-compliant as possible??dealing with key
presses and events from browser to browser can be tricky.
The important matter is that a string representing the current value of the
text box (the Your Name field) is passed to a PHP file on the fly. The next
block of code displays what the PHP script does with the passed-in information.
text box (the Your Name field) is passed to a PHP file on the fly. The next
block of code displays what the PHP script does with the passed-in information.
<?php //A list of all names. //Generally this would be in a database of some sort. $names = array ("Lee Babin","Joe Smith","John Doe"); $foundarr = array (); //Go through the names array and load any matches into the foundarr array. if ($_GET['sstring'] != ""){ for ($i = 0; $i < count ($names); $i++){ if (substr_count (strtolower ($names[$i]), ¥ strtolower ($_GET['sstring'])) > 0){ $foundarr[] = $names[$i]; } } } //If we have any matches. if (count ($foundarr) > 0){ //Then display them. ?> <div style="background: #CCCCCC; border-style: solid; ¥ border-width: 1px; border-color: #000000;"> <?php for ($i = 0; $i < count ($foundarr); $i++){ ?><div style="padding: 4px; height: 14px;" onmouseover=¥ "this.style.background = '#EEEEEE'" onmouseout=¥ "this.style.background = '#CCCCCC'" onclick=¥ "setvalue ('<?php echo $foundarr[$i]; ?>')"><?php echo $foundarr[$i]; ?> ¥ </div><?php } ?> </div> <?php } ?>
The autocomp.php file takes the
passed-in string and attempts to find any matches. As it finds valid matches to
the query string, it loads them into another array. The reason for loading into
an alternate array is to keep the height of the div at nothing unless a valid match has been found. If a valid match (or set
of matches) is acquired, the auto-complete shows the correct matches. If you are
to click a valid match, it will load the name into the appropriate form field
(using the setvalue function) and close the
auto-complete pop-up form. Voil??, you now have a fully functioning auto-complete
feature using Ajax technol-ogy (as shown in Figure 3-2).
passed-in string and attempts to find any matches. As it finds valid matches to
the query string, it loads them into another array. The reason for loading into
an alternate array is to keep the height of the div at nothing unless a valid match has been found. If a valid match (or set
of matches) is acquired, the auto-complete shows the correct matches. If you are
to click a valid match, it will load the name into the appropriate form field
(using the setvalue function) and close the
auto-complete pop-up form. Voil??, you now have a fully functioning auto-complete
feature using Ajax technol-ogy (as shown in Figure 3-2).
Not only does this feature save the user a large amount of time, it just
feels very intu-itive. It is important to make applications as simple as
possible so that newly initiated Internet users find it easy to get along with
your applications.
feels very intu-itive. It is important to make applications as simple as
possible so that newly initiated Internet users find it easy to get along with
your applications.
Form Validation
I won??t get too far into form validation until Chapter 5,
when I discuss forms in their entirety. However, it would be prudent to show a
rather nice trick that can be done using Ajax to validate what used to be a
difficult set of information to error check. Most fields could be validated on
the client side by using JavaScript to determine empty fields, bad information,
and so on. There was, however, always a problem with checking for valid
information that might be contained within a database that only your scripting
language could identify.
when I discuss forms in their entirety. However, it would be prudent to show a
rather nice trick that can be done using Ajax to validate what used to be a
difficult set of information to error check. Most fields could be validated on
the client side by using JavaScript to determine empty fields, bad information,
and so on. There was, however, always a problem with checking for valid
information that might be contained within a database that only your scripting
language could identify.
Now that you have Ajax as a tool, however, you can get the best of both
worlds. The workaround in the past was to submit the form, check the server,
send back all values that were currently entered, and prepopulate the form when
the screen returned. While this worked fairly well, it was a rather large task
to code all of it, and it did not work with such fields as file uploads (which
cannot be prepopulated). In the next example, you will use the same task-entry
code as you used earlier, but now when you submit the form, you will first check
whether the Your Name field exists within your script before allowing
sub-mittal. This simulates something like a username validator, in which a user
is prevented from entering a username that is already taken when signing up at a
site.
worlds. The workaround in the past was to submit the form, check the server,
send back all values that were currently entered, and prepopulate the form when
the screen returned. While this worked fairly well, it was a rather large task
to code all of it, and it did not work with such fields as file uploads (which
cannot be prepopulated). In the next example, you will use the same task-entry
code as you used earlier, but now when you submit the form, you will first check
whether the Your Name field exists within your script before allowing
sub-mittal. This simulates something like a username validator, in which a user
is prevented from entering a username that is already taken when signing up at a
site.
Rather than show the entire code set over again, let??s go over changes that
have been made. First off, I have added a new function called validateform, as shown in the follow-ing code:
have been made. First off, I have added a new function called validateform, as shown in the follow-ing code:
//functions.js function validateform (thevalue){ serverPage = "validator.php?sstring=" + thevalue; objID = "messagebox"; var obj = document.getElementById(objID); xmlhttp.open("GET", serverPage); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { obj.innerHTML = xmlhttp.responseText; } } xmlhttp.send(null); }
This function loads a PHP script into a certain section of your
page. The following code contains the changes to the form:
page. The following code contains the changes to the form:
<!-- theform.php --> <div style="padding: 10px;"> <div id="messagebox"></div> <form method="post"> Your Name<br /> <input id="yourname" style="width: 150px; height: 16px;"➥ type="text" value="" onkeypress="autocomplete(this.value, event)" />➥ <br /> Your Task<br /> <textarea style="height: 80px;"></textarea><br /> <input type="button" value="Submit" onclick="validateform➥ (document.getElementById('yourname').value)" /> <div align="right"><a href="javascript:closetask()">close</a></div> </form> </div>
As you can see, you have added a div called messagebox (which will show any
errors you may come across) and a button that you are using to call the validateform function. When that button is clicked, the validateform function will fire, accessing a PHP
script contained within a file called validator.php. The code for this is shown following:
errors you may come across) and a button that you are using to call the validateform function. When that button is clicked, the validateform function will fire, accessing a PHP
script contained within a file called validator.php. The code for this is shown following:
<?php //validator.php //A list of valid names. //Again, this would usually come from a database. $names = array ("Lee Babin","Joe Smith","John Doe"); if (!in_array (strtolower ($_GET['sstring']), strtolower ($names))){ //Then return with an error. ?><span style="color: #FF0000;">Name not found...</span><?php } else { //At this point we would go to the processing script. ?><span style="color: #FF0000;">Form would now submit...</span><?php } ?>
All the PHP script does is check for a valid match from the
passed-in Your Name field. If a match is found, the script would merely submit
the form using JavaScript (in this case, it merely displays a message??I will
discuss more on submitting a form using JavaScript later in this book). If an
error is found, you can output the error seamlessly and rather quickly. The nice
thing about this little bit of functionality is that your form stays populated
(since the form has not been submitted yet). This saves you a lot of time from a
coding perspective and makes things seamless and intuitive for the user (see
Figure 3-3).
passed-in Your Name field. If a match is found, the script would merely submit
the form using JavaScript (in this case, it merely displays a message??I will
discuss more on submitting a form using JavaScript later in this book). If an
error is found, you can output the error seamlessly and rather quickly. The nice
thing about this little bit of functionality is that your form stays populated
(since the form has not been submitted yet). This saves you a lot of time from a
coding perspective and makes things seamless and intuitive for the user (see
Figure 3-3).
Tool Tips
One of the more common DHTML ??tricks?? you will see on the
Internet is the tool tip. This is basically a little box filled with information
that will appear above a properly placed cursor. While this is all fine and
dandy, the information contained within said box in non-Ajax enabled web sites
is usually either hard-coded in or potentially loaded through some server-side
trickery. What you will do in the next example is load the box dynamically using
Ajax.
Internet is the tool tip. This is basically a little box filled with information
that will appear above a properly placed cursor. While this is all fine and
dandy, the information contained within said box in non-Ajax enabled web sites
is usually either hard-coded in or potentially loaded through some server-side
trickery. What you will do in the next example is load the box dynamically using
Ajax.
As a useful addition to your calendar application, it would be nice to
dynamically display a box with all currently assigned tasks when a user hovers
over a certain date. The PHP script would henceforth have to scour the database
and return any instances of tasks associated with said day. Since I??m not going
to get into databases just yet, I??ll have you build the script to accommodate an
array of tasks for now, just to showcase the tool tip functionality.
dynamically display a box with all currently assigned tasks when a user hovers
over a certain date. The PHP script would henceforth have to scour the database
and return any instances of tasks associated with said day. Since I??m not going
to get into databases just yet, I??ll have you build the script to accommodate an
array of tasks for now, just to showcase the tool tip functionality.
First off, let??s have a look at the calendar.php file in order to view the changes to the calendar code:
//Let's make an appropriate number of rows... for ($k = 1; $k <= $numrows; $k++){ ?><tr><?php//Use 7 columns (for 7 days)... for ($i = 0; $i < 7; $i++){ $startdate++; if (($startdate <= 0) || ($startdate > $lastday)){ //If we have a blank day in the calendar. ?><td style="background: #FFFFFF;"> </td><?php } else { if ($startdate == date("j") && $month == date("n") && $year == date("Y")){ <td onclick="createform(event)" class="calendartodayoff"➥ onmouseover="this.className='calendartodayover'; checkfortasks ➥ ('<?php echo $year . "-" . $month . "-" . $startdate; ?>',event);"➥ onmouseout="this.className='calendartodayoff'; hidetask();">➥ <?php echo date ("j"); ?></td><?php } else { <td onclick="createform(event)" class="calendaroff"➥ onmouseover="this.className='calendarover'; checkfortasks➥ ('<?php echo $year . "-" . $month . "-" . $startdate; ?>',event);" ➥ onmouseout="this.className='calendaroff'; hidetask();">➥ <?php echo $startdate; ?></td><?php } } } ?></tr><?php }
The major change made here is calling a checkfortasks function that is fired by the onmouseover event, and a hidetask function that fires on the onmouseout event.
Basically, the current date that a user is hovering over will be passed to the
appropriate functions, which are located within the functions.js
file (shown following):
Basically, the current date that a user is hovering over will be passed to the
appropriate functions, which are located within the functions.js
file (shown following):
//functions.js function checkfortasks (thedate, e){ theObject = document.getElementById("taskbox"); theObject.style.visibility = "visible"; var posx = 0;var posy = 0; posx = e.clientX + document.body.scrollLeft; posy = e.clientY + document.body.scrollTop; theObject.style.left = posx + "px"; theObject.style.top = posy + "px"; serverPage = "taskchecker.php?thedate=" + thedate; objID = "taskbox"; var obj = document.getElementById(objID); xmlhttp.open("GET", serverPage); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { obj.innerHTML = xmlhttp.responseText; } } xmlhttp.send(null); } function hidetask (){ tObject = document.getElementById("taskbox"); tObject.style.visibility = "hidden"; tObject.style.height = "0px";tObject.style.width = "0px"; }
Again, your tool tip machine uses some DHTML tricks to hide the
div you want to load your task-checker script
within. You will need to create the new div as shown
in the following code in order for this to work properly.
div you want to load your task-checker script
within. You will need to create the new div as shown
in the following code in order for this to work properly.
<body> <div id="createtask" class="formclass"></div> <div id="autocompletediv" class="autocomp"></div> <div id="taskbox" class="taskboxclass"></div> <p><a href="javascript://" onclick="showHideCalendar()"><img id="opencloseimg"➥ src="images/plus.gif" alt="" title"" style="border: none;➥ width: 9px; height: 9px;" /></a> <a href="javascript://" onclick=➥ "showHideCalendar()">My Calendar</a></p> <div id="calendar" style="width: 105px; text-align: left;"></div> </body>
The checkfortasks function will
accept a date and then pass it along (via Ajax) to a new file called taskchecker.php. The taskchecker.php file would then usually read from a database and show the appropriate
tasks for the current date in a dynamically created, hovering div
(not unlike the task entry form). In this case, because you don??t
want to get into database integration just yet, you have made use of an
associative array. The code for taskchecker.php is
as follows:
accept a date and then pass it along (via Ajax) to a new file called taskchecker.php. The taskchecker.php file would then usually read from a database and show the appropriate
tasks for the current date in a dynamically created, hovering div
(not unlike the task entry form). In this case, because you don??t
want to get into database integration just yet, you have made use of an
associative array. The code for taskchecker.php is
as follows:
<?php //taskchecker.php //Actual Task dates. //This would normally be loaded from a database. $tasks = array ("2005-11-10" => 'Check mail.',"2005-11-20" => 'Finish Chapter 3'); $outputarr = array (); //Run through and check for any matches. while ($ele = each ($tasks)){ if ($ele['key'] == $_GET['thedate']){ $outputarr[] = $ele['value']; } } //If we have any matches... if (count ($outputarr) > 0){ ?> <div class="taskchecker"> <div class="tcpadding"> <?php for ($i = 0; $i < count ($outputarr); $i++){ echo $outputarr[$i] . "<br />"; } ?> </div> </div> <?php } ?>
As you can see, the system runs through the associative array
(this would normally be a database query) and then loads any matches into the
outputarr array variable. Then, if any matches are
found, it displays them within a div that you create
on the fly. The result is a very dynamic task listing, as shown in Figure 3-4.
(this would normally be a database query) and then loads any matches into the
outputarr array variable. Then, if any matches are
found, it displays them within a div that you create
on the fly. The result is a very dynamic task listing, as shown in Figure 3-4.
Summary
Well, how was that for a crash course on some rather
complicated, but basic client/server Ajax/PHP examples? You have combined
extensive knowledge in JavaScript, DHTML, Ajax, and PHP to create a very cool
set of little applications. Considering that you??ve only scratched the surface,
imagine all of the good stuff you can come up with when you start getting into
the more advanced topics!
complicated, but basic client/server Ajax/PHP examples? You have combined
extensive knowledge in JavaScript, DHTML, Ajax, and PHP to create a very cool
set of little applications. Considering that you??ve only scratched the surface,
imagine all of the good stuff you can come up with when you start getting into
the more advanced topics!
For now, it is merely important to see the basics behind using Ajax as a
concept. First off, you should note that you will be doing far more programming
in JavaScript than you may be used to. For me, when I first started working with
Ajax, I found this to be a rather complicated issue??but I can assure you that
your JavaScript skills will improve with time. It is imperative that you become
good with CSS and such helpful tools as Firefox??s JavaScript console and its DOM
inspector. The JavaScript console (shown in Figure 3-5), in particular, is very
efficient at pointing out any JavaScript syntax errors you may have accidentally
put into place.
concept. First off, you should note that you will be doing far more programming
in JavaScript than you may be used to. For me, when I first started working with
Ajax, I found this to be a rather complicated issue??but I can assure you that
your JavaScript skills will improve with time. It is imperative that you become
good with CSS and such helpful tools as Firefox??s JavaScript console and its DOM
inspector. The JavaScript console (shown in Figure 3-5), in particular, is very
efficient at pointing out any JavaScript syntax errors you may have accidentally
put into place.
Once you have a firm grip on JavaScript and CSS, the possibilities for
Ajax-based applications are endless. It is really a matter of getting the
appropriate information to the appropriate PHP script, and then
returning/displaying what you want. As you progress through the rest of this
book, you will build upon the principles developed in this chapter to create
some very powerful applications. For now, let??s look at one of the more powerful
aspects of server-side functionality: databases.
Ajax-based applications are endless. It is really a matter of getting the
appropriate information to the appropriate PHP script, and then
returning/displaying what you want. As you progress through the rest of this
book, you will build upon the principles developed in this chapter to create
some very powerful applications. For now, let??s look at one of the more powerful
aspects of server-side functionality: databases.
This article is excerpted from Chapter 3 of Beginning Ajax with PHP: From Novice to
Professional with permission from Apress.
Professional with permission from Apress.