Autocomplete with AJAX in WordPress

Autocomplete with AJAX in WordPress: 2019

David Nash code, tutorial, wordpress 3 Comments

This is the second part of Create an autocomplete field in WordPress: 2019, which works well if you don’t have that many options. If you have less than 10 options, use a select element. Less than 100-1000 (depending), you don’t need AJAX. If you have more than 100-1000, you’ll probably need to use autocomplete with AJAX.

This means the page will load faster, and we’ll limit the data to entries that more closely match what the user is looking for.

We didn’t even need Javascript for the previous part. What we’re doing now is a bit more complex, so we’ll need it now. I’m not going to use jQuery, and I recommend you stop using it too. If you know jQuery then Plain Javascript is a great site to help you with the transition.

I’ve created a branch of the wp-autocomplete theme on github, and again will link to the commits for each step. And continuing in the theme of making small, easy steps, let’s get started with the first one!

Let’s look at the Awesomplete documentation

Fortunately Awesomplete provides an example of how to implement autocomplete with AJAX, which is so short I’ll show it here:

var ajax = new XMLHttpRequest();"GET", "", true);
ajax.onload = function() {
	var list = JSON.parse(ajax.responseText).map(function(i) { return; });
	new Awesomplete(document.querySelector("#ajax-example input"),{ list: list });

Looks pretty straight-forward. We can use that, and just update the URL and the input selector to match our page.

In order to run that code, we need a couple of things:

  1. Update our theme to run the Javascript, which we’ll copy and paste from the example
  2. Set up an AJAX endpoint function so that our theme returns some data for Awesomplete
  3. Modify the above function so that what we see come from our WordPress database

Load custom javascript

This is pretty simple, but it’s our first step towards our goal of autocomplete with AJAX.

So let’s create the js directory within the theme, and then within that a file called wp-autocomplete.js. And then we’ll edit functions.php to tell our theme to load that javascript file.

And in wp-autocomplete.js, we’ll just have it output a message to the developer console:

document.addEventListener('DOMContentLoaded', function() {
    console.log('wp-autocomplete.js loaded!');

Let’s refresh the page to make sure it does… and success!

You can see the changes in this commit on github.

Clean up index.php and do a basic autocomplete without AJAX

We can strip index.php down to the following very simple code:

<?php get_header(); ?>

<input id="autocomplete-field" />

<?php get_footer(); ?>

Simple is good! Much less that can go wrong. But then we’ll update the javascript to look like this:

document.addEventListener('DOMContentLoaded', function() {
    console.log('wp-autocomplete.js loaded!');

    // Select the <input id="autocomplete-field" /> element in our index.php:
    var autocomplete_field = document.getElementById('autocomplete-field');

    // Basic Awesomplete demo
    new Awesomplete( autocomplete_field, {
        list: ["Ada", "Java", "JavaScript", "Node.js", "PHP", "Perl", "Python", "Ruby on Rails"]

Now when we reload the page we should still see our “loaded” message, and then when we test out the autocomplete field, we should see this:

Autocomplete in WordPress - using Javascript but not AJAX, yet
Autocomplete without AJAX

Have a look at the code changes with this github commit.

Autocomplete with AJAX (but not our own data)

Now that this is working, let’s jump back to the Awesomplete AJAX demo and update our javascript so that it looks like this:

document.addEventListener('DOMContentLoaded', function() {
    // Awesomplete AJAX demo from
    var ajax = new XMLHttpRequest();"GET", "", true);
    ajax.onload = function() {
        var list = JSON.parse(ajax.responseText).map(function(i) { return; });
        new Awesomplete(document.getElementById("autocomplete-field"),{ list: list });

I’ve made a couple of changes from the demo code they supply. I’ve changed their querySelector() to getElementById() and used the ID of our input field element. And because I thought it was fun I changed the language from fr (French) to en (English). Also, having more results makes for a better screenshot:

Autocomplete with AJAX
Autocomplete with AJAX – but not our data

Sweet! And here’s the github commit showing the code changes for this step. Next we’ll take a bit of a sideways step and get WordPress to respond to AJAX requests.

Set up our theme to handle AJAX requests

When we want to build something up, sometimes it means knocking things down. So let’s forget about Awesomplete for the moment.

First, let’s look at functions.php. We can leave the Awesomplete style and script enqueue stuff, because we don’t mind if that’s loaded. But we need a way of telling our theme JS file where WordPress’s AJAX URL is. So we add this code inside add_theme_scripts():

wp_localize_script('theme-js', 'wp_autocomplete', array('ajax_url' => admin_url('admin-ajax.php')));

Next we need to add our AJAX function inside functions.php:

function get_autocomplete() {
    if ( isset($_POST['user_input']) ) {
        echo $_POST['user_input'];

    die(); // Stop WordPress from outputting 0
add_action('wp_ajax_autocomplete_data', 'get_autocomplete');
add_action('wp_ajax_nopriv_autocomplete_data', 'get_autocomplete');

To break it down, starting from add_action() at the bottom:

When admin-ajax.php receives a request with action=autocomplete_data, it calls get_autocomplete() in functions.php. The wp_ajax_ and wp_ajax_nopriv_ hooks mean that it’ll work whether or not the user is logged in as an admin user.

I know, this is still really confusing. Bear with me. It’s just one of those bits of WordPress that’s hard to understand.

Then, in our get_autocomplete() function, we just want to send back whatever it was that we received from some other POST variable. I’ve called it user_input.

We’re still in a state where we can’t see if this works yet, so let’s take a deep breath and move on to the next step.

Simple WordPress AJAX javascript

Let’s totally change our existing js/wp-autocomplete.js script, so that it looks like this:

document.addEventListener('DOMContentLoaded', function() {
    // Make sure we're getting the variable/URL from wp_localize_script in functions.php
    console.log( 'ajax_url: ' + wp_autocomplete.ajax_url );

    var ajax = new XMLHttpRequest();

    // Open our autocomplete URL"POST", wp_autocomplete.ajax_url);  // From functions.php and wp_localize_script()

    // Tell it what sort of data we're sending
    ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    // Create some sample "input"
    var user_input = encodeURI('this is a test');

    // And send it
    ajax.send('action=autocomplete_data&user_input=' + user_input);

    // Output whatever we get back from the AJAX request
    ajax.onload = function() {

Open the web developer console, and first you should see this:

ajax_url: http://wp-autocomplete.localhost/wp-admin/admin-ajax.php

And on the next line you should see this:

this is a test

Here’s the commit for these changes.

I encourage you to play around with this. Change the “this is a test” text and you should see the same text sent back to you. In functions.php you could also have get_autocomplete() do something other than just echo the text – maybe try this instead:

echo strtoupper( $_POST['user_input'] );

Whatever you do, just make one change at a time and reload your page to make sure it works. If it does, try to work out why it broke. If it doesn’t, make another change!

Let’s get more complex and get data from the field

Now we’ll be a bit more interactive. Let’s add an event listener so that when our input field changes, we then make the AJAX request.

Here’s get_autocomplete() in our functions.php file:

function get_autocomplete() {
    if ( isset($_POST['user_input']) ) {
        $list = array( 'aa first item', 'aa second item', 'aa third item', 'aa fourth item' );
        echo json_encode( $list );

    die(); // Stop WordPress from outputting 0

And what we’re saying above is “if the user has inputted something, return the array”. I’ve used ‘aa’ at the start of each so that we can type ‘aa’ into our autocomplete field and get some results.

I needed to write a bit of code to get XMLHttpRequest() working, and now I think I should have gone with the simpler Fetch API. Fetch is now supported in all major browsers except Internet Explorer, and we could use a polyfill for the ~2.5% of IE holdouts. But I digress…

document.addEventListener('DOMContentLoaded', function() {
    var ajax = new XMLHttpRequest();
    const min_letters = 2;  // how many letters before we start doing AJAX requests
    var autocomplete_field = document.getElementById('autocomplete-field');
    var awesomeplete_field = new Awesomplete(autocomplete_field);

    // When the user presses and releases a key, get the input value
    autocomplete_field.addEventListener('keyup', function() {
        var user_input = this.value;  // Use another variable for developer clarity

        // If there's enough letters in the field
        if ( user_input.length >= min_letters ) {
            // Do the AJAX request
  "POST", wp_autocomplete.ajax_url);
            ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            ajax.send('action=autocomplete_data&user_input=' + user_input );

    // When we get a response from AJAX
    ajax.onload = function() {
        var json_list = JSON.parse(ajax.responseText);  // Parse the JSON from functions.php
        awesomeplete_field.list = json_list;  // Update the Awesomplete list
        awesomeplete_field.evaluate();  // And tell Awesomplete that we've done so

Hopefully it’s commented well enough for you to get an idea of what’s going on here. And here’s the commit for these updates.

Let’s get some data from the dang database already!

We’re almost there! Let’s get our autocomplete values from the database. Our javascript doesn’t need to be changed. And here’s our get_autocomplete() in functions.php:

function get_autocomplete() {
    global $wpdb; // WordPress's database object

    if ( isset($_POST['user_input']) ) {
        $input = $wpdb->esc_like(stripslashes($_POST['user_input']));

        // Match strings that start with what user typed:
        $input_starts_with = $input . '%';

        // Or perhaps match strings that end with what the user typed:
        $input_ends_with = '%' . $input;

        // Or match if the user's input is anywhere in the string
        $input_contains = '%' . $input . '%';

        $sql = "select post_title
            from $wpdb->posts
            where post_title like %s
            and post_status='publish'";

        $sql = $wpdb->prepare($sql, $input_contains);  // Replaces the %s in $sql with $input
        $results = $wpdb->get_results($sql);  // Get the rows from the database

        // Build an array of matching post titles
        $post_titles = array();
        foreach ($results as $r) {
            $post_titles[] = addslashes($r->post_title);

        echo json_encode($post_titles);

    die(); // Stop WordPress from outputting 0

I’ve provided three options for the MySQL like statement, and used the one that most people are most likely to want – searching for the user’s input anywhere in the post title string. We’re only using one, $input_contains.

To give you more flexibility, I’ve used an SQL statement. This could be applied to any database table. If we were really looking for posts, it’d be better to use native WordPress functions for that.

Have a look at the new code in this commit.

This is all very exciting to see it working, but it doesn’t really do anything. When the user clicks a title, nothing happens. Let’s fix that.

Trigger an event on autocomplete item click

After lots of build-up, here’s what you came for. At the moment we’re just sending labels to awesomplete via AJAX. Let’s also send the post’s URL for each result.

At the end of the Basic Usage section of the Awesomplete documentation, it show us how to send label/value pairs. We’ll keep the post name as the label, and then use the post URL for the value.

But instead of replacing the input field with the key, we’ll override that so that clicking on a suggestion will act as if we clicked a link.

In functions.php we modify the results loop to look like this (and change the $post_titles variable to $post_data so it more accurately reflects what it contains):

// Build an array of matching post URLs and titles
$post_data = array();
foreach ($results as $r) {
    $post_data[] = array(
        'value' => get_permalink($r->ID),  // Note: This is another database call
        'label' => addslashes($r->post_title),

echo json_encode($post_data);

Then in our javascript we add the following, so that instead of putting the value into the input field, it redirects the browser:

    // Instead of inserting the slug into the input field, follow the link
    awesomplete_field.replace = function(suggestion) {
        window.location.href = suggestion.value;  // Redirect the browser

Here’s the changes for this step. Please note I realised that I’d spelt the library as “Awesomeplete”, but the correct spelling is “Awesomplete” (without the ‘e’) and I’ve fixed that too.

I’ve also updated our simple index.php to show the title of the page that we’re currently on.

Here’s the commit on github. And here’s the finished result:

Autocomplete with AJAX: Conclusion

Creating something you’ve never done before can be complex. But if we take it in small, simple steps, that complexity is greatly reduced. There’s still the odd bit of frustration. I went through it myself while creating this post. But if you just do a little bit at a time, and verify each step along the way, you take one big problem and turn it into lots of little ones.

I hope you’ve enjoyed this post. I’ve stuck with native Javascript, but I suspect a lot of WordPress developers are still much more comfortable with jQuery.

Would you like to see something like this in jQuery? Is there something I missed here? Is there anything you’d like to add? Please let me know in the comments!

Comments 3

  1. Thank you for this post David, truly appreciate it.

    Is there a way of querying a large list from a database and having those results autocomplete?

    Ideally, I want to store a list of contacts in a DB and have a user search for their record and then submit a form.

    Is this possible? What steps would I need to take?

  2. David,

    Thank you so much for this tutorial! Of all the solutions I tried, this one was effortless to implement and works exactly as I hoped. I’m grateful that you put this together as I prefer code to plugins.

    I’m implementing in a child theme lines so lines 3, 4 and 5 in functions.php were changed to “get_theme_file_uri” instead of “get_template_directory_uri”. Hopefully that is useful to future visitors.



Leave a Reply