HOWTO: Animated Live Search
I’ve been meaning for some time to give a little tutorial on the live search I created for this latest design. There are a few steps involved, and I’ll do my best to explain each as we go. I should also note that I’m not including all the effects that you’ll find in my search. I need to keep mine unique, right?
Also, I’ll be touching on a few steps that are WordPress specific, but the theory can be applied to any site platform.
<h2>Setting Up the Results</h2>
<p>The first thing we need to do is configure our WordPress theme to give the search results in the minimized fashion required by the Javascript request. We don’t want to get the entire search results page, just the headings and list items containing said results. This is a pretty simple process. Just open the index.php file in your template folder, and place the following code at the top of the page:</p>
<?php if (isset($_GET['ajax']) && $_GET['s']) { ?> <?php // You might want to show fewer results than the standard per-page number $max_results = 6; ?> <h3>Search Results</h3> <?php if (have_posts()) : $i = 0; ?> <ul> <?php while (have_posts()) : the_post(); $i++; ?> <li><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title(); ?>"><?php the_title(); ?></a> <?php if ($i == $max_results) break; ?> <?php endwhile; ?> <li class="more"><a href="/?s=<?php echo urlencode($_GET['s']); ?>">Get More Details in the Full View</a></li> </ul> <?php else : ?> <p>Sorry, nothing matched your search.</p> <?php endif; ?> <?php } else { ?>
… and then this at the bottom to complete the if
statement…
<?php } ?>;
I’ve removed a few non-critical pieces of the XHTML I use in my template for simplification, but feel free to edit the XHTML to your liking.
Once this is in place, you should be able to go http://www.yourdomain.com/?s=searchterm
(replace www.yourdomain.com with your website URL, and searchterm with a relevant search term for your content). This should return a normal search results page. Now, simply add &ajax
to the end of that url, and you should get your custom results for your search form. See, not that bad.
The Form Itself
Now that we have our libraries in place, we need to add the XHTML for the search form.
<form method="get" id="searchform" action="/"> <input type="text" value="" name="s" id="s" /> <input type="submit" value="Find It" class="submit" id="searchbutton" value="Search" /> </form> <div id="search-results"></div>
We want the form to degrade gracefully if the user does not have JavaScript enabled, so you will notice it’s simply a standard form with our search page as the action. We will overwrite this functionality on page load with JavaScript.
Onto the JavaScript
First, we’ll need to get the relevant JS libraries. I’ve used Prototype and scriptaculous for my site, but you could modify the code to use the lighter moo.fx library if your really concerned about page weight. Because javascript files are cached, I don’t worry about it that much. So sue me.
The prototype library is included in the latest release of scriptaculous available from the downloads page on the script.aculo.us site.
Once you have downloaded and extracted the files from the archive, I recommend placing the /lib/prototype.js file and all the files located in the /src/ folder into a /js folder at the root of your site. Then, in the header.php file in your template folder (or wherever you specify the <head>
section of your site), include the following lines:
<script src="/js/prototype.js" type="text/javascript" language="javascript"></script> <script src="/js/scriptaculous.js" type="text/javascript" language="javascript"></script>
Sidenote: Because we won’t use the functions in a few files that are included into scriptaculous, you can cut down a little bit of page weight by either commenting out or removing the references to dragdrop.js, controls.js, and slider.js from the scriptaculous.js file (see my scriptaculous.js file for an example). If you plan to use these the functions in these files elsewhere on your site, you would obviously need to leave them intact.
Next I created a blank general.js file, placed it in the /js file in my site root, and included it below our other script includes with the following line:
<script src="/js/general.js" type="text/javascript" language="javascript"></script>
Now, we fill general.js with this code:
function activateSearch() { if ($('searchform')) { $('s').value = 'Start Your Search...'; // Default text in the search box var o = document.createElement('div'); // Old search results div var n = document.createElement('div'); // New search results div $('searchform').onsubmit = function() { doSearch();return false; }; $('s').onfocus = focusS; // Function to clear the default search box text on focus var s = $('search-results'); var f = $('searchform'); o.id = 'old-search-results'; n.id = 'current-search-results'; s.appendChild(n); s.appendChild(o); o.style.display = 'none'; n.style.display = 'none'; is_searching = false; } }function doSearch() { // If we're already loading, don't do anything if (issearching) return false; s = $F('s'); // Same if the search is blank if (s == '' || s == 'Start Your Search...') return false; issearching = true; c = $('current-search-results'); o = $('old-search-results'); b = $('searchbutton'); b.value = 'Loading'; b.disabled = true; o.innerHTML = c.innerHTML; c.style.display = 'none'; o.style.display = 'block'; // Setup the parameters and make the ajax call pars = 's=' + escape(s) + '&ajax'; var myAjax = new Ajax.Request('http://www.yourdomain.com/', {method: 'get', parameters: pars, onComplete:doSearchResponse}); }
function doSearchResponse(response) { $('current-search-results').innerHTML = response.responseText; new Effect.BlindUp('old-search-results',{duration:.8}); new Effect.BlindDown('current-search-results',{duration:.8, afterFinish:resetForm}); }
function resetForm() { s = $('searchbutton'); s.value = 'Find It'; s.disabled = false; is_searching = false; }
function focusS() { if ($F('s') == 'Start Your Search...') $('s').value = ''; }
Event.observe(window, 'load', activateSearch, false);
Be sure to change the www.yourdomain.com to your website url. Rather than explain the code line-by-line, I’ll give you a general overview of what happens.
First, the Event.observe
at the bottom of the file will run the activateSearch function once the page loads. This function will check for the search form and if it exists, set the default text in the search box, creates the <div>
elements for the current and old search results, sets the submit action to the doSearch
function, sets the onFocus
action for the search box to delete the default text when the element is focused, and sets a few variables. Whew.
Ok, so when somebody submits the form, it calls the doSearch
function. This verifies that there’s something in the search box and checks to see if we’re already searching. If everything checks out, it takes whatever is in the current-search-results div
(if this is the first search, it will be empty) and puts it in the old-search-results div
. Then it swaps the visibility of the old-search-results and the current-search-results immediately after, making the switch transparent to the user. This sets the stage for the animation once the search results are returned. Next it sets the button text to ‘Loading’ and disables it. Finally, it sets the url and parameters for the ajax call, and specifies the doSearchResponse
function to run when the results are returned.
The doSearchResponse
is passed a variable we store as response
, and the html of the results is found at response.responseText
. We take that code and place it in the now-hidden current-search-results div
and animate our current-search-results to roll in while the old-search-results rolls out. Once those effects are finished, we set the button back to normal and tell js that we’re done searching, and we’re finished.
All that’s left to do is style your results with a little css.
View the demo to see what it should look like when it’s all finished.
A few disclaimers:
- I’m sure that some of the JavaScript could be cleaned up. I’d like to eventually put this all into a class.
- I’d like at some point to rework the script to eliminate the innerHTML usage by setting up an actual XML response and adding the results through the DOM. But that’s for version 2.
- Be sure to make backups of any template files you edit. This always goes without saying, but I felt like saying it anyway.