Guides / Building Search UI / UI & UX patterns / Autocomplete

Building a Multi-Category Autocomplete Menu

Introduction

For multi-category autocomplete menus, a horizontal display often provides a better user experience than a vertical display. The increased size allows for more results to be displayed without the need for scrolling, which helps users more easily find information. We can also take advantage of the additional space to display images of the team logos.

Dataset

We are going to display the results from 2 datasets: player but also team information.

Different types of data should be created as different indices. This allows you to create separate ranking strategies more tailored to each type of data.

Players

1
2
3
4
5
6
7
8
9
10
11
12
13
[
  {
    "name": "Sam Young",
    "team": "Grizzlies",
    "points": 595
  },
  {
    "name": "Thaddeus Young",
    "team": "76ers",
    "points": 926
  }
  [...]
]

You can download the dataset here. Learn how to import the dataset into Algolia here

Teams

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
  {
    "name": "Hawks",
    "location": "Atlanta",
    "logoUrl": "Hawks_Atlanta.gif",
    "score": 595.5714285714286
  },
  {
    "name": "Celtics",
    "location": "Boston",
    "logoUrl": "Celtics_Boston.gif",
    "score": 428.2105263157895
  }
]

You can download the dataset here. Learn how to import the dataset into Algolia here

Configuring the indices

For both indices we specify searchable attributes and custom ranking criterion.

Players

1
2
3
4
5
6
7
8
9
$client->initIndex("players")->setSettings(array(
  "searchableAttributes" => array(
    "name",
    "team"
  ),
   "customRanking" => array(
      "desc(points)"
  )
));

For players, it makes sense to allow users to search by team name or location, as both of these attributes will be displayed. To rank the players, we have added a “points” attribute that reflects the amount of points each player has scored (the higher the scores, the better).

Teams

1
2
3
4
5
6
7
8
9
$client->initIndex("teams")->setSettings(array(
  "searchableAttributes" => array(
    "name",
    "location"
  ),
  "customRanking" => array(
    "asc(score)"
  )
));

For teams, it makes sense to allow users to search by team name or location, as both of these attributes will be displayed. To rank the teams, we have added a “score” attribute that reflects the average of the points of a team’s respective players.

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- Include stylesheet -->
<link href="app.css" rel=stylesheet />

<!-- 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 v3 and autocomplete.js library -->
<script src="https://cdn.jsdelivr.net/npm/algoliasearch@3/dist/algoliasearchLite.min.js"></script>
<script src="https://cdn.jsdelivr.net/autocomplete.js/0/autocomplete.min.js"></script>
<script src="app.js"></script>

jsDelivr is a third-party CDN. We are not able to provide support regarding third party services.

JS (app.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const client = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
const players = client.initIndex('players');
const teams = client.initIndex('teams');

autocomplete(
  '#aa-search-input',
  {
    debug: true,
    templates: {
      dropdownMenu:
        '<div class="aa-dataset-player"></div>' +
        '<div class="aa-dataset-team"></div>',
    },
  },
  [
    {
      source: autocomplete.sources.hits(players, {hitsPerPage: 7}),
      displayKey: 'name',
      name: 'player',
      templates: {
        header: '<div class="aa-suggestions-category">Players</div>',
        suggestion({_highlightResult}) {
          return `<span>${_highlightResult.name.value}</span><span>${_highlightResult.team.value}</span>`;
        },
        empty: '<div class="aa-empty">No matching players</div>',
      },
    },
    {
      source: autocomplete.sources.hits(teams, {hitsPerPage: 5}),
      displayKey: 'name',
      name: 'team',
      templates: {
        header: '<div class="aa-suggestions-category">Teams</div>',
        suggestion({logoUrl, _highlightResult}) {
          return `
            <img src="${logoUrl}">
            <div>
              <span>${_highlightResult.name.value}</span>
              <span>${_highlightResult.location.value}</span>
            </div>`;
        },
        empty: '<div class="aa-empty">No matching teams</div>',
      },
    },
  ]
);

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
.aa-input-container {
  display: inline-block;
  position: relative;
}
.aa-input-search {
  width: 300px;
  padding: 12px 28px 12px 12px;
  border: 1px solid #e4e4e4;
  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;
  pointer-events: none;
}
.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: 6px 12px;
  cursor: pointer;
}
.aa-suggestion + .aa-suggestion {
  border-top: 1px solid rgba(228, 228, 228, 0.6);
}
.aa-suggestions-category {
  border-bottom: 1px solid rgba(228, 228, 228, 0.6);
  border-top: 1px solid rgba(228, 228, 228, 0.6);
  padding: 6px 12px;
}

See it in action

Did you find this page helpful?