Autocomplete search is a tricky thing to track. The underlying logic is that when the user starts feeding characters into a search form, the search suggests results based on a limited input. If the user is not satisfied with the results, they can continue adding characters to the search, thus increasing the accuracy. Often there’s also the option to revert to a regular search with what they’ve already written. Tracking this logic in tools like Google Analytics is difficult, because there’s really no way to know if the search was successful.

Take the example in the screenshot above. The user has written ban into the search field, and they’ve received a list of search items to proceed with. If they now chose any item in that list, would the search be considered successful?

Yes. And (maybe) no. Yes, it would be a successful search because the user clicked an item in the list, thus validating the suggestion. And (maybe) no, because we have no idea if that is the item the user was searching for in the first place. If the purpose of our search is to match a response to a query, then any click on the search results is a success. If the purpose of our search is to match a response to the original intent of the query, then only a certain type of search result click is a success.

This is going too far into the ontology of search and intent, so I’m going to stop with the philosophy here. However, what I do want to show you is a pretty simple way of tracking what users do search in the autocomplete search field, using Google Tag Manager. It’s up to you to decipher if the searches are successful, though.

How it works

The setup is basically just a Custom HTML tag. In this tag, we’ll write a custom listener for the keydown browser event, which is registered when the user presses (down) a key. You must provide a minimum length of search for this handler, as well as the timeout you want the browser to wait before sending a search term to Google Analytics (or whatever platform you want to use this with).

For example, if you set minLength = 3 and timeout = 2000, the handler will only send searches that are at least three characters in length, and where 2 seconds have elapsed since the last character was written. This is necessary to eliminate accidental keystrokes and search terms with far too little information to be useful.

The search event thus occurs when two seconds have passed since the last character has been typed OR when the user presses the Enter key OR when the focus leaves the search field.

The code is all contained in a single Custom HTML tag which fires on the DOM Ready trigger. You’ll then need a Custom Event trigger to fire when a valid search is recorded, as well as a Data Layer Variable to grab that search term.

1. The Custom HTML tag

Here’s the code that runs the whole thing:

<script>
  (function() {
    // Set searchField to the search input field.
    // Set timeout to the time you want to wait after the last character in milliseconds.
    // Set minLength to the minimum number of characters that constitutes a valid search.
    var searchField = document.querySelector('input#search-field'),
        timeout = 2000,
        minLength = 3;

    var textEntered = false;

    var timer, searchText;
    
    var handleInput = function() {
      searchText = searchField ? searchField.value : '';
      if (searchText.length < minLength) {
        return;
      }
      window.dataLayer.push({
        event: 'customSearch',
        customSearchInput: searchText
      });
      textEntered = false;
    };
    
    var startTimer = function(e) {
      textEntered = true;
      window.clearTimeout(timer);
      if (e.keyCode === 13) {
        handleInput();
        return;
      }
      timer = setTimeout(handleInput, timeout);
    };
    
    if (searchField !== null) {
      searchField.addEventListener('keydown', startTimer, true);
      searchField.addEventListener('blur', function() {
        if (textEntered) {
          window.clearTimeout(timer);
          handleInput();
        }
      }, true);
    }
  })();
</script>

First, you need to define some utility variables.

  • Set searchField to capture the search field HTML element. This is the element to which the handlers will be attached to.

  • Set timeout to the time in milliseconds you want the script to wait after the last character has been typed into the field. This is to prevent the search event from happening too often, especially when people type slowly.

  • Set minLength to the minimum number of characters that constitutes a search. If the search is less than this number in length, the search will not be recorded.

The handleInput method checks if the search is long enough. If it is, an object is pushed to dataLayer with the following key-value pairs:

  • event: 'customSearch', which we’ll use to build the Custom Event Trigger that fires any tags you want when a search is recorded.

  • customSearchInput: searchText, which grabs the text the user wrote into the search field.

The startTimer method is run each time the user types something into the search field. First, it stops the current timer (which is counting the time to the timeout limit), because we want to reset the timer with each key press.

Next, it checks if the key that was pressed was the Enter key (keyCode === 13). If it was, then we interpret this as a valid search and run the handleInput method. Finally, the timer is started again, and after the timer hits the timeout limit, handleInput is called.

The final rows of the script add the keydown and blur listeners to the search field to track keystrokes and if the focus leaves the search field, respectively.

2. The triggers

To fire the Custom HTML tag above, use a DOM Ready trigger. It makes sense to only fire the trigger on pages where you have the search form, so adding a DOM Variable condition to the trigger is not an altogether bad idea.

This way your search listener will only fire on pages with the search field (sensible!).

Next, you’ll need a Custom Event trigger to fire your tags when a successful search is registered:

As you can see, I’m also checking if the search field had some text. This is, again, a sensible precaution to avoid false positives if something goes wrong with the script.

3. The Data Layer Variable

The Data Layer Variable you’ll need to capture the search term is simple:

This Data Layer Variable pulls the value of the key customSearchInput, pushed by the script upon a successfully recorded search.

Putting it all together

So now you have the components, and it’s time to put this all together. Below is an example, where I use a Universal Analytics tag to send the search term to Google Analytics using a custom query parameter.

The key is the page field, which is overwritten with:

/search/?q={{DLV - customSearchInput}}

Thus, when the Universal Analytics fires with the Event - customSearch trigger, the page field is sent with the custom path /search/?q=searchterm. So if I wrote uunilohi into the search field, the page path sent to GA would be /search/?q=uunilohi.

After this, all you need to do is go to View Settings in Google Analytics, and set the parameter q to be the site search parameter Google Analytics uses to build the site search reports.

Summary

Tracking autocomplete search is difficult not just because of the technical restrictions but because it’s difficult to determine original intent with just a few characters to work with.

It can be argued that ANY search that precedes a click of search results or even a conversion is successful, and I tend to agree with this. However, especially for content creators it’s important to align searches with relevant results, not just any old result that the user finds attractive. For this reason, it’s important to track partial searches as well as searches with refined inputs.

How have you solved the problem of autocomplete search? Do you trust the site search reports when you see only partial searches, and do you find this information useful when optimizing the site to be more responsive to complex searches?