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.

Setting Up the Results

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:

<?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>
    <p> </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 (is_searching) return false; 
    s = $F('s');
    // Same if the search is blank
    if (s == '' || s == 'Start Your Search...') return false; 
    is_searching = 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.

45 Comments

  • Shawn Grimes

    Great read Steve. I have been wondering how you did that search form ever since you redesign. Thanks a bunch for showing me the way.

  • Alex

    Doesn’t work in IE … I know it should, but for some reason it doesn’t.

  • Steve Smith

    Alex: It seems the latest version of scriptaculous fixed the issue I was trying to resolve with the (now-deleted) fixIE function. I’ve removed the references to it, and it seems to be working now in IE. Let me know if you find otherwise. Thanks for the catch.

  • Jonic

    Awesome stuff, thanks for letting us know how you did it!

  • Reinier / djust.nl

    Great stuff -thanks for taking us on a tour!

  • veridicus

    Interesting. An AJAX API is being added to Drupal (4.7). If a “live search” isn’t added soon I’ll take a closer look at doing something similar to this on Drupal. Thanks for the article.

  • VeeKay

    Excellent demo, Steve.

    But your live-search doesn’t work properly in Opera (i tried it in Opera 8.5, though you better check it out in the new Opera 8.52 as well).

    In Opera, though the results of a live-search are fetched properly, but new results get jumbled up visually. I suspect that Opera doesn’t render the “Blind” effect properly.

    Keep up the good work.

    - VeeKay

  • Vanessa

    Thank you for this plugin!
    Really lovely.. :)

  • Bryan

    Sorry if I have to sound like the ajax moron in the group :) hehe, but is all I am suppose to see is the FIND IT button turn to LOADING?

    I kept thinking there was more to it then that.

    thx

  • Daan Timmer

    I tried this for my site, and after some fiddling and rereading this page, it worked, but extremely slow. Is there any way to speed up the page?

  • Dustin

    I was thinking about writing a tutorial on exactly how you did this since I already dug up everything you did the day after your new site redesign. I thought to myself, “Oh this is simple” – indeed, prototype and scriptaculous make things really easy – hence the small amount of code involved (not counting the libraries).

    It was still nonetheless impressive. Well done.

  • Ryan

    Wow, that’s pretty slick. I like the way it folds down. I’ll probably have to steal some of that code one of these days.

  • Weiran

    Wow, I’ve always been a fan of your search, but I’ve been too lazy to figure it out for myself. Now I don’t have to! Thanks Steve!

  • Ben

    At least this will calm down all those requests you probably have about the form.

    Personally, I love the form too. It’s an awesome representation of coding and it looks so sleek and cool too. Great post and thanks for the tutorial.

  • Eth

    Very Nice! all in one package, thanks for putting it together!

  • Alex

    Steve: Thanks for the fix, but it still doesn’t work properly in IE. I tried it on a plain vanilla empty test blog and it just hangs. Oh well, works perfectly in FF, Camino and Safari. Thanks.

  • Bish

    Nice work Steve, I am a big fan of the Tiger Admin plugin and this is certainly one of the technical highlights of your site. Thanks for sharing the knowledge!

  • Brutal

    Wow! This is really slick.
    But how do I add a close-button like you have? (To close the search again)

  • Alex R

    Alex, it seems to be working for me in IE…

    I’m also curious about the close button… Excellent tutorial!

  • John of Played Films

    This is tiiiiiiiiiiiiiiiiiiiiiiight! Best part of marking up is AJAX in your cup.

  • Jake

    I actually had this running on a site a few months ago, but you’ve seemed to not be as lazy as I when it comes to handling the events. Good stuff.

  • dzer0

    sometioeans gidday wow

  • Ganesh Iyer

    Wowe, I have trying to find a way to do this, thanks I’ll try it on my site.

  • Collin Allen

    Great work! I love the sliding effect and how clean the result list looks.

  • Andy

    I thought it was kind of confusing if there were more than 6 search results. So in index.php I changed
    <?php if ($i == $max_results) break; ?>
    to:
    <?php if ($i == $max_results) { echo "<li>(Only first $max_results results shown.)"; break; } ?>

  • Enter

    Sweet tut

  • theBag

    Just wondering if there is a Perl version of this. AJAX these days always seem to be written forr php. I unfortuantely don`t have the choice to work with php, only perl.

    Nice work none the less.!

  • fireyy

    if you have a search.php files in template folder,delete it?

  • Christopher

    Great read, thanks for sharing this very slick effect.

  • Nate

    Not critical, but…

    http://www.yourdomain.com/s=searchterm

    should really be

    http://www.yourdomain.com/?s=searchterm
    (Note the `?`)

    Excellent work. I hope to someday be as proficient in design and CSS as this site/designer. This is one of a few sites whose design I really look up to for it’s simplicity and clarity.

  • Justin

    You know, I’ve tried using your code for my Wordpress blog and I can’t seem to get the php to work. I’m sure the js works, but it seems that your first step (appending ‘&ajax’ to the end of ‘?s=searchterm’ didn’t return the code in your php snippet.

    Maybe I’m a retard, but I’m definitely having trouble with it.

  • Minsuk Han

    Justin,

    I had same problem, but just solved. The problem is the template has search.php file. Get rid of it, and try again with ‘&ajax’. Hope it works.

  • Tom S. Weber

    Very nice tutorial, but I think you are missunderstanding the word “Live-Search”.

    In my opinion a Live-Search shows you (after typing some letters in the text-field) some suggestions.
    A such example is Google Suggest.

    Your example fetches results only after clicking the submit-button, which isn’t “live”.

    Greetz

  • Vick

    Awesome stuff!

  • Greg

    Steve,

    Nice work…thanks for sharing!

  • redstar

    Its looking great and i have been trying to get it work al night. When there is the moment to pusch find it, im directed to my homepage without any searchressults.
    Anyone who can help me?

  • bri

    Nice site, I love the comment preview, and the above tutorial, although i have to agree with Tom S. Weber.

  • Paul Whitrow

    Nice little widget you have there man…. allow me to share mine!

    example: http://www.gr0w.com/amos/livesearch/
    related article: http://www.gr0w.com/articles/code/ajax_vs_rpc_javascript_with_livesearch_example/

  • redstar

    I have made it a little futher. http://www.red-star.nl/weblog/?page_id=95
    in the bottom you cab find my search engine, but the result is a bit weard. Someone willing to gif advise?

  • Atul Varaskar

    It doesnt show up well on Opera though
    (using Opera 9 TP2 Build Unix 1670)

  • redstar

    Ive changed a few things and its working! Great!

  • Geremy F

    It sticks on loading… for me. Any idea what I’m doing wrong?

    You can see it here:
    www.geremology.com/search.php

  • Gregory Haase

    Hmm… That was a quick trip from awe to frustration. I performed a search, clicked on the first link, didn’t find what I wanted, hit the back button… oh darn, where did my search results go?

    I guess in today’s browser, I’m supposed to open in a new tab. I just find alt+left far too convenient to give up on.

  • Nik

    Thanks for this tutorial, and it works great, with one exception. Right now, if someone goes to www.mydomain instead of mydomain .. it doesnt return the search results. It just thinks.

    View here:
    scarylittlemonkey.com

    If anyone else has run across the same issue, please let me know the solution, likewise, If I figure out that I was just an idiot, I will add another comment.

  • Nik

    Well, I fixed it to an extent. It’s more of a work-around than a fix. I stripped all the js out of the general.js page and included it directly in my wordpress header. Then I found the line that says:

    var myAjax = new Ajax.Request('http://www.yourdomain.com/',

    I changed that line to:

    var myAjax = new Ajax.Request('http://".$_SERVER['SERVER_NAME']."/"); ?>',

    It’s ugly, but it works.