Icon search ui white

Autocomplete

Last updated 14 September 2017

Before starting

Make sure you understand the following Algolia Concepts before starting this tutorial:

Introduction

This tutorial will walk through how to display results “as you type” in an autocomplete menu using Algolia’s search engine. We’ll be building this feature with our autocomplete.js JavaScript library.

Let’s begin by examining a possible use case: searching for players on a team. An autocomplete dropdown will help users easily find the player they are looking for within a few keystrokes.

To build this autocomplete, we will need to:

  1. Import the dataset of players to Algolia
  2. Configure the ranking information
  3. Utilize autocomplete.js to develop a user interface

Dataset

For this example, we will import player data (download JSON file) that includes the player’s name, team, and points. You can find a sample extract below.

[
  {
    "name": "Kevin Durant",
    "team": "Golden State Warriors",
    "points": 2472
  },
  {
    "name": "LeBron James",
    "team": "Cavaliers",
    "points": 2258
  },
  // [...]
]

Initialize the client

<?php
// composer autoload
require __DIR__ . '/vendor/autoload.php';

// if you are not using composer
// require_once 'path/to/algoliasearch.php';

$client = new \AlgoliaSearch\Client('YourApplicationID', 'YourAdminAPIKey');

$index = $client->initIndex('your_index_name');
require 'rubygems'
require 'algoliasearch'

Algolia.init application_id: 'YourApplicationID',
             api_key:        'YourAPIKey'
index = Algolia::Index.new("your_index_name")
// var algoliasearch = require('algoliasearch');
// var algoliasearch = require('algoliasearch/reactnative');
// var algoliasearch = require('algoliasearch/lite');
// import algoliasearch from 'algoliasearch';
//
// or just use algoliasearch if you are using a <script> tag
// if you are using AMD module loader, algoliasearch will not be defined in window,
// but in the AMD modules of the page

var client = algoliasearch('YourApplicationID', 'YourAPIKey');
var index = client.initIndex('your_index_name');
from algoliasearch import algoliasearch

client = algoliasearch.Client("YourApplicationID", 'YourAPIKey')
index = client.init_index('your_index_name')
import "github.com/algolia/algoliasearch-client-go/algoliasearch"

func main() {
    client := algoliasearch.NewClient("YourApplicationID", "YourAPIKey")
    index := client.InitIndex("your_index_name")
}

Index Configuration

Configuring your index properly will help ensure that the most relevant results appear first for your users.

Searchable attributes

By default, all the attributes in your records will be searchable. For this example, we want users to be able to search by player name and team, but not by points. To accomplish this functionality, we will need to set up a list of attributes to search.

Auto complete 1

Since searching for a player by name is our primary use case, we should indicate this attribute is more important by placing it at the top of the list.

Custom Ranking

To ensure users see the most relevant players, we will take into account the amount of points each player has scored (the higher the scores, the better). For that, we’ll configure the points attribute in the setting customRanking. Similar to the searchable attributes settings, this can be set up either via an API client or the dashboard (under the Ranking tab).

You can refer to our custom ranking guide for additional information regarding custom ranking setup.

Auto complete 2

Since we would like higher scoring players to appear first, we have set the points attribute to descending sort order.

Alternatively, these settings can be configured via the API client (see example code snippets below).

<?php
$client->initIndex("YourIndexName")->setSettings(array(
  "searchableAttributes" => array(
    "name",
    "team"
  ),
   "customRanking" => array(
      "desc(points)"
  )
));
index = Algolia::Index.new('players')

index.set_settings(
  searchableAttributes: %w(
    name
    team
  ),
  customRanking: %w(
    desc(points)
  )
)
client.initIndex('YourIndexName').setSettings({
  "searchableAttributes": [
    "name",
    "team"
  ],
  "customRanking": [
    "desc(points)"
  ]
});
client.init_index('YourIndexName').set_settings({
    "searchableAttributes": [
        "name",
        "team"
    ],
    "customRanking": [
        "desc(points)"
    ]
})
client.InitIndex("YourIndexName").SetSettings(JObject.Parse(@"{
  ""searchableAttributes"":[
    ""name"",
    ""team""
  ],
  ""customRanking"":[
    ""desc(points)""
  ]
}"));

User Interface

The easiest way to render an autocomplete dropdown menu is to use our autocomplete.js JavaScript library. It provides an out of the box autocomplete menu that can be easily configured and integrated with Algolia’s realtime search engine.

We have created a live demo of a basic autocomplete menu using the autocomplete library that allows users to search for players. Try querying by both player name (e.g. “Kobe Bryant”) and team (e.g. “Lakers”)!

<!-- HTML Markup -->
<div class="aa-input-container" id="aa-input-container">
    <input type="search" id="aa-search-input" class="aa-input-search" placeholder="Search for players or teams..." name="search" autocomplete="off" />
    <svg class="aa-input-icon" viewBox="654 -372 1664 1664">
        <path d="M1806,332c0-123.3-43.8-228.8-131.5-316.5C1586.8-72.2,1481.3-116,1358-116s-228.8,43.8-316.5,131.5  C953.8,103.2,910,208.7,910,332s43.8,228.8,131.5,316.5C1129.2,736.2,1234.7,780,1358,780s228.8-43.8,316.5-131.5  C1762.2,560.8,1806,455.3,1806,332z M2318,1164c0,34.7-12.7,64.7-38,90s-55.3,38-90,38c-36,0-66-12.7-90-38l-343-342  c-119.3,82.7-252.3,124-399,124c-95.3,0-186.5-18.5-273.5-55.5s-162-87-225-150s-113-138-150-225S654,427.3,654,332  s18.5-186.5,55.5-273.5s87-162,150-225s138-113,225-150S1262.7-372,1358-372s186.5,18.5,273.5,55.5s162,87,225,150s113,138,150,225  S2062,236.7,2062,332c0,146.7-41.3,279.7-124,399l343,343C2305.7,1098.7,2318,1128.7,2318,1164z" />
    </svg>
</div>
<!-- Include AlgoliaSearch JS Client and autocomplete.js library -->
<script src="https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js"></script>
<script src="https://cdn.jsdelivr.net/autocomplete.js/0/autocomplete.min.js"></script>
<!-- Initialize autocomplete menu -->
<script>
var client = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
var index = client.initIndex('YourIndex');
//initialize autocomplete on search input (ID selector must match)
autocomplete('#aa-search-input',
{ hint: false }, {
    source: autocomplete.sources.hits(index, {hitsPerPage: 5}),
    //value to be displayed in input control after user's suggestion selection
    displayKey: 'name',
    //hash of templates used when rendering dataset
    templates: {
        //'suggestion' templating function used to render a single suggestion
        suggestion: function(suggestion) {
          return '<span>' +
            suggestion._highlightResult.name.value + '</span><span>' +
            suggestion._highlightResult.team.value + '</span>';
        }
    }
});
</script>
<!-- HTML Markup -->
<div class="aa-input-container" id="aa-input-container" ng-app="myApp" ng-controller="yourController">
    <input type="search" id="aa-search-input" class="aa-input-search" placeholder="Search for players or teams..." name="search"
    autocomplete aa-datasets="getDatasets()" aa-options="{hint: false}" /> <!-- Ensure autocomplete and aa-datasets attributes are added to your search input -->
    <svg class="aa-input-icon" viewBox="654 -372 1664 1664">
        <path d="M1806,332c0-123.3-43.8-228.8-131.5-316.5C1586.8-72.2,1481.3-116,1358-116s-228.8,43.8-316.5,131.5  C953.8,103.2,910,208.7,910,332s43.8,228.8,131.5,316.5C1129.2,736.2,1234.7,780,1358,780s228.8-43.8,316.5-131.5  C1762.2,560.8,1806,455.3,1806,332z M2318,1164c0,34.7-12.7,64.7-38,90s-55.3,38-90,38c-36,0-66-12.7-90-38l-343-342  c-119.3,82.7-252.3,124-399,124c-95.3,0-186.5-18.5-273.5-55.5s-162-87-225-150s-113-138-150-225S654,427.3,654,332  s18.5-186.5,55.5-273.5s87-162,150-225s138-113,225-150S1262.7-372,1358-372s186.5,18.5,273.5,55.5s162,87,225,150s113,138,150,225  S2062,236.7,2062,332c0,146.7-41.3,279.7-124,399l343,343C2305.7,1098.7,2318,1128.7,2318,1164z" />
    </svg>
</div>
<!-- Include AlgoliaSearch JS Client Angular module and autocomplete.js Angular directive after jQuery and Angular dependencies -->
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.angular.min.js"></script>
<script src="https://cdn.jsdelivr.net/autocomplete.js/0/autocomplete.angular.min.js"></script>
<!-- Autocomplete angular module -->
<script>
 angular.module('myApp', ['algoliasearch', 'algolia.autocomplete'])
    .controller('yourController', ['$scope', 'algolia', function($scope, algolia) {
      var client = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
      var index = client.initIndex('YourIndex');
      $scope.getDatasets = function() {
        return {
          source: algolia.sources.hits(index, { hitsPerPage: 5 }),
          //value to be displayed in input control after user's suggestion selection
          displayKey: 'name',
          //hash of templates used when rendering dataset
          templates: {
            //'suggestion' templating function used to render a single suggestion
            suggestion: function(suggestion) {
                return '<span>' +
                    suggestion._highlightResult.name.value + '</span><span>' +
                    suggestion._highlightResult.team.value + '</span>';
            }
          }
        };
      };
    }]);
</script>
<!-- HTML Markup -->
<div class="aa-input-container" id="aa-input-container">
    <input type="search" id="aa-search-input" class="aa-input-search" placeholder="Search for players or teams..." name="search" autocomplete="off" />
    <svg class="aa-input-icon" viewBox="654 -372 1664 1664">
        <path d="M1806,332c0-123.3-43.8-228.8-131.5-316.5C1586.8-72.2,1481.3-116,1358-116s-228.8,43.8-316.5,131.5  C953.8,103.2,910,208.7,910,332s43.8,228.8,131.5,316.5C1129.2,736.2,1234.7,780,1358,780s228.8-43.8,316.5-131.5  C1762.2,560.8,1806,455.3,1806,332z M2318,1164c0,34.7-12.7,64.7-38,90s-55.3,38-90,38c-36,0-66-12.7-90-38l-343-342  c-119.3,82.7-252.3,124-399,124c-95.3,0-186.5-18.5-273.5-55.5s-162-87-225-150s-113-138-150-225S654,427.3,654,332  s18.5-186.5,55.5-273.5s87-162,150-225s138-113,225-150S1262.7-372,1358-372s186.5,18.5,273.5,55.5s162,87,225,150s113,138,150,225  S2062,236.7,2062,332c0,146.7-41.3,279.7-124,399l343,343C2305.7,1098.7,2318,1128.7,2318,1164z" />
    </svg>
</div>
<!-- Include AlgoliaSearch JS Client and autocomplete.js jQuery plugin after the jQuery dependency -->
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js"></script>
<script src="https://cdn.jsdelivr.net/autocomplete.js/0/autocomplete.jquery.min.js"></script>
<!-- Initialize autocomplete menu -->
<script>
  var client = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
  var index = client.initIndex('YourIndex');
  //initialize autocomplete on search input (ID selector must match)
  $('#aa-search-input').autocomplete(
    {hint: false}, [
    {
      source: $.fn.autocomplete.sources.hits(index, { hitsPerPage: 5 }),
      //value to be displayed in input control after user's suggestion selection
      displayKey: 'name',
      //hash of templates used when rendering dataset
      templates: {
        //'suggestion' templating function used to render a single suggestion
        suggestion: function(suggestion) {
          return '<span>' +
            suggestion._highlightResult.name.value + '</span><span>' +
            suggestion._highlightResult.team.value + '</span>';
        }
      }
    }
  ]);
</script>
.aa-input-container {
  display: inline-block;
  position: relative; }
.aa-input-search {
  width: 300px;
  border: 1px solid rgba(228, 228, 228, 0.6);
  padding: 12px 28px 12px 12px;
  box-sizing: border-box;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none; }
  .aa-input-search::-webkit-search-decoration, .aa-input-search::-webkit-search-cancel-button, .aa-input-search::-webkit-search-results-button, .aa-input-search::-webkit-search-results-decoration {
    display: none; }
.aa-input-icon {
  height: 16px;
  width: 16px;
  position: absolute;
  top: 50%;
  right: 16px;
  -webkit-transform: translateY(-50%);
          transform: translateY(-50%);
  fill: #e4e4e4; }
.aa-dropdown-menu {
  background-color: #fff;
  border: 1px solid rgba(228, 228, 228, 0.6);
  min-width: 300px;
  margin-top: 10px;
  box-sizing: border-box; }
.aa-suggestion {
  padding: 12px;
  cursor: pointer;
}
.aa-suggestion + .aa-suggestion {
    border-top: 1px solid rgba(228, 228, 228, 0.6);
}
  .aa-suggestion:hover, .aa-suggestion.aa-cursor {
    background-color: rgba(241, 241, 241, 0.35); }
@import 'https://fonts.googleapis.com/css?family=Montserrat:400,700';
.aa-input-container {
  display: inline-block;
  position: relative; }
.aa-input-search {
  width: 300px;
  padding: 12px 28px 12px 12px;
  border: 2px solid #e4e4e4;
  border-radius: 4px;
  -webkit-transition: .2s;
  transition: .2s;
  font-family: "Montserrat", sans-serif;
  box-shadow: 4px 4px 0 rgba(241, 241, 241, 0.35);
  font-size: 11px;
  box-sizing: border-box;
  color: #333;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none; }
  .aa-input-search::-webkit-search-decoration, .aa-input-search::-webkit-search-cancel-button, .aa-input-search::-webkit-search-results-button, .aa-input-search::-webkit-search-results-decoration {
    display: none; }
  .aa-input-search:focus {
    outline: 0;
    border-color: #3a96cf;
    box-shadow: 4px 4px 0 rgba(58, 150, 207, 0.1); }
.aa-input-icon {
  height: 16px;
  width: 16px;
  position: absolute;
  top: 50%;
  right: 16px;
  -webkit-transform: translateY(-50%);
          transform: translateY(-50%);
  fill: #e4e4e4; }
.aa-hint {
  color: #e4e4e4; }
.aa-dropdown-menu {
  background-color: #fff;
  border: 2px solid rgba(228, 228, 228, 0.6);
  border-top-width: 1px;
  font-family: "Montserrat", sans-serif;
  width: 300px;
  margin-top: 10px;
  box-shadow: 4px 4px 0 rgba(241, 241, 241, 0.35);
  font-size: 11px;
  border-radius: 4px;
  box-sizing: border-box; }
.aa-suggestion {
  padding: 12px;
  border-top: 1px solid rgba(228, 228, 228, 0.6);
  cursor: pointer;
  -webkit-transition: .2s;
  transition: .2s;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: justify;
      -ms-flex-pack: justify;
          justify-content: space-between;
  -webkit-box-align: center;
      -ms-flex-align: center;
          align-items: center; }
  .aa-suggestion:hover, .aa-suggestion.aa-cursor {
    background-color: rgba(241, 241, 241, 0.35); }
  .aa-suggestion > span:first-child {
    color: #333; }
  .aa-suggestion > span:last-child {
    text-transform: uppercase;
    color: #a9a9a9; }
.aa-suggestion > span:first-child em, .aa-suggestion > span:last-child em {
  font-weight: 700;
  font-style: normal;
  background-color: rgba(58, 150, 207, 0.1);
  padding: 2px 0 2px 2px; }

The “CSS - Basic” tab contains out-of-the-box structural styles. For the fully-themed version (as shown in the live demo), copy the “CSS - Themed” tab.

Options

The autocomplete.js library provides several options out of the box for further customization. These can be configured on initialize of an autocomplete menu. In the example above, setting hint: false disables the hint text that by default appears in the search input.

Tip: Setting debug: true will prevent auto-closing of the menu and make styling easier!

Highlighting Results

Technically, suggestion attributes can be accessed in the suggestion templating function at suggestion.attribute_name. However, we recommend accessing the content of suggestion attributes at suggestion._highlightResult.attribute_name.value. As a result, matched words will be wrapped with an <em> HTML tag, which can be styled to indicate to users what portion of their query matches a suggestion. Leveraging this functionality helps users better understand the relevance of the results in relation to their query.

See it in action

What’s Next

Continue building your Algolia knowledge with these tutorials:

© Algolia - Privacy Policy