Drupal
August 8, 2022

Anirudh Mahant

A Step-by-Step Tutorial to Customize Drupal SearchAPI Facets without AJAX

Drupal SearchAPI Facets is a powerful module for Drupal 8 that allows you to create a dynamic, faceted search with just a few clicks. In this tutorial, we will show you how to customize Drupal SearchAPI Facets without AJAX. It’s also going to guide readers through the process of customizing Drupal’s SearchAPI facets and will introduce some new techniques (hacks) for tweaking Facet items through Drupal/Facets API, Twig templating, and JavaScript.

We will start by exploring the default Drupal SearchAPI Facets configuration and behavior, then go over how to customize the facets, and finally, we will explore some absorbing ways of customizing it through JavaScript.

How to create SearchAPI Facets in 5 Minutes

Drupal SearchAPI Facets are used to provide search results in the form of facets. Some of the most common reasons for using faceted search are:

  • It can be used to segment results into different categories, such as price and availability.
  • It can be used to find specific items within a category or subcategory.
  • It can be used for filtering content by date, author, or other criteria.
  • It can also be used for sorting and ranking content based on popularity and relevance.

Offering a rich array of options, a Faceted search can enhance the entire user search experience and is a great way to let users narrow down their search results. For the sake of simplicity, we’ve set up our instance to use the default Database search provider service in Search API. For production sites, Apache Solr is recommended. Beginning with:

SearchAPI Server and Search Index
SearchAPI Server and Search Index
Select Default Database as Backend Provider
Select Default Database as Backend Provider
Configure Search Indexing for Bundles (Entities/Nodes)
Configure Search Indexing for Bundles (Entities/Nodes)
Add Fields for Search Indexing
Add Fields for Search Indexing

Hop over to the View tab and manually build the index if it’s still pending. The SearchAPI is now ready to accept Faceted search which we will configure on the Facets configuration page. Before that, there is one more missing piece of the puzzle - A View that works on the SearchAPI Index. A View acts as a proxy between exposed Facets and fetching search results from the SearchAPI index.

Facet Configuration Page
Facet Configuration Page
Facet Settings
Facet Settings
Configure Facet Field Settings
Configure Facet Field Settings
View on SearchAPI Index
View on SearchAPI Index

Customizing the Drupal SearchAPI Facets with JavaScript

Informally this is quite the opposite of what SearchAPI and FacetAPI offer out-of-the-box. A Google search on “AJAX with Facets”, or “Post request with Facet” is sure to surprise you. You’ll find dozens of active discussions among Drupal developers to implement AJAX in Facets.

Incidentally, FacetAPI defaults to a URL processor that works over GET requests, which makes a lot of sense when building progressive search results ie. one facet is always dependent on the other. The limitations of “GET” requests (8 KB approx), and the complexity of AJAX POST requests coil up these feature requests ending up with numerous patches which are risky and not production-ready yet.

These are a few of the feature implementations that were made possible:

  1. Prevent page reload on Facet selection (requires storage on the client-side = sessionStorage)
  2. Maintain a stack of selected and removed facets.
  3. Allow users to select multiple facets and submit them simultaneously (risky and unpredictable).
  4. Clear out storage (sessionStorage) for each Facet, or all of them altogether.

Making changes to default Drupal JavaScript behavior is next. This was done with a bit of JavaScript as the Facets module binds a JavaScript event on facet items on click and on change events (depending on which widget type you select). To unbind this jQuery event we came up with:

FYI Reynolds is the namespace under which we’re wrapping up all the functions, yeah, there could be another way of putting this using Drupal.behaviors which is the right way to do it. Anyhow, it works, and there’s more explaining of what each of the functions would do. So let’s dive into them:

  1. Reynolds.facetStorageSetOrCreate(); creates an empty storage or returns it
  2. Reynolds.findFacetByKey(facetKey) would find a facet by its key or id
  3. Reynolds.removeFacetByKey(facetKey); would remove a facet from storage
  4. _.set(Reynolds.facetsData, facetKey, facetIdent); will merge storage entries JSON object using underscore.js

For this to work, you’ll also need a Facet hook function as below in your .theme file:

This additional step just adds some data attributes for our reference in JavaScript. You might have noticed some key functions that are at play:

These helper functions control the click behavior with additional checks like does a facet exists or does the sessionStorage exists, and consequently the removal of a facet item based on its key. Now comes the submission part - as we’ve maintained the selections in the sessionStorage JSON object it’s relatively easy to form a submit query:

Finally, this little snippet gathers all the selected facets, converts them into proper valid facet query parameters, and redirects to the current path with query parameters. To simplify just call the Reynolds.hookFacetSubmit(); function on the page ready or window load event and you’re all set.

What about Facet summaries?

Most of the time, you’ll need to show your selected facets as summaries and/or allow users to remove them for convenience. This will also require some JavaScript, which is easier with the functionality that is already implemented. All you need is to handle the click event on summaries as below:

And call the function Reynolds.hookFacetSummaryRemoval(); on page load or window load event. It’s not the most robust or comprehensive solution, yet gets you closer to customizing facets without using AJAX. On an endnote, you’re not restricted to this approach, it might strike as a starting point for a full AJAX implementation. A live implementation can be found here:

https://www.reynoldsbrands.com/recipes

Useful Resources:

Related Insights

Subscribe to our newsletter