{{craftSnippets}}
Home Articles Plugins Starter theme Components on Github Gists About
  • Home
  • Articles
  • Search autocomplete component for Craft CMS
Posted on Mar 24, 2019 by Piotr Pogorzelski
Updated on Apr 06, 2019

Search autocomplete component for Craft CMS

Search autocomplete component on github gists
Basic search autocomplete component, appending search hints to input. It works out of the box and can be dropped into any Craft project.
Table of contents:
  • Component overview
  • Component structure
  • Element API endpoint
  • Frontend side of things
  • jQuery autocomplete
  • Styling
  • More advanced solution

Component overview #

Our search autocomplete component is pretty uncomplicated. This however, makes it universal and easy to use in any Craft project. Based on typed in text, search hints will be created and appended to search input. These hints are composed of text representing suggested entry, with a phrase that user initially typed highlighted.

Here's an example of search form with search hints displayed:

Component structure #

Let's analyze how autocomplete functionality that we are building works, step by step.

  • User starts typing into search input.
  • JS code performs AJAX query to backend endpoint. Query carries phrase that user started typing in.
  • Based on the phrase, endpoint performs search in database and returns results.
  • JS code renders search hints from received results and appends them to search input.
  • After clicking on one of the hints, action can be performed - for example, visiting URL of clicked element.

For our endpoint, we will use Element API plugin. It will return results in JSON format. It's first party plugin (maintained by Craft team), so we can be 100% sure that it will seamlessly work with Craft and there won't be any problems with its maintenance.

For frontend code - making AJAX requests and rendering search hints - we will use jQuery autocomplete. It simple library, doing just what we need it to do.

Element API endpoint #

First, we need to create endpoint using Element API. We need to define it in config/element-api.php file.

<?php
use craft\elements\Entry;

return [
    'endpoints' => [
        'search.json' => function() {

        // settings
        $section_handle = 'articles';
        $phrase = Craft::$app->request->getParam('query');

        $criteria = [
            'section' => $section_handle, 
            'search' => 'title:'.$phrase,
        ];
      
            return [
                'elementType' => Entry::class,
                'criteria' => $criteria,
                'paginate' => false,
                'transformer' => function(craft\elements\Entry $entry) {

                return [
                    'title' => $entry->title,
                    'url' => $entry->url,
                ];
            },

            ];
        },
    ]
];

Let's briefly analyze endpoint structure.

search.json is endpoint name. Endpoint itself will be available under address that will look like yourdomain.com/search.json.

$criteria is an array that contains parameters of our database query. It uses same ElementCriteriaModel rules that are used for creating queries in Twig templates.

In our case, we are stating that we want to get elements from section with "articles" handle (change it to whatever section you need), and search through them using string received from GET parameter named query. If we wanted to perform such query in template it would look like this:

{{ craft.entries.section('articles').search(craft.app.request.getParam('query')) }}

In return part of endpoint, we define a couple of important paramaters.

We set paginate to false - jQuery autocomplete is unable to handle paginated results. elementType must be set to Entry::class - note that while setting up $criteria, we didn't specify if query will be performed through entries, categories or another element - we do it there.

Finally, there is transformer. It defines how single object in returned data is structured. In our case, we just return title and url of entries. This is how returned JSON data will look like:

{  
   "suggestions":[  
      {  
         "title":"Blog post 1",
         "url":"http://domain.com/blogpost1"
      },
      {  
         "title":"Blog post 2",
         "url":"http://domain.com/blogpost2"
      },
      {  
         "title":"Blog post 3",
         "url":"http://domain.com/blogpost3"
      },      
   ]
}

Frontend side of things #

Now that we set up endpoint, we need to write some javascript code that will communicate with it. First, let's add jQuery autocomplete files to our website (along with jQuery library itself of course). After that, we just need to set up our search form.

<form action="">
    <div class="js-search-wrapper">
        <input type="text" class="js-search-input" name="search">
    </div>
</form>

{% js %}
$('.js-search-input').autocomplete({
    serviceUrl: '{{url('search.json')}}',
    appendTo: $('.js-search-wrapper'),
    onSelect: function (suggestion) {
        window.location.href = suggestion.url
    },
    transformResult: function(response){
        return {
            suggestions: $.map(JSON.parse(response).data, function(dataItem) {
                return { value: dataItem.title, url: dataItem.url };
            })
        };
    }
  });
{% endjs %}

The first thing worth noting is that I have written JS code inline - within Craft {% js %} tag. This is vue-like approach - I put my JS code right next to HTML and Twig, structuring my code like vue single file components. Thanks to this approach, my Twig components are highly self-contained and can be easily transfered to other projects.

Another advantage of such approach is that I can actually use Twig variables and functions within JS code - in example above I used Craft url() Twig function in serviceUrl JS parameter. So, '{{url('search.json')}}' was rendered as 'http://mydomain.com/search.json'.

jQuery autocomplete #

We create autocomplete instance by attaching it to search input. Autocomplete requires two basic settings - serviceUrl that defines URL of endpoint and appendTo that defines DOM element that search hints will be appended to.

Before jQuery autocomplete can consume data returned from endpoint, we need to transform it into the proper format using transformResult event. jQuery autocomplete requires key of results object to be called suggestions while element API by default returns data inside object called data. Name of single element needs to be called value instead of title - this can be fixed using jQuery $.map method.

By default, clicking on search hint will just put suggested text (its value attribute) into input. I modified this behavior using onSelect event. Clicking on hint will actually load page associated with it. That's why each object returned by endpoint has url parameter - it is not required by plugin itself, but we needed it to make hints behave like links.

Styling #

jQuery autocomplete comes with its own basic CSS stylesheet. I modified it a little, stripping it to a bare minimum and changing pixels to rems.

.autocomplete-suggestions {
    box-sizing: border-box;
    border: 1px solid #999;
    background: #FFF;
    cursor: default;
    overflow: auto;
}

.autocomplete-suggestion {
    padding: 0.1rem 0.3rem;
    white-space: nowrap;
    overflow: hidden;
}

.autocomplete-no-suggestion {
    padding: 0.1rem 0.3rem;
}

.autocomplete-selected {
    background: #F0F0F0;
}

.autocomplete-suggestions strong {
    font-weight: bold;
    color: #000;
}

.autocomplete-group {
    padding: 0.1rem 0.3rem;
    font-weight: bold;
    font-size: 1rem;
    color: #000;
    display: block;
    border-bottom: 1px solid #000;
}

More advanced solution #

If you want to make your search autocomplete more advanced (for example - by displaying thumbnails along with entry titles) you will need to write frontend code yourself, instead of using jQuery plugin.

I urge you to visit this article on nystudio blog. It also deals with search autocomplete functionality based on Element API, but compared to my fairly simple solution, nystudio's one is more advanced - it uses Vue JS framework to manually render search hints.


TAGS:
#twig component #javascript
If you want to get latest updates on Craft CMS tutorials and components, follow me on Twitter or subscribe to RSS feed.
Quick links for this article:
Search autocomplete component on github gists
Articles on blog:
  • Frontend testing for Craft CMS websites with Codeception and Cypress
  • Building reactive Craft Commerce product page with Sprig plugin
  • Dynamically generated PDF attachments for Freeform submissions
  • Using template hooks in Craft CMS
  • Alpine JS modal component for Craft CMS
  • Using template UI elements to extend Craft CMS control panel
  • Matrix within a Matrix - possible solutions for Craft CMS
  • Universal email template for Craft CMS
  • Creating attributes table from entry fields in Craft CMS
  • Namespacing forms in Craft CMS
  • Creating map-based navigation for Craft CMS
  • Placeholder image macro for Craft CMS
  • Building AJAX contact form with Craft CMS
  • Using incognito field plugin for Craft CMS
  • Email footer creator made with Craft CMS
  • Infinite scrolling and lazy loading with Craft CMS
  • Using Javascript in Twig templates with Craft CMS
  • Twig templating tips and tricks for Craft CMS
  • Basic SEO functionality for Craft CMS
  • Working with dates in Craft CMS templates
  • Working with SVG images in Craft CMS templates
  • Responsive and lazy-loaded youtube videos with Craft CMS
  • Debugging and inspecting Twig templates in Craft CMS
  • Creating article excerpts with Twig component in Craft CMS
  • Adding favicons to Craft CMS website
  • Truncating text with Twig macros in Craft CMS
  • Universal language switcher for Craft CMS
  • Read time macro for Craft CMS
  • Using attr() function to render HTML attributes in Craft CMS
  • Building dynamic, AJAX based pagination for Craft CMS
  • How to add Disqus comments to Craft CMS website
  • Ellipsis pagination component for Craft CMS
  • Converting email addresses into links using Twig macro
  • Breadcrumb created from URL for Craft CMS
  • Best developer-oriented Craft CMS plugins
  • Search autocomplete component for Craft CMS
  • RSS feed - template component for Craft CMS
  • Testing emails sent by Craft CMS using Mailtrap
  • Quick edit link - Twig component for Craft CMS
  • Filtering entries in control panel using Searchit plugin
  • Fetching routes into Twig templates in Craft CMS


Frontend testing for Craft CMS websites with Codeception and Cypress

Building reactive Craft Commerce product page with Sprig plugin

Dynamically generated PDF attachments for Freeform submissions

Using template hooks in Craft CMS

Alpine JS modal component for Craft CMS

Using template UI elements to extend Craft CMS control panel

Matrix within a Matrix - possible solutions for Craft CMS

Universal email template for Craft CMS

Copyright ©2023 Piotr Pogorzelski