Create an auto-complete field in WordPress

David Nash wordpress 57 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’.

Update: If you’d like to get all posts, remove  and post_type='job_listing' from $sql above.

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 57

  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);
      }

      });

      }

      });

      });

      1. Hi Mirko,

        I used your code with same method. I used similar.

        array(‘label’ => addslashes($r->meta_value), ‘value’ => addslashes($r->post_id) )

        and Json is like [{“label”:”Allis Chalmers”,”value”:”687″},..

        but now response is showing only HTML.

        i am trying on this form

        please check. http://bit.ly/2rWPi0N

  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

  12. David,

    Such a nice process, thank you for sharing. Would it be possible to use this only on a custom field in the backend of the checkout? I’m not nearly as programming savvy as you so be gentle please. Haha

    Thank you

  13. Hi David,

    very nice approach. Thanks for sharing this.

    But unfortunately it does not work for me. I think the reason is in the Ajax call. There is a call to an ajax function named get_listing_names. So I think, the function name is built to wp-ajax-get-listing-names in wp-admin/admin-ajax.php and is then called as an action in wp-admin/includes/ajax-actions.php. But there seems to be no function with that name.

    I’m using WP 4.7.2.
    Perhaps there may be another action that has to be called in the Ajax Script?

    Thank you.

    1. Post
      Author

      Hi Nikolas

      WordPress won’t let you use AJAX to call functions directly. In the functions.php, we have this:

      add_action('wp_ajax_nopriv_get_listing_names', 'ajax_listings');
      add_action('wp_ajax_get_listing_names', 'ajax_listings');

      Our javascript code sets the ‘action’ to get_listing_names, and WordPress runs the hooks above – wp_ajax_[action] (for logged in users) and wp_ajax_nopriv_[action] (for logged out users). We point these hooks to run the ajax_listings() function.

      So the AJAX isn’t running a function, it’s running a hook which in turn runs the function.

      To be honest it totally confused me the first time I tried it. But from a security perspective it makes sense to have this extra layer.

      1. Hi David,

        Thank you for clarifying this for me.

        So, to understand it right, can you describe the way, how data is returned after being read from posts table?
        And is it displayed in the input field used for autocomplete or do I have to define a second field for result data?
        My problem is, no suggestions will be returned to client, although I see a collection of results returned when watching network traffic.

        Last but not least, did you tell Ajax to wait for three chars or WP and can I change it to one char?

        Thanks again, David.
        Nikolas

        1. Post
          Author

          In the section “The PHP that sends AJAX data from WordPress”, each post_title is copied into a simple array, and then that array gets output via the json_encode function. It’s actually a string but on the javascript side, the autocomplete.js library converts it into a javascript array.

          You shouldn’t have to define a second field for the response, autoComplete will take care of that (with what looks like an absolutely position div below your input field). Have a look at the demo at https://goodies.pixabay.com/jquery/auto-complete/demo.html .

          If you’re having issues, in the code listed in “The AJAX code”, just under the line that says “success: function(data) {“, add console.log(data).

          Then in the web developer console you’ll see the raw data that the AJAX is getting.

          To change the number of characters from 3, add the minChars object property under the ‘source’ element property. Check the link above for the other options.

          1. Hi David,
            I finally managed to get response data back to form, but it appears under the footer.
            In order to have result near to autocomplete field, anything I can do?
            I’m using Avada child theme and caldera forms.
            Thanks, Nik

          2. Post
            Author
  14. Good day.
    How to go to page of selected title?
    I select element from dropdown list and want to go to page of this element.
    Thank you.

    1. Post
      Author

      Hi John, I don’t think it’s possible with the jQuery autoComplete library, which expects an array of text items. You would also need to provide the URL for each item so you can build the link. autoComplete just fills out the text input for you when you click an option.

  15. Hi David,
    Thanks for this awesome tutorial. I was trying to use this on a project I’m working on, this solution seems to perfectly fit my needs, but I was unable to get the code to work. I basically copied and pasted your code with necessary modifications, but it seems like jQuery autocomplete function is not getting called. When I load dev console, the file is included. In my-site.js (alert works here…), I changed id: #company_works_at to id:#my_own_css_id also, my functions.php, which I think might have the issue but should be the same as in your tutorial, has:

    add_action(‘wp_enqueue_scripts’, ‘mysite_js’);

    function mysite_js() {
    wp_enqueue_script(‘autocomplete’, get_stylesheet_directory_uri().’/js/jquery.auto-complete.js’, array(‘jquery’));
    wp_enqueue_script(‘mysite-js’, get_stylesheet_directory_uri().’/js/mysite.js’, array(‘jquery’, ‘autocomplete’));
    }

    Is there something else I can do to make this code work? Thanks.

    1. Post
      Author

      Hi Tony

      If you’re not seeing any 404s in the developer console, and you can see that both jquery.auto-complete.js and mysite.js are getting loaded then the issue would either be in mysite.js or the AJAX code in functions.php.

      I would start by looking at mysite.js – replace the ajax call with local data (ie just a simple array). If you look at the docs here: https://goodies.pixabay.com/jquery/auto-complete/demo.html, scroll down to “Demos” and the first one is “Searching in local data”. You can use that code to make sure the JS is okay.

      Assuming it is, the issue must lie in functions.php. To keep it simple I would ignore the autocomplete js for a while. In functions.php, edit the ajax_listings() function so that it outputs a simple string like “it works!” (followed by die(), so the rest of the code is skipped). In mysite.js just have an ajax function that logs the output, like this:

      $.ajax({
      url: ‘/wp-admin/admin-ajax.php’,
      data: ‘action=get_listing_names’,
      success: function(data) {
      console.log(data);
      }
      });

      That should provide enough info to work out where the problem lies. It wouldn’t hurt to check your web server log for errors too.

      Good luck!

  16. David this is a great tutorial – really helpful!

    I’m a bit stuck though with the JS – I can’t seem to trigger it from the text field.

    I have a simple form on a page:

    Name

    To narrow down why I couldn’t get things to work I tried with local data as per https://goodies.pixabay.com/jquery/auto-complete/demo.html sticking in some alerts so I knew script definitely running.

    jQuery(document).ready(function($) {
    $(‘#company_works_at’).autoComplete({
    minChars: 2,
    source: function(term, suggest){
    term = term.toLowerCase();
    var choices = [‘ActionScript’, ‘AppleScript’, ‘Asp’, …];
    var matches = [];
    for (i=0; i<choices.length; i++)
    if (~choices[i].toLowerCase().indexOf(term)) matches.push(choices[i]);
    suggest(matches);
    }
    alert('should be done now.');
    });

    Mysite.js is definitely being loaded but just the JS doesn't seem to trigger by input to the field.

    Am I referencing the field correct?

    Any advice much appreciated!

    1. Post
      Author

      Hi Tom, does the “name” field have the id company_works_at? To trigger the JS it should look like

      If not, change company_works_at in mysite.js to whatever the id of your name input element is… eg $(‘#name’).autoComplete({ … });

        1. Post
          Author
          1. Hang on – I’d made a silly mistake. Totally forgot to update my path to admin-ajax.php as my WP install isn’t in root.

            Now in console I can see that ‘get_listing_names’ query successfully sent, and the console is then showing the correct JSON response data but this isn’t actually displaying on my page.

            Afraid am stuck again – cheers for any help with this!

          2. Post
            Author

            If you use the developer console inspector, do you see the elements updating? From memory you should see the results directly after the input field. If you do, it’s a CSS issue. If you don’t, I’m not sure. I would create a very simple template with just the input field and nothing else – no header, footer, or other content, no JS or CSS except for what’s required to get this working. When I do that I usually work out what the error is, or I start building it back up from there.

          3. Cheers David – all sorted, was CSS error in the end… Just for others:

            Added a query.auto-complete.css in theme folder.

            .autocomplete-suggestions {
            text-align: left; cursor: default; border: 1px solid #ccc; border-top: 0; background: #fff; box-shadow: -1px 1px 3px rgba(0,0,0,.1);

            /* core styles should not be changed */
            position: absolute; display: none; z-index: 9999; max-height: 254px; overflow: hidden; overflow-y: auto; box-sizing: border-box;
            }
            .autocomplete-suggestion { position: relative; padding: 0 .6em; line-height: 23px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 1.02em; color: #333; }
            .autocomplete-suggestion b { font-weight: normal; color: #1f8dd6; }
            .autocomplete-suggestion.selected { background: #f0f0f0; }

        2. Quick question – would it be possible to use the field name instead of ID?

          I have a plugin which creates custom fields but only gives a ‘name’ selector instead of ID selector.

          Cheers, t.

          1. Post
            Author
  17. Hi David, thanks for your time and your tutorial!
    I was having no results shown, as many people reported here, and I fix it enqueuing also the autocomplete stylesheet, that you haven’t enqueued in your tutorial.
    I hope this can help anyone stuck at the same point as me!

    1. Post
      Author

      Thanks Miguel! I tried it myself, and it still functioned without the autocomplete CSS, but I have added that line in – it looks better now.

      This tutorial is proving to be quite popular, I think at some point I might write a new version that’s more thorough.

      Thanks again for your feedback, I appreciate it and I’m sure it’ll help some others!

  18. David, Congratulations on a very popular blog tutorial. It’s been 18 months and still getting questions. Kudos also for your continued replies to questions! Speaking of questions, I have a couple.

    When you said, “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.”

    Since jQuery-UI Autocomplete is included by default and used by other parts of WordPress already, wouldn’t it be more convenient to use it? You mentioned keeping things lightweight. What about using jQuery Suggest instead? That also comes with WordPress. See https://developer.wordpress.org/reference/functions/wp_enqueue_script/

    My next question has to do with an autocomplete feature I see often with WordPress. When adding users to a group in the admin panel, I get a text field that will show matching user names as I type. When I click on a suggested name, that name drops to the bottom of that text field and has an “X” in front for me to delete in case I change my mind. After the name is dropped in that area, the text field is cleared so I can type another name to add to the list at the bottom. When I click save, all those names are saved for that group in the DB.

    How would you recommend doing something like that?

    1. Post
      Author

      Hi Thomas, thanks for your kind comment!

      With jQuery-UI – that’s a good point. It’s used by wp-admin, but that doesn’t mean it needs to be used on the front-end. For theme development I like to only include what’s essential to reduce load times and increase the quality of user experience.

      jQuery suggest might be a good alternative, although it looks like it only provides one option at a time. Personally I prefer the list of options. Also it would be up to us to populate the array via ajax – Suggest doesn’t seem to have “native” ajax support.

      For your second question, I’ve previously used http://xoxco.com/projects/code/tagsinput/ – though unfortunately the autocomplete part of the demo is no longer working. You could also try http://textextjs.com/ – the demo there is working and seems to do what you’re after – though I haven’t used it myself.

  19. Hey David,

    Fantastic article! Thank you so much for sharing this!

    I was really wondering how to add links to each suggestion and I was actually able to come up with a working code.

    For all wondering, I’ll submit the code and give a brief overview.

    in the function.php I editing the code following the creation of titles array as follows:

    which generated an array containing all the job listing posts permalinks followed by all of the titles.
    Then I edited the mysite.js code to the following:

    Which basically converts the obtained php array to a new multidimensional array containing a list of arrays in the form: [post title, post url]. I am then able to run that through the autocomplete function using the options laid out above and end up with links on all of my suggestions from my input with name tag “lsearch”.

    All of this had to be done in the iDependOnMyParameter function as a workaround for the async false setting on the ajax to ensure asynchronous loading.

    I’m relatively new to php and ajax so if anyone has any suggestions on irrellevant code or bad practice please let me know. Just wanted to contribute to this forum because it helped me so much.

    1. Post
      Author
  20. Hi,

    What if I need to send multiple values back to client? Let’s say, I need to show output like “ID | Value” in the autocomplete table and set the textbox value as ID once one of the values from the table is selected.

    regards

Leave a Reply

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