DIY: Implementing Autocomplete with Search

Highlighted
Shopify Partner
30 0 8

INTRO

I implemented an autocomplete search using jQuery and the jQuery UI library. You can see it here (search in the box). Basically, it leverages Shopify search, Liquid and Javascript / jQuery. This does require that you have a moderate understanding of both jQuery and liquid to make this work, but if you do it's relatively straightforward to make it happen. 

In addition you'll need CSS skills to skin the autocomplete accordingly. I won't really go over that part as it will require some customization to make it work with your website. I'm not very good with CSS and my implementation is different from the jQuery UI default because I included a link to the product's collection in the autocomplete menu which required a little tweaking. I recommend using the jQuery UI theme roller to get the basic autocomplete CSS and go from there.

You will need to include jQuery and the jQuery UI autocomplete plugin on your page. The jQuery UI Autocomplete documentation can be found here.

THE BASICS

The jQuery UI autocomplete plugin requires a data source. While it's possible to dump all your products and such into a huge JSON object in your page source, that's not too elegant. As it is a Magic the Gathering card store, there are over 21 thousand unique products (cards) on my site and over 100k variants. It would be simply infeasible to load that much data into every page. 

This is where Shopify storefront search comes in. See, Shopify uses Apache Solr to power its search. So it already has your products, collections, blog posts, and articles indexed. You can read more about it here. You can also filter the results and use wildcards. As you'll see, that will come in handy. 

THE LIQUID

Shopify has a cool feature where you can specifiy a view in the URL, and be taken to a special Liquid template corresponding to that view. In this case, you will create a template file called search.json.liquid. Here is (a somewhat stripped down for clarity version of) my search.json.liquid file:

{% layout none %}

{% capture output %}
    {% for result in search.results limit: 10 %}
        {% assign resultURL = result.url %}
        {% assign thumbURL = result.images[0] | product_img_url: 'thumb' %}

        {"value":"{% include 'json_cleanup' with result.title %}","label":"{% include 'json_cleanup' with result.title %}","url":"{% include 'json_cleanup' with resultURL %}","thumb":"{% include 'json_cleanup' with thumbURL %}","id":{{result.id}} }{% unless forloop.last %},{% endunless %}
    {% endfor %}
{% endcapture %}

{% comment %} Enclose in square brackets and output the json object {% endcomment %}
{{ output | strip_newlines | prepend: '[' | append: ']' }}


The {% layout none %} declaration will tell Shopify that only whatever is in this template will be displayed (so it's not using your theme.layout file). So, using that we can use liquid to prepare a JSON object of our results that will be returned for the 'json' search view. Now, the autocomplete plugin requires it's data in a certain way. Basically, the two required fields for each item in the JSON array are label and value. While it's possible you could just spit out {% search.results | json %} and use Javascript to later get the array in the right format for the autcomplete plugin, I think using liquid to initially pare down the search results into only what you need is much cleaner. In the code above, an array with the elements label, value, url, thumbURL and id is returned. On my site I also returned collection and collection_url but that makes the above code a lot longer to filter the correct collection to use, so for brevity it was omitted.

The 'json_cleanup' snippet you see referenced above is just a one liner that escapes slashes and whitespace in the results so that it is returned properly:

{{ json_cleanup | replace: '\\', '\\\\' | replace: '"', '\\"' | replace:  '/','\\/' }}

THE JAVASCRIPT

A simpler version of the Javascript I used is included below. Definitely look through the examples on the jQuery UI Autocomplete plugin page. The ._renderItem override below comes from the "Custom data and display" example there.

$(document).ready(function() {
	var cache = {};
	$( '#topsearch' ).autocomplete({
		source: function( request, response ) {
			if (request.term in cache) {
				response(cache[request.term]);
				return;
			}
			$.ajax({
				url: "http://www.flipsidegaming.com/search";, 
				data: {
					q: "*" + request.term + "*",
					type: "product",
					view: "json",
				},
				dataType: "json",
				success: function( data ) {
					if (data.length) {
						cache[request.term] = data;
					}
					response(data);
				}
			});
		},
		minLength: 2,
		select: function( event, ui ) {
			window.location = ui.item.url;
		},
	}).data( "ui-autocomplete" )._renderItem = function( ul, item ) {
      return $( "<li>" )
        .append( "<a><img src="' + item.thumb + '" class="autothumb" />" + item.label + "</a>" )
        .appendTo( ul );
    };

});

A few things are happening here. First, we create an empty cache array. This is going to be used to cache the autocomplete results, so that we are kind to Shopify's servers and aren't making the same searches over and over as the user changes their input. The '#topsearch' element is selected and the autocomplete() method is applied to it.

The key parameter here is source. The plugin gives you the option of providing a function which takes two arguments: the request object (basically, what the user input) and a callback function you must call with the autocomplete data. The source function is fairly simple. First, we check the cache object to see if we have an entry for what the user is searching (request.term). If we do, it will be returned, otherwise we need to make an AJAX request to the storefront search.

The $.ajax call requires a few parameters. You need to provide the URL (which is just your storefront search url) and you will need to tell it to expect json data back. You also need to pass the url parameters via the data option. Set view to be 'json' so that Shopify knows to use the special template from earlier. In my case I also set type='product' so that only products are returned by storefront search as thats the behavior I wanted. Finally, the q parameter is the Solr query string. In this case, I surrounded it with asterisk wildcards, so that a user searching for 'tes' will get results that include 'test' and 'detest' rather than results that only include 'tes' as it's own word. Experiment with these parameters to find what works best for you. The success parameter is where the resulting data is added to the cache using the request.term as the key and then the the data is passed to the response() function which will generate the autocomplete menu.

It's also worth mentioning that the select parameter is used in conjunction with the result.url we passed in the search.json object. Basically, we set the select behavior so that when the user clicks on the result in the menu, they are then linked to that item's page. Had we not included the url in the search result JSON object, we would not have been able to do this.

CONCLUSION

Apologies for the long post, but I hope that was helpful. As I said, the posted code is somewhat watered down from what I used on my site, but the crucial elements are there. You can use this technique to create a very powerful user experience and leverage liquid and storefront search. Please let me know if you have any comments, critiques or questions and I'll do my best to address them. 

3 Likes
Highlighted
Shopify Expert
6 0 0

Great writeup, and very helpful!

We just did something very similar to this, but were unaware you could add a view query to get a specific template. So we just added data- attributes to the search results markup, AJAX'd the whole search page and parsed the info out (which always felt a little dirty).

I've just changed it to use an alternate template like you did, as that's so much nicer. Thanks for the tip!

There's only one thing I'd mention, I changed the code in the search template to use:

{% paginate search.results by 10 %}
 {% capture output %}
    {% for result in search.results %}

Although search.results fetches 10 by default, this allows us to increase it to 15 in future, or reduce it to 5 and save on Shopify fetching 10 results and only returning 5 because of the for-limit.

Will - from those Clean Canvas folks
0 Likes
Highlighted
Shopify Partner
30 0 8

Thanks for the feedback Will, I'm glad you found it useful. The view parameter is a very useful thing indeed and it has come in handy for me before. It's nice to just make a 'json' view for each liquid drop template (collection, product metafields, etc) so that you can get json for that stuff via ajax elsewhere on the site. So for example, to have a 'product.metajson.liquid' template, and in that its just {{ product.metafields | json }} below a {% layout none %} tag. Very clean.

I did make the paginate change you suggested, as you are right it makes a lot more sense to limit the result quantity via paginate rather than the for loop so liquid is always loading the desired number. Thanks again for the feedback!

0 Likes
Highlighted
Tourist
58 0 3

I got this working on my site and its awesome.. I have a bug I am still trying to work out. I notice it is affecting your site also.

  1. When searching on an Ipad the search autocomplete is not lined up properly they are staggered. (I havent figured out how to fix that yet)

http://www.mypleasurebox.com/

If you have any fix for this let me know.

Thank You

Thank You, Lora http://www.MyPleasureBox.com
0 Likes
Highlighted
Shopify Partner
13 0 0

Thanks for the great tutorial Charlie. I am have implementing this to my store: http://tinyurl.com/l8mefpv password: leudre.

But the only way I can get the auto complete show the results is if I for example type "top" then press space followed by backspace. I think I have the input labelled incorrectly?

Thanks

 

0 Likes
Highlighted
Shopify Partner
17 0 6

Quick correction to the JavaScript on the ajax url line (has ; causing error). Also made it agnostic to the store domain:

 

$(document).ready(function() {
	var cache = {};
	$( '#topsearch' ).autocomplete({
		source: function( request, response ) {
			if (request.term in cache) {
				response(cache[request.term]);
				return;
			}
			$.ajax({
				url: "{{ shop.url }}/search", 
				data: {
					q: "*" + request.term + "*",
					type: "product",
					view: "json",
				},
				dataType: "json",
				success: function( data ) {
					if (data.length) {
						cache[request.term] = data;
					}
					response(data);
				}
			});
		},
		minLength: 2,
		select: function( event, ui ) {
			window.location = ui.item.url;
		},
	}).data( "ui-autocomplete" )._renderItem = function( ul, item ) {
      return $( "<li>" )
        .append( "<a><img src="' + item.thumb + '" class="autothumb" />" + item.label + "</a>" )
        .appendTo( ul );
    };

});

 

Andrew @ Rehash
0 Likes
Highlighted
Shopify Partner
7 0 0

Hi 

Thanks for the post. I want to search for product title only and will be able to search SKU or Bar Code also. 

If I change the q parameter from

q: "*" + request.term + "*",

to

q: "*" + title:request.term + "*",

it works as expected for the product title only, but I am unable to search neither SKU nor Bar Code.

How can I achieve this? Please help if anyone can.

Thanks in advance.

0 Likes
Highlighted
Tourist
14 0 3

Hi Y'all:

First, many many thanks to Charlie for putting together this great tutorial.  I studied it in great depth and got it to work months ago (woo hoo!).  A little tip to anyone having trouble with search results not returning: try removing www from the URL and use just http://yourstorename.com.

I wanted some extra autocomplete features and enlisted the aid of a Shopify Guru:  her Awesomeness, Caroline Schnapp.  I was so flattered that my inquiry / wishes actually inspired her, and she created an amazingly easy and efficient tutorial on how to create an autocomplete function for your store.  The tutorial can be found here: http://docs.shopify.com/support/configuration/store-customization/can-i-add-autocomplete-to-my-store....

She even included some css in the search template, which I moved to my stylesheet to keep all the css in one place.

Her code / solution gives me search results for product SKUs.  I'm assuming this would work for you, Bikshapathi Gorantla, assuming the SKUs / UPC codes are included on the products' pages.

Hope this helps, and many thanks again to Caroline for her assistance and genius-ness.

Best,

Oink!

1 Like
Highlighted
New Member
4 0 0

Has anyone figured out how to change this so the "Clicking outside makes the results disappear" code only gets applied to the original page where the search is preformed, but not effect the search results page?

0 Likes
Highlighted
Shopify Partner
4 0 1

Hi Jon,

We recently released an Autocomplete app for Shopify with very affordable pricing. You can find it here: https://apps.shopify.com/findify-autocomplete

You can try it for free for 7 days, we'd love to hear your feedback!

Cheers,

Meni

0 Likes