# Asynchronous Search With PHP and jQuery

In this tutorial we’re going to be creating a widget for our web pages which allows visitors to search the contents of our site. We’ll be using jQuery to pass the search term to the back-end, and to receive and display the results. We’ll be using PHP to search for the term in a local site and then return any matching URLs back to the page as a JSON object. Part 2 shows how to search a database of a content management system (CMS).

The PHP back-end used in this tutorial is just an example and is not the only way that we could perform the search. The back-end that the widget is connected to will differ depending on the structure of the website that it’s used on. The code used in this example would work well on a small to medium site with lots of static content. A data-driven or product-heavy site would probably make better use of a database-searching back-end, which would be equally as easy to code.

## The PHP

We’ll look at the PHP first and then build around that. There’ll be a simple little script which begins at a specified directory and then spiders down through any subdirectories, collecting the URLs of all of the pages within the tree.

We’ll then need to search though each page to see if it contains the term that the visitor has searched for, and make a note of its URL if it does. Finally we can convert the information to JSON format for easy processing in the browser. Let’s make a start; in a new page in your text editor add the following code:

Most of the functionality in the PHP will be within the function we define here; the searchFiles function accepts two arguments, the first is the directory to start searching in, the second is an array. Next we need to add the spidering and searching logic, all of which can go into this function; within the function, add the following code:

First we get the search term, which will be passed to the file as part of the GET request. We aren’t using a database so in this example, I haven’t focused on any security measures.

We then use the native PHP function scandir which will read the contents ofsc a specified directory. The directory to scan is obtained from the first parameter passed to the function. The return value of scandir is stored in the $contents variable and will be an array. Directly after the code we just looked at, add the following code: The for loop, which encapsulates quite a bit of functionality, cycles through each item in the array returned by scandir. Remember that at this stage, it’s just the contents of the starting directory that we’re working with. We first build the path for each item by concatenating the starting directory with a forward slash and the current filename. This is needed so that we can access files that are within any subdirectories inside the starting directory. Next we check whether the current item is a directory using the native PHP is_dir function. If it is a directory, we ignore the current (.) and parent (..) directories, so that we only get subdirectories, and then recursively call the searchFiles function again, passing in the$path variable as the directory to search and the $urls array if it exists. It will only exist if the function has already been called, and if it does exist, will contain the URLs of any pages that have already been searched and found to contain the search term. If the current item is a file, which we confirm with the is_file function, we then check the extension of the file to see whether it’s a file type that we want to search. We obviously don’t want to search script files or CSS files, or any other resource that doesn’t contain content. In this example we’re just searching HTML files. We can check the extension by exploding the string using the period as the separator, and then looking at the last item in the resulting array. If the current item does have a HTML extension, we open the file in read-only mode and store the entire contents of the file, tags and all, in the$fileContents variable. We use the filesize PHP function to ensure that we read the entire file into the variable. The contents of the file will be stored as one long string.

Next we want to get the title of the page so that we can use this if the file does contain the search term. We can do this easily by first exploding our giant string on the

string. We then explode the remaining string on the

string, which will give us the title of the page, which we store in the $title array. After this we can further prep the string of the file’s content for processing by removing all of the HTML tags from it. This means that only the content of the page will be searched. Once we have a clean string, we can then see if the search term is within the string using the case-insensitive stristr function. If the page does contain the search term we then tidy up the path to the file by removing the first two characters (the ./) as we won’t need these to link to the file. Finally we add the URL of the file and the title of the page as a new item in the$urls associative array.

Once the function has finished executing we can return the associative array:

We still have a couple of tasks to complete with PHP; directly after the searchFiles function add the following code:

We first set the current directory as the starting directory; the $startDir variable is passed to the searchFiles function the first time it is called, which we do next, storing the return value (our associative array) in the$urls variable. If no matches to the search term are found the array will still be created, but it will be empty, which we can test for in our JavaScript a little later on.

We also use the PHP sleep function to delay the response by a single second; we probably wouldn’t need to do this in a real implementation as the delay between the browser and server would be likely to be more than this anyway, but for the purpose of this example delaying the response allows us to see the loading spinner that we’ll be using and just seems to make the example work better.

## Searching the Data

The first thing we do within the loop is store the current row of data in the $row variable using the mysql_fetch_assoc PHP function which returns an associative array where each column in the table row appears as an item in the array. The column name is the label used to access each item in the array. We then search the content item in the array using an if statement and the substr_count PHP function to see if the search term occurs 0 times. If it does occur 0 times we simply continue to the next iteration of the loop. If the string occurs more than 0 times we then create a new multidimensional array called$urls and add to it the url and title items of the array and a new item called occurs, which is the integer returned by the substr_count function. Each item in this array is itself an associative array. We make use of the strtolower function to make the search case insensitive.

The result of this code is a multidimensional array where each item is an associative array containing the URLs, page titles and the number of times the search term was found of pages whose content contains the search term. One thing we can do next to really add value to the search is to sort the array so that the first item in the array contains the highest number of occurrences of the search term, giving each result a ‘rank’. We can do this very easily using the uasort PHP function.

The uasort function expects 2 arguments; the array to sort and a custom function where the items in the array are compared. The cmp function is a custom comparison function which accepts 2 of the items from the array passed to uasort. The function will then return false or true if the value of the first occurs item is greater than the value of the second. The uasort function will automatically convert the outer array into an associative array and each item will be given its original pre-sort index number as a label.

We then wrap this array in parenthesis and convert it to a JSON object. Each property of this object is a nested JavaScript object that we’ll be able to process quickly and efficiently within the browser. Just before we echo back the JSON object we use the PHP sleep function to delay the response by 1 second; this part of the script shouldn’t be used if and when this widget is deployed. I’ve just found when running this example locally that it works better with this delay. After the delay we echo back the JSON object. We haven’t created the page that will interact with this file yet, but the following screenshot shows the structure that the JSON object will take:

## The JSON Data Structure

JSON is a subset of JavaScript that allows us to define simple or complex objects and arrays. The values of these data structures can be any of a number of different data types including strings, numbers, Boolean or null values and can even be other objects, as in this example. PHP’s native json_encode function works by preserving standard arrays so that they remain as arrays, but converting associative arrays to objects. When this occurs, the label of the associative array item is used as the name of the property.

The structure of the JSON object that we’re using in this example is different than the structure we used in part one of this tutorial. The reason for this, as I explained earlier, is because of the additional data supplied by the uasort function. Previously, our JSON object was an array, which we were able to iterate through rapidly and easily. The fact that our JSON object’s structure has changed doesn’t make it any harder to get at our data, as we’ll see shortly.

For reference, we can easily return our JSON object to an array by wrapping the array within the array_values PHP function inside the json_encode function, like this:

The code that we’ll be using to process our JSON object in this example however is extremely flexible because it can be used to access the data in our new JSON format, but the exact same code can be used to access the array format that the JSON took in part 1.

## Styling the Search Widget

Next we can define the stylesheet for our widget; in a new file add the following selectors and rules:

Save this file as search.css in the dbSearch folder. Let’s look at the styles that we define; first we create a class for text elements so that the various bits of typography are consistently styled. The next four id selectors target the default elements that appear in the search widget when the page initially loads. Pretty much all of the styles set by these rules are arbitrary and have been decided by me for the purposes of this example. This is how it’ll look when the page loads:

The #error and #success selectors are for the different types of feedback that the user may receive, such as the message that is shown when no search term is entered, the message that is shown when the search term hasn’t been found, or the icon that is shown when results are found. Again most of the styles used for these elements can be changed according to your preference. The most important rule in each of these is display:none; which of course hides them until they are ready to be shown. The following screenshot shows the error message:

The remaining rules are all used to style the list of results that is produced when the search term is found in the data store. We don’t know beforehand how long each result is going to be, so we use the min-width rule to allow the width of the result list to grow. The white-space:nowrap rule also prevents the width of each of the results from being restricted.

Again, a lot of the rules here are used to set this particular skin, nothing more, so you can change the appearance of the widget easily without preventing it from working. The menu is positioned absolutely so that it does not interfere with other elements on the page. When the results are presented the first result is focused and has the selected class applied to it. We set the focus outline of the link to the same color as the selected class instead of removing the focus outline for accessibility reasons. The following screenshot shows how the results list will appear:

## Creating the Page Shell

We’ll look at the underlying page first; in a new file in your text editor create the following page:

The page is as simple as possible, having just the search widget and some layout text present. The text is there to show how the result list overlays any existing page content (although there is no provision for overlaying flash content or select boxes). The widget itself is also very minimal, containing a simple heading, the search input, and a button. The rest of the elements that we’ll need we can create as and when necessary.

We link to our stylesheet in the head of the page, as well as a local version of jQuery at the end of the body. After this, we leave a script element containing the standard jQuery document.ready function which is where the bulk of our code will reside. We can add this next; there’s a lot, so after the following code sample we’ll look at what each bit does individually:

All of this code is encapsulated within a click handler for the search button; within the anonymous function we pass to the click helper method there are several distinct sections. Let’s look at each of them in turn.

## Housekeeping

Our first task is to tidy up, as this may not be the first time the button has been clicked and there may be elements left over from previous interactions. There are four things we need to look for and remove if they are present:

We can test whether each of these elements exist using the JavaScript ternary construct; if they do exist we fade them out, if they don’t exist we do nothing.

## Pre-Search Processing

Next there are a few things we need to do before we perform the actual search; we first check that the input field in the widget does have a value and if it doesn’t, we show the error message:

There is actually a lot more code inside the first part of the conditional, but this is everything we do before we make the request; we first disable both the button and the input element, to prevent multiple submissions while a request is in progress. We also add an AJAX spinner so that the visitor knows that something is happening behind the scenes.

The last thing we do before actually making the request is to prepare the data that is to be sent to the server, which is the search term entered into the input field.

## Requesting and Handling the Response

We’re now ready to request and process the response; making the request is easy with jQuery’s getJSON method:

The method takes three arguments; the URL of the server-side resource that will receive the query and return the data. The second argument is the data to send to the server, and the last one is an anonymous callback function which will be executed if the request is successful. The code that goes into this anonymous function is used to process the response and display the results:

In this section we first check that there is data in the response; if the search term isn’t found in the database null will be returned and if this is the case we can create and show a message. The message will appear like this:

After we’ve processed the object and displayed either the ‘no results’ message or the results, we can then remove the AJAX spinner, and enable the button and input to allow for additional searches to be performed.

## Displaying the Results

If the search term is found, we’ll have a JSON object to process and results to create and display:

First we need to create a few new elements; we create a success icon, which is inserted into the widget and positioned so that it appears next to the widget’s title (it just looks out of place anywhere else), and a container element for the results. We also create a message stating that the following list contains the search results.

The order of the items in the list is important in this context, so we create an ordered list element which we’ll populate in just a moment. The search results are in descending order within the JSON object, with the highest ranking (i.e. the page that contains the highest number of occurrences of the search term) item at the top.

We then use a for in loop to iterate over each property within the JSON object; on each iteration we create a new list item, then a new anchor element. We give the link some class names, so that they pick up the appropriate styles. We set some of the attributes of the link element using various values of the properties of the inner objects within the JSON object.

This is achieved using a combination of bracket and dot notation. The object consists of a series of properties and the value of each property is another object. Within each inner object there are another series of properties whose values contain our data. To access each property within the outer object we use the variable prop, which is defined in the loop, using square bracket syntax: data[prop] and to access our data, we simply append the name of the property who’s value we’d like to obtain: .url for example.

We also add the text content of the anchor element using data from our object before finally appending it to the list item. Following this the list item is appended to the list. One the loop has ended and all of the list items have been created and appended, we then need to give the first and last list items specific class names so that we can easily reference them later on in the script.

In the final part of this section of code we slide the results list into view and then select the first item in the list, giving it a class name and focusing it. This is pretty much where we finished off in part one of this tutorial, but now we’re going to add keyboard navigability to the results.

We attach our event listener to the anchor within the list item that has the class name selected, which we applied to the first item in the list; this way the widget will only be listening for events when it is relevant to do so. We attach the listener using jQuery’s live method so that we don’t have to keep rebinding to the event whenever we show the results:

Whenever the keydown event is detected the anonymous function is executed, and is automatically passed the event object. Before we get on with moving the selection to the next item in the list, there are a couple of things we need to do:

First we need to check whether the key that was pressed was the tab key, as this has its own default behavior that needs to be disabled. We do this by using the JavaScript ternary to see whether the keyCode property of the event object is equal to 9. If it does we prevent the browser’s default behavior using the preventDefault method.

We can also check whether the escape key was pressed and if it was we can close the results list and reset the value of the input field.

Next we need to do different things depending on whether the currently selected list item is the first or last item in the list, which we can test using the class names we added earlier:

Within each branch of the conditional we also need to react differently depending on which key was pressed; we’re targeting the up and down arrow keys, which move the selection up or down the list respectively, and also the tab key, which will move the selection down the list. We use a nested ternary conditional for its compact syntax, which is equivalent to an if else statement.

Each branch of the outer conditional contains very similar expressions; let’s walk through the first one to see what’s going on. The first part of the ternary checks for the down arrow key or the tab key, if either of these is detected we navigate up from the anchor, which is in the context of \$(this), to the parent list item and remove the selected class name. Then we navigate to the next sibling list item and give it the class name selected. We then navigate down to its first child, which will be the anchor element, and focus it.

If neither of these keys is detected we then check for the up arrow key, represented by 38. We want the selection to cycle through the list as if it were a menu, so if the up key is pressed while the first item is selected, we should move to the last item in the list and apply the selected class and focus. If none of these keys are detected we do nothing.

The ternary within the next branch of the outer conditional is very similar but this time we are looking at the last list item, but this time if the down or tab key is pressed, we move the selection to the first item in the list. The final condition again is very similar, but this time we just move the selection up or down depending on which key was pressed.

## Catering for Mouse Interactions

Just because we’ve built keyboard navigation into our widget, it doesn’t necessarily mean that every visitor is going to make use of it, so for consistency we should move the selection around if the mouse pointer hovers over any of the list items. The code for this is very simple indeed:

This is a simpler version of what we did with the keyboard event handlers; when the pointer hovers over a list item, we remove the selected class from all of list items and then add it back to whichever item was hovered, focusing the anchor element as we go.

Finally we can add a function that will close the result menu if any element outside of the menu is clicked. We do this by attaching a click handler to the body tag, and checking that the element that was clicked does not have a parent higher up in the DOM which is an ordered list with an id of resultList:

Attaching the event listener to the body in this situation is useful because a click on absolutely any element on the page will bubble up to the body where we can capture and examine it.

## Summary

We should now have a fully working widget which will allow us to search all of the content from a site that is contained within a database. The list of results will be both keyboard and mouse navigable and should appear as in the following screenshot:

Let’s recap what we’ve covered in this tutorial:

• Many web sites and blogs are powered by a database in which all of the page content is stored. We looked at an example MySQL data source and saw how we can easily populate a test database using a simple text file.
• We looked at how we can use PHP to retrieve the information from the database and search through it to look for occurrences of the search term. We looked at constructing a data structure, sorting the data, and converting it to an easily consumed JSON object.
• We then looked at how to process this object in the browser and update the DOM of the widget to reflect whether the search was successful or not. We at looked at some of the considerations required to enable keyboard navigation of the results, turning it into a menu.