Developing an auto-complete script with AJAX/PHP

Working on on Management system for the Comercial department I received innumerous requests saying “but can’t you show me the client’s name as I type here?”. Well I wish the words were as poetica as those but what really happened is that they were mixing up the registered clients with IE auto-complete feature, well you know, users will be users.

So I was faced with a challenge, how could I solve the problem working is a web-based environment? I needed a script that would search the database in real time as I typed the actual words in the text box, and return to me the vendor related to that client. Well some time back thinking in web-based systems I would just claim “impossible! Dude, we are talking browser here, no some VB or deplhi program!”… not anymore! Hence xmlHttpRequest came into question and the new AJAX-way of working net content came into view.

So I begun my search for an “auto-complete” or “Google suggest” (yeah, when you are good I can create cool names uh?) script that would handle my needs. I found quite a bit each with a diferent solution but no AJAX integrated (except Google’s of course). I tried to get a clue from Google implementation, but trust me, when someone wants to make a javascript file look scrambled and illegible, well it is possible, and Google accomplished just that.

So I begun from the beginning as they say:


<input name="string" id="string" onkeyup="sendAjaxReq(this.value)" type="text" />

The idea was simple, with each keystrike an AJAX request is sent to a PHP script that is responsible for searching the database looking for the entries that begin with the string provided and returning a XML formatted list of results. Something like:

<root>
<item id="286" label="temos"></item>
<item id="288" label="ter"></item>
<item id="332" label="tecnol%F3gico%2C"></item>
</root>

That was simple enough, just trigger a floating DIV and fill it with an unordered list (UL) and feed each result as a lit item (LI). So the basic was there, but you needed to grab the mouse and click on the choosen result when you found it, not that much fun…

It was missing something like Google used, that triggered the navigation through the results using the keyboard, up and down arrows to be exact. And also using the ENTER and TAB keys to select the result. So I based myself on another script I had checked out just to get a heading on which way to go.
So adding A plus B I finally finished the script, complete with AJAX, database search, keyboard and mouse navigation… the works. When it was done I found innumerous other applications for it, it seemed this kind of script can be applied in several situations, and make them much more user-friendly. So I decided to make the script more generic to make its distribution easier so I could shared it with all of you.

Live Demo: here
Download (with examples): dmsAutoComplete.zip

Hope it turns out to be of some use to any of you.

Known Bugs: In Firefox on the first list population run the UL appears as a single line, not breaking lines between choices.

This post is also available in: Portuguese (Brazil)

31 thoughts on “Developing an auto-complete script with AJAX/PHP

  1. Checked out the live demo, worked great apart from the fact that it seemed to be case sensitive. Example typing in “Pens” would bring up “Pensando” but “pens” wouldn’t

  2. Lee,

    Thanks for the feedback.
    Thats actually a live demo restriction, i only applied a simple pregmatch with case sensitive implications.
    If you were to use the script for other purposes the PHP backend can be adapted to any data source, like databases or txt files, using case insensitive matches.
    The live demo is just a simple example of how it works.

  3. Great JOB!! Thats almost what I’m looking for but I want the text to appear under the search box, kind-of like what a Google search result would look like. I will work on it and let you know if I find anything!

  4. Hello, it’s a nice script – good work! I only think the little disadvantage is that options are not only highlighted but also selected by “onmouseover event”. It could result in unwanted selection. The better would be if selection is activated only by “onmousedown event”.

  5. Hi,

    It’s not working on Mozilla Firefox.
    Error is :
    xmlRes.getElementsByTagName is not a Function

    this also not supporting- getAttribute(“id”)

    I am working on MVC architecture (JSP, JS,java,SQL). Please help……

  6. I had no trouble getting this script to work in IE, but using the identical page, I get nothing in Firefox. I can have them side by side on my screen. IE works, Firefox doesn’t. This is using your example.htm in both. Does anyone have a fix?

    Also, is it possible to have multiple instances on a page?

    In IE it is flawless. Very good work. This is the best looking autocomplete I have discovered.

  7. Brian,
    That’s very weird, the script was released working in both IE and FireFox, even better on FireFox actually. Just tested it and it all seems well.
    If you could, install Firebug extension on your firefox e and check any errors, send them over to me and i’ll check out what’s wrong.

  8. I checked the error console of Firefox and found repeated instances of:

    Error: uncaught exception: Permission denied to call method XMLHttpRequest.open

    I don’t see any option in Firefox to change its security permission level to allow the request. Does anyone have a hack?

  9. Brian,

    AJAX is not cross-domain, and that error is usually related to using AJAX to obtain data from other sites, like a request from my page trying to get an url in your site.
    This should not work on IE nor FF. Are you trying to access a remote site?

  10. Rafael, you seem to have the answer. I have several domain names parked to my main url. Since they all resolve to the same url and resolve the same programs, I am not always careful about which one I enter. I just tried the example program in Firefox, being careful to use the exact same domain name that I used in a hard reference in the code (I have your .js and php programs in deep subfolders) and it works perfectly. IE must be more tolerant.

    Thanks for your help.

  11. Hi Rafael.
    I like your auto-complete script.
    Can it be used without php/mysql?
    The reason is that I have kids in a class so they can have they on message page. It is only a small (45) line, so php/mysql would be overkill.
    The lines like:
    Jody Masori
    So if I can use your script to put these inside the page that would be nice.
    Let me know
    Thanks
    Josef F.K

  12. Josef,

    Well the script just need something taht returns a XML response, that you can easily find out its structure by looking at the script. If this is going to be a static XML return ou a php/mysql script, or ASP, or java, the script iginre, just as long as its a xml in the right format.
    So i guess you can use it because its just a frontend.

    Hope that helps.

  13. Can I easily change separator from “space” to something else, e.g. “;”? I’d like the application to interpret “Company A” and “Company B” as two different auto-complete entries. In the current setup they are interpreted as 3: “Company”, “A” and “B”, because “space” is the separator…

  14. Mikey,

    Shouldn’t be too hard to figure out, in the actual example there is a spot where i split the text in an array using ” ” (explode function), just replace that.
    That is actually just and example of use, you can easily rig the script to get database results and insert a node into the xml file for each result.

    Greetz

  15. Thanks you for this tool. One little thing I wanted to change was that after you select a value I would like the focus to remain/go back to the text field. So it would be easy to go on typing some more, without having to click in the field first to get the cursor back there. I don’t know why it actually loses focus, but I did find a solution after many tries, though I can’t explain why this works. (Using I.E. 7.) The solution was making these changes in dmsAutoComplete.js:

    1.comment out the me.hideDiv() in this part:
    //Sumir com autosuggest
    me.elem.onblur = function() {
    //me.hideDiv();
    }

    2.comment out the same here:
    li.onmousedown = function() {
    me.acChoose(this.id);
    //me.hideDiv();
    return false;
    }

    3.add this part just above the code of step 2:
    li.onmouseup = function() {
    me.hideDiv();
    document.mailform.friend_emails.focus(); // in my example my form is called mailform and the text field/area is called friend_emails; for some reason it did not work putting this line in the onmousedown code
    return false;
    }

    I also made some changes so that it does not replace the text in the text field, but adds to it, and some other changes to make it suitable for an online email sending tool (i.e. I made it more like gmail). In case anybody is interested (my email can be found on sturman.net).

    Thanks again for a nice tool.

  16. Henry,

    Actually if you look at the beginning of the file you will see some variables set to do that, like true/false variables that can do what you like them to.
    Also you can do the focus thing editing the actual return function, so you can actually do all this without touching the backend.

    cheers!

  17. Thanks for a great script. I used another one in the past (can’t remember who developed it) and couldn’t find it so I googled and found yours. Your script is lightning fast compared to the one I used before. I absolutely love it.

    I have a small problem that I am hoping that someone can help me with. After the list is populated with data, I would like to allow the user to press the enter button to submit the form. I have placed the form tag in the html along with a submit button. When I select the item I want from the list and the input text box is filled, I am getting the javascript alert box.

    Instead of the alert box, how would I get the form to post? Thanks to anyone that can help.

  18. Tom,

    If you look at the code you will see on the same page as the autocomplete html, there is a function which determines what it does when you choose an option, just alter this function to do a form submit. Also look at the varios options available to alter the script, everything is documented, but on look at the code will allo easy identification of options.

    cheers

  19. Наша компания занимается арендой ноутбуков, аренда компьютеров и арендой компьютерного класса.
    Аренда ноутбуков в большом городе.

  20. When I set it to search the database it’s not working! Here’s the code in dmsAC.php file:

    if (class_exists(‘DOMDocument’)){ //Adapta o script para PHP4

    //Criar documento XML atraves de DOM
    //Create XML Doc through DOM
    $xmlDoc = new DOMDocument(‘1.0′, ‘utf-8′);
    $xmlDoc->formatOutput = true;

    //Criar elementos Raíz do XML
    //Create root XML element
    $root = $xmlDoc->createElement(‘root’);
    $root = $xmlDoc->appendChild($root);

    }else{
    $xmlDoc = ”;
    $xmlDoc .= ”;
    }
    /**
    * :pt-br:
    * Definir Lista (itens) a ser mostrada.
    *
    * Neste passo podemos realizar buscas em banco de dados, filtrar arrays
    * Ou qualquer outra tarefa que retorne um resultado baseado no string
    * recebido
    *
    * :en:
    * Define list to be returned
    *
    * In this step we could do a database search, filter arryas or perform
    * other actions which would return a resultig list based on an input
    * string
    */
    if ($_POST['string'] != ”){
    //Fazer filtro ou busca
    //Filter ou search
    //SQL, Array, etc…

    $select_keywords=”SELECT * FROM jos_vm_keywords WHERE keyword LIKE ‘%”.$_POST['string'].”%’ LIMIT 5″;
    $select_keywords_rez=@mysql_query($select_keywords);

    $results=@mysql_fetch_array($select_keywords_rez);
    //Construir elementos ITEM
    //built ITEM elements
    foreach($results as $key=>$label){

    if (class_exists(‘DOMDocument’)){
    //Cadastrar na lista
    //Add to list
    $item = $xmlDoc->createElement(‘item’);
    $item = $root->appendChild($item);
    $item->setAttribute(‘id’,$key);
    $item->setAttribute(‘label’,rawurlencode($label));
    //rawurlencode evita problemas de charset
    //rawurlencode avoids charset problems
    }else{
    $xmlDoc .= ”;
    }
    }

    }

    //Retornar XML de resultado para AJAX
    //Return XML code for AJAX Request
    header(“Content-type:application/xml; charset=utf-8″);
    if (class_exists(‘DOMDocument’)){
    echo $xmlDoc->saveXML();
    }else{
    $xmlDoc .= ”;
    echo $xmlDoc;
    }

    Please any help???

  21. Hi

    Just wondering …

    Would you be interested in helping to integrate it into a Viart chopping cart?

    Cheers

  22. Hi, I love this script, and have tailored it.

    I have one problem, when the user uses arrow keys and hits ENTER on the keyboard, the value is not populating. In all other circumstances it is, and it works on your demo.
    I can’t figure out for the life of me why.
    Any suggestions on where to look?

    Cheers

    John

  23. Actually, the value is being stored and submitting in my form, it is just not displaying on tab or enter, or on the iPod/iPad. I am starting to wonder if it is because there is no match of the returned text, because I am displaying multiple fields in the returned text. I will strip it back to a searchable value.

    Regardless, thanks for the script, loving playing with it.

  24. I fixed it. I had to add AC.clearField = false; under the ajax target.
    Then when usign tab or enter it was returning the string into the field with the html encoding so it had bold tagging, so I modified this function to return flabel rather than label.

    this.acChoose = function (id){

    if (id != ”){
    //Função de retorno (Opcional)
    if (me.chooseFunc != null){
    me.chooseFunc(id,unescape(me.arrItens[id]['label']));
    }
    }

    //Esconder lista de clientes
    me.hideDiv();
    if (this.clearField){
    me.elem.value = ”;
    }else{
    me.elem.value = unescape(me.arrItens[id]['flabel']); //changed label to flabel
    }

    }

  25. hi,
    i am trying to put 2 textboxes reading names in one and phone nos in another.the first textbox is working but the second is not.please help with this code.
    —————————————————————————————-

    Ajax Auto Suggest

    function lookup(inputString)
    {
    if(inputString.length == 0)
    {
    // Hide the suggestion box.
    $(‘#suggestions’).hide();
    }
    else
    {
    $.post(“rpc.php”, {queryString: “”+inputString+””}, function(data)
    {
    if(data.length >0)
    {
    $(‘#suggestions’).show();
    $(‘#autoSuggestionsList’).html(data);
    }
    });
    }
    } // lookup

    function fill(thisValue) {
    $(‘#inputString’).val(thisValue);
    setTimeout(“$(‘#suggestions’).hide();”, 200);
    }

    function lookup2(inputString2)
    {
    if(inputString2.length == 0)
    {
    // Hide the suggestion box.
    $(‘#suggestions2′).hide();
    }
    else
    {
    $.post(“rpc2.php”, {queryString: “”+inputString2+””}, function(data)
    {
    if(data.length >0)
    {
    $(‘#suggestions2′).show();
    $(‘#autoSuggestionsList2′).html(data);
    }
    });
    }
    } // lookup

    function fill2(thisValue) {
    $(‘#inputString2′).val(thisValue);
    setTimeout(“$(‘#suggestions2′).hide();”, 200);
    }

    body {
    font-family: Helvetica;
    font-size: 11px;
    color: #000;
    }

    h3 {
    margin: 0px;
    padding: 0px;
    }

    .suggestionsBox {
    position: relative;
    top: 35px;
    left: 450px;
    margin: 10px 0px 0px 0px;
    width: 200px;
    background-color: #323943;
    -moz-border-radius: 7px;
    -webkit-border-radius: 7px;
    border: 2px solid #000;
    color: #fff;
    }

    .suggestionsBox2 {
    position: relative;
    top: 65px;
    left: 450px;
    margin: 10px 0px 0px 0px;
    width: 200px;
    background-color: #323943;
    -moz-border-radius: 7px;
    -webkit-border-radius: 7px;
    border: 2px solid #000;
    color: #fff;
    }

    .suggestionList {
    margin: 0px;
    padding: 0px;
    }

    .suggestionList2 {
    margin: 0px;
    padding: 0px;
    }

    .suggestionList li {

    margin: 0px 0px 3px 0px;
    padding: 3px;
    cursor: pointer;
    }

    .suggestionList li:hover {
    background-color: #323943;
    }

    <!– –>

     

    <!– –>

     

    To:

    From:

    —————————————————————————————–
    php code from database

    real_escape_string($_POST['queryString']);

    // Is the string length greater than 0?

    if (strlen($queryString) > 0)
    {
    // Run the query: We use LIKE ‘$queryString%’
    // The percentage sign is a wild-card, in my example of countries it works like this…
    // $queryString = ‘Uni';
    // Returned data = ‘United States, United Kindom';

    // YOU NEED TO ALTER THE QUERY TO MATCH YOUR DATABASE.
    // eg: SELECT yourColumnName FROM yourTable WHERE yourColumnName LIKE ‘$queryString%’ LIMIT 10

    $query = $db->query(“SELECT first_name FROM info WHERE first_name LIKE ‘$queryString%’ LIMIT 5″);
    if ($query)
    {
    // While there are results loop through them – fetching an Object (i like PHP5 btw!).
    while ($result = $query->fetch_object())
    {
    // Format the results, i m using for the list, you can change it.
    // The onClick function fills the textbox with the result.

    // YOU MUST CHANGE: $result->value to $result->your_colum
    echo ‘first_name . ‘\’);”>’ . $result->first_name . ”;
    }
    }
    else
    {
    echo ‘ERROR: There was a problem with the query.';
    }
    }
    else
    {
    // Dont do anything.
    } // There is a queryString.
    }
    else
    {
    echo ‘There should be no direct access to this script!';
    }
    }

    ?>

Comments are closed.