Create an auto-complete field in WordPress

David Nash wordpress 29 Comments

I’m working on a project that has a large database – about 14,000 business listings. It also has a page for a user to sign up and select which business they work at. 14,000 is way too many options for a SELECT (drop-down) field and I don’t want to put that much load on the MySQL database either.

My solution: an auto-complete text field that reads from the WordPress database.

I take it one step at a time, and make sure each step works correctly before starting the next step. Here are the steps I took to do it.

Front-end JavaScript

I’m using a child theme because I’m customising a premium theme. In the child-theme’s function.php, I add code to load the JavaScript:

I create /js/mysite.js in the child theme directory that looks like this:

When I reload any page on the site I now get “jQuery loaded” in a JavaScript alert box, so I’m happy that’s working. I could have also used console.log(‘hello’); and checked the Developer Console in Chrome.

Next I downloaded jQuery-autocomplete. I could have used the jQuery-UI autocomplete but I’m not using it otherwise and like to keep things lightweight.

From the download I copied jquery.auto-complete.css and jquery-autocomplete.js to /js/ – the same directory that has mysite.js

Then update functions.php to load the library:

I reload the page, make sure the library is appearing in Chrome Dev Console under the “Sources” tab.

The AJAX code

Next I update mysite.js to do something useful:

#company_works_at is the field on the “Register” page I want to autocomplete. I followed the docs on the autocomplete page – the “source” function needs “response” callback to make it work, this is in the “success” function in the jQuery $.ajax() call.

The PHP that sends AJAX data from WordPress

Back in functions.php, I add this:

The JavaScript that makes the AJAX call sends the data in “name”. I use stripslashes() so that names with apostrophes etc work okay. I use esc_like() for security. The ‘%’ at the end means that I only want to match based on the starting characters. For example if a user types ‘the’ I want to see results like ‘The Best Business’, not ‘Not the best business’. But if they type ‘not’ I want to see ‘Not the best business’.

Testing that it works

I click on the auto-complete field, type three characters and get a fairly snappy response. It auto-suggests several businesses, which the user can select.

This is just a text field, and it also allows the user to type the name of a business that is not in the database. If we want we could create more restrictions, and instead of saving text save the ID of the business listing. We could then use the ID to create a link to that business listing on some other template.

Comments 29

  1. You’re loading the names from a custom post type or what? I’m trying to imply this code but I get no error but my autocomplete doesn’t work. I guess it has something do to with the fact that I’m trying to get post titles from a custom post type. Do I need to change the $_POST value or the query?

    1. Post
      Author

      Hi Genuanceerd, that’s right – in this case my custom post type is ‘job_listing’. If you update that in the functions.php code to your type, it should work.

      AJAX can be hard to debug but if you use the Developer Console in your browser you can should be able to see any errors. You can also echo variables there to see what they contain. You can also use the network tab to see the responses from ajax.php.

      1. And do I need to change something in case I have a different table prefix or is that something that doesn’t matter when using $wpdb. And what does the $_POST[‘name’] code do? Because I notice there are requests in my console, but it isn’t showing any results.

      2. I’m also not seeing any errors, that’s why I’m confused.
        Adjusted the sql query but that didn’t work either.

        1. Post
          Author

          That’s right, $wpdb handles the database prefix.

          $_POST[‘name’] is the POST variable that gets sent by the autocomplete library – ie whatever text is in the field that you’re autocompleting on. In the example above, that field has id ‘company_works_at’:

          1. Well I have the right id, in the html it has added autocomplete=”off” so that should be fine. But whenever I’m typing in the field, there isn’t happening anything.

  2. Hi David, wanted to let you know that it works for me too at the moment. I directly changed the code for my own while I was following the tutorial. So when it didn’t work, I literally copied exact the same code as you wrote and it worked. Then I adjusted it to my preferences. Thanks for this!

  3. Hello

    First thank you for this code, just what I need. But I don’t get it to work.

    The error I get is: Resource interpreted as Stylesheet but transferred with MIME type text/html: and it points to my input field that I want the auto complete on.

    The difference I have from your example is that I have added this to my plugin instead of a child theme. I have also a search in the usersmeta table.
    I can do an alert on the data that is returned to the jQuery.

    Could you please help?

    Best Regards

  4. Hi, your guide really helped me get started with my custom autocomplete field. What I cant figure out right now is a way to search two different columns in the same table for the autocomplete function, while displaying autocomplete results from either column. Some sort of OR query, like:

    …where columnA like %s OR colummB like %s

    Thanks!

  5. Hi again, I managed to solve my problem immediately after asking here by including an if(!empty… for the $sql and including a second $sql definition.
    Not pretty but it works.

  6. It seems to only load information from the first word, is this an intended behavior?

    Could someone help me have it search within the title?

    Thanks.

    1. Post
      Author

      That’s right, the code above only searches from the start of the string.

      To search anywhere within the string, you can use this in your functions.php

      $name = ‘%’.$wpdb->esc_like(stripslashes($_POST[‘name’])).’%’;

      It’s basically the same as the original code, except now there’s a ‘%’ added to the start of the LIKE string. % is the MySQL wildcard.

      So instead of “stuff*” we’re now searching for “*stuff*”.

      1. Perfect, really appreciate the help. Playing for hours before asking , hope I learned something along the way 🙂

        Those parentheses gave me some issues for some reason.

        $name = ‘%’.$wpdb->esc_like(stripslashes($_POST[‘name’])).’%’;

        1. David, its me again 🙂 Need your help. I noticed a bug that one of titles has a qoute ” for inches. When the field is selected from the dropdown it doesn’t show the info after the qoute in the textarea.

          I pretty sure it escaping cause of the quote. Not sure if this is at the javascript or php level. Wondering if there is some sort of stripslashes magic you could help me with.

          Thanks man.

          1. Post
            Author

            Escaping would always happen at the PHP level. You might need to add addslashes() around the returned code. I assumed that json_encode() would have handled that.

            I’ve updated the code above. Not 100% sure it’ll work, and don’t have time to have a look in detail. Maybe you could let me know?

  7. Looks like reply is disabled after going down deep.

    If your referring to CSS it did not take effect. Maybe your using a CDN and it will take time for the changes to propagate. You still have.

    .x-comments-list .children {
    margin: 0 0 0 89px;
    }

    I was wrong before, you still should have some sort of indent. My bad

    .x-comments-list .children {
    margin: 0 0 0 10px;
    }

    Starting to think the qoute problem is with the javascript. I tried the code below with no luck.

    $name1 = ‘%’.$wpdb->esc_like(stripslashes($_POST[‘name’])).’%’; //escape for use in LIKE statement

    $name = addslashes($name1);

  8. I David, tnx for sharing your solution.
    Do you know how to manage the row ID into the showed data list?
    I have a data list with AUTHOR NAMES taken from a DB and when I select an author I want to populate another input field with relative AUTHOR ID taken in the DB.
    Can you help me?
    tnx a lot

    1. “Hi David” and not “I David” 🙂 Sorry for my bad English.

      I solved my questions by passing an array of objects with label and value properties:
      [ { label: “Choice1”, value: “value1” }, … ]

      upgraded the titles array into foreach into function.php :

      foreach( $results as $r )
      $titles[] = array(‘value’=>addslashes($r->ID),
      ‘label’=>addslashes($r->nome));

      Then I used the select event/function into the javascript

      jQuery(document).ready(function($) {

      $(‘#nomeautori’).autocomplete({
      select: function (event, ui) {
      $(“#nomeautori”).val(ui.item.label); // display the selected text
      $(“#IDautori”).val(ui.item.value); // save selected id to hidden input

      var id = ui.item.value;
      var name = ui.item.label;
      $(“#nomeautori”).val(name);
      return false;

      },
      source: function(name, response) {
      var searchParam = name.term;
      $.ajax({
      type: ‘POST’,
      dataType: ‘json’,
      url: ‘wp-admin/admin-ajax.php’,
      data: ‘action=get_listing_names&name=’+searchParam,
      success: function(data) {
      response(data);
      }

      });

      }

      });

      });

  9. Hello,

    Follow the steps in your tutorial and I do not work the field for the auto complete, not might be wrong, if I can give an idea please as I am new to php code to insert theme.

    the field I want to use is the one that says ¿Que Buscas?

  10. I really like what you have done, but would like to implement this for the main search on WP. I changed #company_works_at to #s for the search field. In functions.php I changed post_type from job_listings to post. Do you know what else I need to do to get this to work? Thanks

    1. Post
      Author

      That sounds like it should work. See if you can separate each step – what data is getting sent to the server? what is the server returning? and what does your JS display that data correctly?

      You might be better off using a dedicated plugin, rather than modifying this. This code will only search titles, for posts you will probably want to search the content of the posts too.

      1. Thanks for the reply, David, I appreciate it.

        You’re probably right that I should use a plugin. The only suitable one I found though was Search Autocomplete by Gabe Shackle which searches titles and categories. The other plugins mostly search only the titles. Unfortunately, Gabe’s plugin hasn’t been updated for some time and the code is very heavy. It seems to mostly auto complete after the user finishes typing, which is too slow.

        I just thought that your code looked like the solution, you’ve done a great job. It’s light and looks very logical, I can understand why you said it gives a snappy response. Sadly, I can’t get it to show a drop down box, it doesn’t auto complete, it just works as normal search. I’m not skilled enough to understand why. I can’t find any error logs and I don’t know what data is sent to the server.

        It’s a real shame, especially as I can’t find a proper solution to this anywhere. I thought a good functioning auto complete search would be essential to a lot of people using WP.

        Thanks for your time.

  11. Hi David, tnx for sharing your solution.
    It’s very clear, but it does’not work for me (i’m new in wp) and i don’t undestand where i’m wrong. I have add a Console.log and a breakpoint into jquery function (before response data) with data value, i run the page with wp preview and in console i have an array with the result of the query, but my input did’nt display anything.

    This is the div that i add on the page test
    Test:

    Another question, if i want to run autocomplete after only a word type, i must add minLength: 1 after source parameter?

    I hope you can help me…Thanks a lot

Leave a Reply

Your email address will not be published. Required fields are marked *