Guided search
On this page
Facets are a great way to let your users refine their search. But sometimes, there are so many facets that it can be hard to find the right ones. One way to solve this problem is to show the most popular facets at the top of the page.
In this image, from the live demo, a set of guided search facets appear near the search box. For example, if a user is looking for a computer, clicking Computers & Tablets will update the results on the page and the facet filters to stay in context with the scope of the query. In this case, the brands facet updates to show relevant brands such as HP and Apple. This helps users to refine and improve the relevance of their results. The order of the facet values is based on count but you can adjust it to alphabetical or any custom order that makes sense for your organization.
Before you begin
This tutorial requires InstantSearch.js.
Implementation guide
In this guide, you build a classic ecommerce search page with a search box, guided search facets at the top, two facets (brand and price), and results.
High-level approach
To create guided search facets, you need to create a UI for the refinementList
widget using connectors. Essentially, you override the out-of-the-box behavior of a refinement list widget with your own render function. You can find the code examples in the guide on the custom refinement list connector.
Before you get started, make sure you:
- Select an attribute as the guided facet filter and set it as one of the
attributesForFaceting
. - Set up your UI with the InstantSearch getting started guide.
File and index structure
For this implementation, you need two files:
index.html
main.js
You also need an Algolia index containing the ecommerce dataset. For this guide, name the index instant_search_solutions_ecommerce
.
It’s a three-step process (this code is located in main.js
):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. Create a render function
const renderRefinementList = (renderOptions, isFirstRender) => {
// Rendering logic
};
// 2. Create the custom widget
const customRefinementList = instantsearch.connectors.connectRefinementList(
renderRefinementList
);
// 3. Instantiate
search.addWidgets([
customRefinementList({
// instance params
}),
]);
Create a render function
The rendering function is called before the query and each time results come back from Algolia. The function in main.js
is populated with rendering options. This code creates the look and feel of the category’s facet values and defines the click-event behavior. It also manages the “show more” logic.
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
48
49
50
51
52
53
54
55
56
57
// 1. Create a render function
const renderRefinementList = (renderOptions, isFirstRender) => {
const {
items,
refine,
createURL,
isShowingMore,
toggleShowMore,
widgetParams,
} = renderOptions;
//Do some initial rendering and bind events
if (isFirstRender) {
const ul = document.createElement('ul');
const button = document.createElement('button');
button.classList.add('btn-showMore')
button.textContent = 'Show more';
button.addEventListener('click', () => {
toggleShowMore();
});
widgetParams.container.appendChild(ul);
widgetParams.container.appendChild(button);
}
//Render the widget
widgetParams.container.querySelector('ul').innerHTML = items
.map(
item => `
<li style="${isRefined(item)}">
<a
href="${createURL(item.value)}"
data-value="${item.value}"
>
${item.label} (${item.count})
</a>
</li>
`
)
.join('');
[...widgetParams.container.querySelectorAll('a')].forEach(element => {
element.addEventListener('click', event => {
event.preventDefault();
refine(event.currentTarget.dataset.value);
});
});
const button = widgetParams.container.querySelector('button');
button.textContent = isShowingMore ? 'Show less' : 'Show more';
};
function isRefined(item) {
if (item.isRefined) {
return 'font-weight: bold; background-color: rgba(83,101,252, 0.5)'
}
}
Create the custom refinementList
connector
1
2
3
4
// 2. Create the custom widget
const customRefinementList = instantsearch.connectors.connectRefinementList(
renderRefinementList
);
Instantiate the custom refinementList
connector
Now add the new customized connector to your collection of widgets also in main.js
. You give it the container used by the HTML, as described in the next section. In the example, the container ID is #guided-search-facets
.
1
2
3
4
5
6
7
8
// 3. Instantiate
search.addWidgets([
customRefinementList({
container: document.querySelector('#guided-search-facets'),
attribute: 'categories',
showMoreLimit: 40,
})
]);
Update the HTML
The last step is to add the widget to your index.html
. Here it’s positioned just under the search box.
1
2
<div id="searchbox" class="searchbox"></div>
<div id="guided-search-facets" class="guided-search-facets"></div>