Going instant with PHP, XML, and jQuery

Learn how to use Ajax to build an "instant" feature into your web application

Build "instant" style features into your web site with a combination of jQuery, XML, and PHP. You can pick up and use the code that you find in this article as you wish.

Share:

Jack D. Herrington, Senior Software Engineer, Fortify Software, Inc.

Photo of Jack HerringtonJack Herrington is an engineer, author, and presenter who lives and works in the Bay Area. You can keep up with his work and his writing at http://jackherrington.com.



09 November 2010

Also available in Chinese Japanese Portuguese

Getting started with Instant

Frequently used acronyms

  • Ajax: Asynchronous JavaScript + XML
  • CSS: Cascading Stylesheets
  • DOM: Document Object Model
  • HTML: HyperText Markup Language
  • JSON: JavaScript Object Notation
  • UI: User Interface
  • URL: Uniform Resource Locator
  • XML: Extensible Markup Language

Google's Instant feature, a new search enhancement that shows results as you type, has been getting a lot of buzz, and it's easy to see why. All you have to do to start getting results is just type. You don't need to press the Enter key to see the results, then tweak your search and press Enter again. It just happens as you type. Give it a try if you haven't already. It's amazing how such a small change can make such a huge difference in usability.

The great thing about this type of Instant functionality is that it's easy to implement, especially when you use great client-side tools such as jQuery (see Resources). In this article, you follow the process of building a simple search engine and then build an instant search user interface for that engine.

It all starts with getting the data to search.


Setting up the data

For this article, I decided to search episodes of The Simpsons. I put together an XML file (included in the source download) that contains all of the Simpsons episodes, their titles, season, episode number, air date, and an episode summary. You can see a portion of the XML in Listing 1.

Listing 1. The XML data source
<?xml version="1.0" encoding="UTF-8"?>
<episodes>
  <episode title='Simpsons Roasting on an Open Fire' episode='1' season='1' 
  aired='17 December 1989'>
     Christmas seems doomed for the Simpson family when Homer receives no
    Christmas Bonus. Homer becomes a mall Santa Claus, hoping to make money and
    bring Marge, Bart, Lisa, and baby Maggie, a happy holiday.
  </episode>
   ...
</episodes>

It's actually a remarkably big file, weighing in at about 840K. Which shouldn't come as a surprise because The Simpsons has had a remarkably long 22-year run.

The next thing to do is to write a PHP class that does the XML parsing and the searching for you. This class is called Simpsons, and is in Listing 2.

Listing 2. The Simpsons Search Class
<?php
class Simpsons {
 private $episodes = array();
 public function __construct() {
   $xmlDoc = new DOMDocument();
   $xmlDoc->load("simpsons.xml");
 foreach ($xmlDoc->documentElement->childNodes as $episode)
   {
     if ( $episode->nodeType == 1 ) {
      $this->episodes []= array( 
      'episode' => $episode->getAttribute( 'episode' ),
      'season' => $episode->getAttribute( 'season' ),
      'title' => $episode->getAttribute( 'title' ),
      'aired' => $episode->getAttribute( 'aired' ),
      'summary' => $episode->nodeValue );
     }
   }
 }
 public function find( $q ) {
   $found = array();
   $re = "/".$q."/i";
   foreach( $this->episodes as $episode ) {
     if ( preg_match( $re, $episode['summary'] ) || 
        preg_match( $re, $episode['title'] ) ) {
     $found []= $episode;
   }
   }
   return $found;
 }
}
?>

The constructor for the class reads the XML file of the episode information using the XML DOM library that is standard to PHP. It iterates through all the children of the root node and extracting their season, title, date aired, and episode attributes and the text of the node that contains the summary. It then appends all of that data as a hash table to the episodes array, which is a member variable.

The find function then searches the episode list to find matches using a simple regular expression match on the title and the summary. Any matching episodes are appended to an array, which is then returned to the caller. If the array is empty, then no matches were found.

With the data in hand, the next step you take is to start building the Ajax responder that your Instant UI calls to retrieve the data.


Creating the Ajax response page

The first version of the UI uses an HTML response for the Ajax request. This approach is the easiest way to implement the Instant UI. The Instant UI web page takes the search term and makes an Ajax request to the server using that term. The server then formats a block of HTML that makes up the response and sends it back to the page. The code in the Instant UI web page then replaces a portion of the page with the updated HTML in one easy call.

Later in the article, I demonstrate using an XML response and a JSON response from the server, but for the moment, just to keep it simple, you'll start with the HTML version.

The first thing you need is the HTML response page. This page takes a query string from the request. Then use that string to call the Simpsons class to search the episodes. Then format the returned episode array as HTML. The code for this is in Listing 3.

Listing 3. The HTML Ajax response page
<?php
include 'Simpsons.php';

$s = new Simpsons();
$episodes = $s->find( $_REQUEST['q'] );
if ( count( $episodes ) == 0 ) {
?>
No results found
<?php	
} else {
?>
<table>
<?php foreach( $episodes as $e ) { ?>
<tr><td class="episode"><b><?php echo( $e['title'] ) 
?></b> - 
 Season <?php echo( $e['season'] ) ?> 
 Episode <?php echo( $e['episode'] ) ?> - 
 Aired on <?php echo( $e['aired'] ) ?></td></tr>
<tr><td class="summary"><?php echo( $e['summary'] ) 
?></td></tr>
<?php } ?>
</table>
<?php
}
?>

At the top, Listing 3 includes the Simpsons class. The code then creates a new instance of it and makes the find call. It then looks to see if the response is empty, and if it is it returns “No Results Found”; otherwise, it loops through the results and puts together a table of results.

To test the page, simply go to your web browser and request that page. You can see the output in Figure 1.

Figure 1. The HTML Ajax response page
Screen capture of the HTML Ajax response page

At this point, you have all you need to start building the Instant Search UI.


Building the Instant Search UI

Using the JavaScript jQuery library makes building the Instant Search UI remarkably easy. Look at Listing 4, and you can see what I mean.

Listing 4. The Instant page using HTML responses
<html><head>
<script src="jquery-1.4.2.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Instant Search - HTML Based</title>
</head>
<body>
Simpsons Search: <input type="text" id="term" />
<div id="results">
</div>
<script>
$(document).ready(function() {
$('#term').keyup(function() {
  $.get('search_html.php?q='+escape($('#term').val()), function(data) {
    $('#results').html(data);
  } );
} );
} );
</script>
</body>
</html>

At the top of the page, Listing 4 includes the jQuery library and a CSS stylesheet to make the output pretty. The body of the page includes an input field for the search term and a results div that holds the output.

The bulk of the work is done in the JavaScript section at the bottom of the page. It starts with a call to the ready function on document. This call ensures that the interior JavaScript is not executed until the page is ready. The inside JavaScript uses the keyup function on the search term input object to monitor key presses on the search term field. When the text field changes, the Ajax get method is called to the server. And the data response from that call is used to populate the results element using the html function.

If the JavaScript code looks like line noise, that's okay. It's actually the state of the art with JavaScript because code size is best kept small because the code has to go over the wire.

You can do all of this work without the jQuery library, but the value of using the library is that the code is concise and all the cross-platform work has been done for you. You don't have to worry about Internet Explorer® versus Safari or Firefox; you just write the code once and it works everywhere.

To test the library, bring up the Instant Search UI in the web browser. You can see something like that in Figure 2.

Figure 2. A few letters typed in the search term
Screen capture of search with only a few letters typed in the search term

Figure 2 shows the interface after I've entered a few characters. After I complete typing the term “frink” you see the results in Figure 3.

Figure 3. After finishing the term
Screen capture of results after the term is completely typed

Figure 3 shows “frink” appearing in the title or synopsis of two episodes. Silly data! Professor Frink (by far the best character on the show) was in a lot more than two episodes. But still this is pretty neat stuff. The response time on my local machine was excellent, even with the server code parsing through 840K of XML.

Now you might want to throttle the number of requests by putting a delay in between each keystroke and when you actually make the request. The updated code to do that is in Listing 5.

Listing 5. The Instant Page using HTML responses with a delay
<html><head>
<link rel="stylesheet" href="styles.css" type="text/css">
<script src="jquery-1.4.2.min.js"></script>
<title>Instant Search - HTML Based With Delay</title>
</head>
<body>
Simpsons Search: <input type="text" id="term" />
<div id="results">
</div>
<script>
delayTimer = null;

function getResults() {
  $.get('search_html.php?q='+escape($('#term').val()), function(data) {
    $('#results').html(data);
  } );
  delayTimer = null;
}

$(document).ready(function() {
$('#term').keyup(function() {
  if ( delayTimer )
     window.clearTimeout( delayTimer );
  delayTimer = window.setTimeout( getResults, 200 );
} );
} );
</script>
</body>
</html>

This code creates a timer when the user presses a key. When that timer goes off after 200 milliseconds, the request is made. If another key stroke comes in before the timer goes off, the original timer is canceled and a new timer is created. The result is that the timer goes off 200 milliseconds after the user has stopped typing. The interface still feels as snappy as the original, but the number of requests made to the server is substantially reduced, particularly when users type in quick bursts.

We could stop there, but there are actually two more ways to do this instant process.


Migrating to XML

The first way is to use XML as your transport syntax from the server to the client. The idea here is that the server provides a generic XML endpoint that any process can use to make queries and that your client is smart enough to read the XML and format it the way it wants to.

To change over to XML, first create a new server page as in Listing 6.

Listing 6. The XML Ajax page
<?php
include 'Simpsons.php';

header( 'Content-type: text/xml' );

$s = new Simpsons();
$doc = new DOMDocument();
$root = $doc->createElement( 'episodes' );
$doc->appendChild( $root );
foreach( $s->find( $_REQUEST['q'] ) as $episode ) {
   $el = $doc->createElement( 'episode' );
   $el->setAttribute( 'title', $episode['title'] );
   $el->setAttribute( 'episode', $episode['episode'] );
   $el->setAttribute( 'season', $episode['season'] );
   $el->setAttribute( 'aired', $episode['aired'] );

   $tn = $doc->createTextNode( $episode['summary'] );
   $el->appendChild( $tn );

   $root->appendChild( $el );
}
print $doc->saveXML();
?>

The search remains exactly the same, but how you format the results changes. Now the code creates an XML document and appends nodes to it that hold all the data returned. Then at the end of the script, it simply saves the XML DOM out as a string. Notice that at the top of the script you also set the content type to text/xml as you export XML and not HTML.

If you navigate to this page in your web browser you see something like Figure 4.

Figure 4. The XML response page
Screen capture of the XML response page

Some browsers, though, might show the returned text in a bit more structured manner. If you want to see the original source XML, you can choose View - Source to see something like the window in Figure 5.

Figure 5. The XML response page source
Screen capture of the XML response page source

As you can see, the script created some nicely formatted XML, ready for a new piece of client-side code to consume.

The new client-side code that parses XML instead of using the HTML directly is in Listing 7.

Listing 7. The Instant Search page using XML
<html><head>
<script src="jquery-1.4.2.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Instant Search - XML Based</title>
</head>
<body>
Simpsons Search: <input type="text" id="term" />
<table id="results">
</table>
<script>
$(document).ready( function() {
$('#term').keyup( function() {
 $.get('search_xml.php?q='+escape($('#term').val()), function(data) {
html = '<table id="results">';
$(data).find('episode').each( function() {
     var ep = $(this);
         html += '<tr><td class="episode"><b>'+
         ep.attr('title')+'</b>&nbsp;';
         html += 'Season '+ep.attr('season')+'&nbsp;';
         html += 'Episode '+ep.attr('episode')+'&nbsp;';
         html += 'Aired '+ep.attr('aired')+'</td></tr>';
         html += '<tr><td class="summary">'+
         ep.text()+'</td></tr>';
   } );
   html += '</html>';
   $('#results').replaceWith( html );
 } );
} );
} );
</script>
</body>
</html>

The client code to monitor the keystrokes and to make the Ajax request is almost exactly the same. The difference is the different URL to get the XML data instead of the HTML data.

After the data is back, the code uses jQuery to find all of the episode tags. It then formats a big chunk of XML and uses the replaceWith function to replace the old table with the new one. Because of jQuery, this code is remarkably easier to use than it would be using the native DOM functions of the browser.

Another way to transfer the data is through JSON (JavaScript Object Notation).


Migrating to JSON

JSON is a very popular way to move data around in the Web 2.0 world. It's compact and easy and fast for the browser to read because all it has to do is evaluate the returned JavaScript code. It's also very easy to create JSON as you can see in the JSON version of the Ajax search page in Listing 8.

Listing 8. The JSON Ajax page
<?php
include 'Simpsons.php';

header( 'Content-type: application/json' );

$s = new Simpsons();
print json_encode( $s->find( $_REQUEST['q'] ) );
?>

You only need to use the json_encode function to turn the returned array into JSON code. If you are curious there is also a json_decode function that can turn JSON back into PHP basic types. Most of the popular languages have JSON mechanisms that are as easy as this one to convert basic data structure into and out of JSON.

If you look at the JSON page in the browser you see something like the response page in Figure 6.

Figure 6. The JSON response page
Screen capture of the JSON response page

This page might not be too attractive to the human eye, but to the JavaScript interpreter in the browser, this page looks like easy-to-read heaven.

The corresponding Instant UI web page code to read the JSON formatted output is in Listing 9.

Listing 9. The JSON Instant Search UI
<html><head>
<script src="jquery-1.4.2.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Instant Search - JSON Based</title>
</head>
<body>
Simpsons Search: <input type="text" id="term" />
<table id="results">
</table>
<script>
$(document).ready( function() {
$('#term').keyup( function() {
 $.get('search_json.php?q='+escape($('#term').val()), function(data) {
   html = '<table id="results">';
   $.each( data, function( ind, ep ) {
         html += '<tr><td class="episode"><b>'+ep.title+'</b>&nbsp;';s
         html += 'Season '+ep.season+'&nbsp;';
         html += 'Episode '+ep.episode+'&nbsp;';
         html += 'Aired '+ep.aired+'</td></tr>';
         html += '<tr><td class="summary">'+ep.summary+'</td></tr>';
   } );
   html += '</html>';
   $('#results').replaceWith( html );
 } );
} );
} );
</script>
</body>
</html>

This code is very similar to the XML code, with the difference that you can use the jQuery each function on the returned array, then use the dot notation to access all the important keys in the data (that is, title, episode, summary, and so on).

There you have it: a rudimentary implementation of the Instant search feature that you can use as a starting point for your own work.


Just a little more to it

This implementation has three main differences from what the developers at Google did. The first is scaling. They were already handling billions of searches a day, now they are handling billions of individual small searches with each keystroke. There are lots of issues and solutions around this, but in this case you have at least one thing going for you—the browser cache. If the user types in the same term twice, because of the browser cache only one request is actually made because the second time it's requested the browser returns the cached data.

Another thing Google does is pre-fetch results. For example, if you type in “mov” it hints that you are looking for “movies,” does the search, and indicates that with gray text for the “ies” that you are missing.

The third difference is the support for paging, which is fairly easy to solve. All you have to do is adds some JavaScript in for page links at the bottom of the page and then call that script when the user clicks to navigate from the first page to any subsequent pages.


Conclusion

The Instant UI feature from Google is really instant. Is it revolutionary? Not really. But it is a small step that has profound implications for usability. As you can see from this article the rudiments of the behavior are not difficult to implement using standard tools like XML, PHP, and jQuery.

I hope you are able to use the code here in your own projects. If you do please let me know. I'd love to have a look at how you use it.


Download

DescriptionNameSize
Source code for articlesrc.zip82KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Open source, Web development
ArticleID=572542
ArticleTitle=Going instant with PHP, XML, and jQuery
publish-date=11092010