Templating

Templating can be used to define a custom HTML structure for your search suggestions and results. This allows a virtually seamless integration of the Site Search 360 JS plugin into your site's theme and makes the layout customization even more flexible.

Defining custom templates will only override the search suggestion/result markup without having an impact on most of the plugin settings (e.g. grid layout or tabbed navigation).

Configuration

To configure templating you need to add a configuration object to the suggestions (suggestTemplate property) or results (resultTemplate property) block of your ss360Config.

The configuration object for search suggestions and search results has the same structure and looks as follows:

{
	template: "<span>My custom template</span>",
	preRenderCallback: function(suggest){ console.log("Rendering:", suggest); },
	templateBuiltCallback: function(markup) { console.log("Rendered:", markup); },
	postRenderCallback: function(node, suggest) { 
		node.addEventListener("click", function(e) { 
			console.log("Clicked:", suggest); 
		}); 
	}
}
Where:
  • template is the HTML template string.
  • preRenderCallback is a callback called before the result is built.
  • templateBuiltCallback is a callback called after the result HTML is built but before it was converted to a DOM Node.
  • postRenderCallback is a callback after the DOM Node has been created.

Check the Callbacks section for more information.

Please note: to use custom search result templates, you need to run at least version 12.3.1 of the plugin, and to use custom search suggestion templates - version 13.1.1 and later.

Templating rules

To fill in your HTML template with content, you can use syntax similar to the Mustache templating engine. The render context is always a single search result/suggestion with the following properties:

  • name - the result title.
  • image - the result image url.
  • link - the result url.
  • content - the search result snippet (this property is not set for search suggestions).
  • dataPoints - an array of data points, each data point has the following structure:
    {"key": "Data Point Name", "value": "Data Point Value", "show": true}
  • dataPointHash - an object mapping data point keys (camelcase) to an array of data point values.

Injecting content

To inject content into the HTML template you can use the {{variableName}} rule. This will lead to all special HTML characters to be escaped. If you do want to inject the HTML content into your template without escaping special characters, you can use the {{{variableName}}} rule.

Undefined properties will be replaced with an empty string.

Let's take a look at a simple HTML template:
<div class="result">
   <h3 class="result__title">{{name}}</h3>
   <a class="result__link" href="{{link}}">See more</a>
   <p class="result__snippet">{{{content}}}</p>
</div>

Loops

If the target property is an array, you can use loops (the {{#array}}{{item}}{{/array}} rule) to iterate over every item, which will become the new render context. This can be useful for displaying all data point values:

<ul class="result__datapoints">
	{{#dataPoints}}
		<li class="result__datapoint"><strong>{{key}}:</strong> {{value}}</li>
	{{/dataPoints}}
</ul>

Conditional statements

To render a specific block under a certain condition, you can use the {{#condition}}I am true{{/condition}}> rule. The condition evaluates to false if it is false, undefined, null, or an empty array. To invert conditions, you can use the {{!#condition}}I am false{{/condition}} rule.

Conditional statements can also be inlined directly into an HTML tag:
<span class="{{# condition : "condition__true" #}}">Test</span>,
or
<span class="{{!# condition : "condition__false" #!}}">Test</span>

Putting it all together:
<div class="result {{# image : "result--has-img" #}}">
	<h2>{{name}}</h2>
	<p>
		{{#link}}
			<a href="{{link}}">Here you go</a>
		{{/link}}
		{{!#link}}
			Sorry, we can't take you to the search result.
		{{/link}}
	</p>
</div>

Handling array and object properties

To inject content from an array you can directly query a specific index {{array[0]}} (0 based), specific values from object properties can be injected by using {{object.myProperty}}. Both can also be combined to query an array value of an object, e.g. {{dataPointHash.price[0]}}.

Callbacks

You can use one of the three available callbacks to customize your templates even further.

Prerender callback

The preRenderCallback is called before the template string is processed and it receives three arguments:

  • suggest - (Object) the full search result/suggestion object, can be modified to set custom properties for the content interpolation.
  • globalStore - (Object) the store that's shared across all search results/suggestions, can be used to set global variables, e.g. result counter.
  • contentGroup - (String) the content group name.

Template built callback

The templateBuiltCallback is called after the template is built (all templating rules were applied). The callback receives four arguments:

  • template - (String) the string template.
  • suggest - (Object) the full search result/suggestion object.
  • globalStore - (Object) the store that's shared across all search results/suggestions, can be used to set global variables, e.g. result counter.
  • contentGroup - (String) the content group name.

This can be useful to do more complex replacements on the template string:

function(template, suggest, globalStore, contentGroup) {
	var price = "";
	if (contentGroup === "Products") {
		price = (suggest.dataPointHash.price || {})[0] || 0;
		price = "$" + Math.round(price);
	}					
	template = template.replace(/#PRICE#/g, price);
	return template;
}

Postrender callback

The postRenderCallback is called after the DOM node was created. It receives 4 arguments:

  • node - (Node) the DOM node.
  • suggest - (Object) the full search result/suggestion object.
  • globalStore - (Object) the store that's shared across all search results/suggestions, can be used to set global variables, e.g. result counter.
  • contentGroup - (String) the content group name.

This can be useful to bind custom events on search results/suggestions:

function(node, suggest, globalStore, contentGroup) {
	node.addEventListener("click", function() {
		MyGlobalTracker.trackResultClick(suggest);
	});
}
Tips & tricks

Suggestion highlighting

The default highlighting of search suggestions (on hover, or keyboard select) won't work for custom templates.

Add the class unibox__highlight-container to the content you want to highlight on selection and it will automatically receive a grey background when selected.

Alternatively you can use custom CSS to target a selected suggestion by using the following CSS selector: .unibox__selectable--active, .unibox__selectable:hover.

Long HTML templates

As the ss360Config needs to be a valid JavaScript object, long templates might be difficult to modify. If you don't use string literars and a JS compiler to ensure cross-browser compatibility, you can also put the full template into a <template> tag and read it directly from the DOM:

<!-- ... -->
<template id="ss360-result-template" style="display: none;">
	<div class="my-search-result">
		<!-- Template Code -->
	</div>
</template>
<script>
	var ss360Config = {
		/* ... configuration ... */
		results: {
			/* ... result configuration ...*/
			resultTemplate: {
				template: document.getElementById("ss360-result-template").innerText;
			}
		}
	}
</script>
<!-- ... -->