This document describes all the options you can use to configure the Doofinder Embedded layer in your website.
Basics
The Embedded Layer is a special type of layer that is inserted into the page and offers autocompletion as a part of the page instead of an external layer.
You specify a place where it’s inserted (see insertionPoint) and, when the user searches, the layer replaces the contents of the insertion point and provides a search interface with results and filters (if any facets defined).
The Options Array
The layer script is in charge of requiring the Javascript code to run the layer from our CDN and configuring the layer or layers described in the options array.
In your script you can see something like this:
var dfEmbeddedLayers = [{
hashid: '1955a94db43999fcc211f925fc85f12c',
zone: 'eu1',
queryInput: '#query'
}];
dfEmbeddedLayers
is the name we gave to the Javascript array (a list) which contains all the layer descriptions for this type of layer.
Each layer description is a Javascript object (a key-value dictionary) with option names as keys and the corresponding values assigned to them.
This means that you could have more than one layer in your website:
var dfEmbeddedLayers = [
{
hashid: '1955a94db43999fcc211f925fc85f12c',
zone: 'eu1',
queryInput: '#query'
},
{
hashid: '43999fcc85f12c1955a94dbc211f925f',
zone: 'eu1',
queryInput: '#query2'
}
];
To customize your layer(s) you will have to add more keys to the proper layer description, taking care of commas, quotation marks for text, curly braces…:
var dfEmbeddedLayers = [
{
hashid: '1955a94db43999fcc211f925fc85f12c',
zone: 'eu1',
queryInput: '#query', // we've added a comma
searchParams: { // to add a new option
rpp: 50, // this also needs a comma
transformer: null // but no comma here…
} // nor here…
},
{
hashid: '43999fcc85f12c1955a94dbc211f925f',
zone: 'eu1',
queryInput: '#query2' // no comma in the last option
}
];
In order to make things simpler, we will represent single layer options in the rest of the document by omitting the dfEmbeddedLayers
array so you’ll see only the layer options:
{
hashid: '1955a94db43999fcc211f925fc85f12c',
zone: 'eu1',
queryInput: '#query'
}
Mandatory options
hashid
Identifies your search engine in our servers. You will find all possible hashid
values in the list of search engines in the Doofinder Admin.
{
hashid: 'ce2c1ex5369d3r28419bzdee7bcfaa5c'
}
zone
Geographical location assigned to your search engine. All search engines in your account have the same location.
Current possible values are:
eu1
us1
This value is automatically added by the layer installer in the Doofinder Admin.
{
zone: 'eu1'
}
queryInput
The CSS selector of the text box used to search on your website. The best value for this option is a CSS id, which should be unique and only used once in the HTML of your page.
The text box usually is an HTML <input
> tag of type text
or search
.
<input type="text" id="query_input_id">
{
queryInput: '#query_input_id' // a unique CSS id
}
NOTICE: If you use too generic selectors the layer will attach to all matching inputs.
Use generic selectors when there’s no other way to match your text box or if you want to explicitly attach the layer to more than one text box.
{
queryInput: 'input[type="search"]' // generic selector
}
General Options
autoHideVirtualKeyboard
This option is experimental and is disabled by default. You can configure a delay in ms to auto-hide Android’s virtual keyboard when the user stops typing.
The recommended value in case you want to use this feature would be an integer greater or equal than 2000
(2s).
{
autoHideVirtualKeyboard: 2000
}
WARNING: This option may cause important usability concerns. Use it at your own risk.
callbacks
Callbacks are functions that will be called at the end of certain actions or events. They are useful to change or add some extra behavior to the layer.
The callbacks
option is an object (dictionary) which contains functions as values for specific keys.
resultsReceived
This callback will be called each time a search response is received.
{
callbacks: {
/**
* @param {Object} response Object that contains the search response.
* Use your browser dev tools to inspect it.
*/
resultsReceived: function(response) {
console.log(response);
}
}
}
hit
This function will be called each time a search result is clicked.
{
callbacks: {
/**
* @param {Node} item The DOM node that represents the item.
*/
hit: function(item) {
// item.dfClicked `true` if the item was already clicked
// item.getAttribute('href') returns the URL if the item is a link
}
}
}
loaded
This function will be called when the layer is loaded. Use it to configure any additional behavior that requires that the layer exists in the page.
{
callbacks: {
/**
* @param {Object} instance that holds the layer and its options.
*/
loaded: function(instance) {
// instance.layerOptions can be changed
// instance.configure() re-configures the layer
//
// instance.layer the layer instance itself
// instance.layer.layer the layer in the DOM
}
}
}
captureForm
Captures the submit
event of the form that contains the search box. This is useful if you have any kind of icons that submit the search form; this way you can add them the same behavior as in the searchbox when you press the ENTER
key.
This option is disabled by default.
WARNING: If your page uses a single form as entry point to the whole application (usually ASP forms), don’t enable this option or the behavior of the page will break.
{
redirections: {
captureForm: false // set to `true` to enable
}
}
If no parent form relative to the searchbox is found, the layer will fall back to the default behavior (capturing the ENTER
key press).
googleAnalytics
The layer can send search events to your Google Analytics account. This option manages how search-related events are sent or if this feature must be disabled.
This option may take different forms:
- Boolean: The easy way. Just
true
orfalse
.true
: This is the default value. The layer searches for any available Google Analytics tracker in your site and uses it to send search events. If you have more than one tracker in your site or you use a custom tracker name that can’t be detected by the layer, this is not the way to go.false
: If you want to disable Google Analytics for the layer then use this.
- String: This way you can specify a Google Analytics Account ID or a custom tracker name.
- Account ID: A Google Analytics account id, like UA-XXXX-. An ad-hoc tracker will be created to send events to the specified account.
- Custom Tracker Name: This is useful when you want to use a specific tracker because there is more than one Google Analytics tracker in the page.
- Options Object: This gives more control on the way the layer works with Google Analytics. Currently you may set any of two options:
account
: This accepts a Google Analytics Account ID or a tracker name. See String above.trackPageView
: A boolean value,false
by default. Iftrue
then the layer will send search events as page views with aquery
parameter with the search terms so you can [configure Site Search]. This is independent of the events being tracked by default.
// Boolean
{
googleAnalytics: true // or false
}
// String: Account ID
{
googleAnalytics: 'UA-XXXXXXX-1'
}
// String: Tracker Name
{
googleAnalytics: 'myTracker'
}
// Object
{
googleAnalytics: {
account: 'UA-XXXXXXX-1',
trackPageView: true
}
}
To learn more about how Google Analytics works in Doofinder layers take a look at this article.
mainContainerId
You can define the prefix of the id
attribute of the layer. When multiple layers of the same type are in the same page, a sequential number is added to the default prefix for the second, third and so on.
If you want a different prefix for your layer you can set it yourself:
{
mainContainerId: 'my-first-layer'
}
redirections
Search results contain any redirection defined for the current search terms. If the user presses the ENTER
key in the search box, she will be redirected to the URL specified from the response.
To disable redirections just set this option to false
:
{
redirections: false
}
If your searchbox is located inside a form you can configure redirections to capture the form’s submit
event. Continue reading.
searchParams
This option is an object (dictionary) of parameters and values to be sent in the query string of each search request.
{
searchParams: {
paramName: 'paramValue'
}
}
All valid parameters can be found in the [Search API] reference but you will find the most common in the [Advanced Concepts] section of this document.
showInMobile
Instructs doofinder whether to force the desktop version of the layer in mobile phones.
This option is disabled by default.
{
showInMobile: false // set to `true` to enable
}
WARNING: You need the mobile add-on to use Doofinder in mobile devices.
typingStopDelay
Use this option to configure the delay in ms between when the user stops typing and the event is triggered. The default value is 1000
(1s).
It is strongly recommended not to use a value less than 1000
(1s).
{
typingStopDelay: 1000
}
WARNING: This option may cause important usability concerns. Use it at your own risk.
urlHash
Optionally, the layer may store its status in the hash part of the URL. This behavior is disabled by default but, if you want to enable it, you can set this option to true
.
This option is disabled by default.
{
urlHash: false // set to `true` to enable
}
Display Options
Internationalization
lang
Language to use in the layer. The layer includes translations for some languages.
The default value for this option is en
.
{
display: {
lang: 'en' // en, es, de, fr, it
}
}
translations
The layer contains some pre-defined texts and translations for some languages. If your language is not included in the list of supported languages, you want to overwrite translations, or you want to add new ones, this is your option.
You need to provide an object (dictionary) to map the original text (the default english version) with its translated counterpart.
WARNING: Key sentences must be provided exactly as shown in the example. Otherwise they won’t work.
{
display: {
translations: {
'Results': '…',
'Search…': '…',
'Sorry, no results found.': '…',
'View less…': '…',
'View more…': '…',
'Selected Filters': '…',
'CLOSE': '…',
'CLEAR': '…',
'FILTER': '…',
'Query Too Large': '…',
'Latest Searches': '…',
'Sort by': '…',
'Relevance': '…',
'Delete': '…',
'POPULAR SUGGESTIONS': '…',
'SEARCH RESULTS': '…',
'View all {{total}} results': '…',
'Previous': '…',
'Next': '…',
'Selected Filters': '…',
'Clear all': '…',
'Suggestions:': '…',
'Did you mean:': '…'
}
}
}
currency
This is an object defining how you want prices be formatted. It has 5 attributes:
- symbol: this is the symbol you want to use for your currency, it can be either an utf-8 symbol, like ‘
€
‘ or the html escaped sequence, like ‘&euro
;’ - decimal: The symbol used to separate the decimal part of the price. Usually
','
or'.'
. - thousand: The symbol used to separate thousands in the price. Usually
'.'
or','
. - precision: How many decimals you want your price to have.
- format: Where to put the currency symbol in relation with the price value. It is a format string: (
"%s %v"
) where%s
is changed by the currency symbol and%v
is changed by the price value. Say you have a price of 13 euros. Then if format is: “%s%v
” the price would be shown as€13
. - forceDecimals:
true
by default, forces precision also for integer values.
Say you have this currency option defined:
{
display: {
currency: {
symbol: '£',
decimal: ',',
thousand: '.',
precision: 2,
format: '%s%v'
}
}
}
So a price of 13211.889 would be displayed as £13.211,89 and a price of 13211 would be displayed as £13.211,00.
NOTICE: By default the layer takes this configuration from the Doofinder servers based on the details provided for the search engine.
If you want to disable decimal formatting for integer values, disable forceDecimals
:
{
display: {
currency: {
symbol: '£',
decimal: ',',
thousand: '.',
precision: 2,
format: '%s%v',
forceDecimals: false // this changed the default value!
}
}
}
So a price of 13211 would be displayed as £13.211.
Input Behavior
captureLength
Minimum number of characters a user must type in the search box for a search request to be triggered.
The default value for this option is 3
.
{
display: {
captureLength: 3
}
}
wait
Input checking is delayed so the user has a chance of typing more characters without immediately sending search requests. You can configure the delay in milliseconds using this option.
The default value for this option is 42
.
{
display: {
wait: 42 // the meaning of life
}
}
Close Behavior
This layer can’t be closed due the way it works (by replacing contents of the insertion point when displayed).
Templating
templateFunctions
This option allows you to define your own template functions. Template functions transform the text passed to it so you can decide what to render in the HTML code.
Here is an example defining myBold
which simply wraps the text with the <b>
html tags.
{
display: {
templateFunctions: {
// my helper is a function that returns a function
myBold: function() {
// the returned function receives the text and
// a render function as an argument
return function(text, render) {
// you can transform the text before or after
// rendering it
return '<b>' + render(text) + '</b>';
}
}
}
}
}
Now you could use it in a custom template:
{{#results}}<h1>{{#myBold}}{{title}}{{/myBold}}</h1>{{/results}}
Take a look at the Mustache documentation to get familiar with the use of this functions and their possibilities.
templateVars
This option allows you to define your own variables to be passed to templates.
{
display: {
templateVars: {
userName: "Charles Xavier"
}
}
}
template
A template defined here overrides the skeleton of the entire Layer. All other templates are inserted somewhere inside this one. You can use it as a starting point to build your own custom layer.
WARNING: Be Careful, changing objects ids may break your layer. Do not use a custom template if you don’t know what you are doing.
NOTICE: You may feel more comfortable if you put the template code in a separate script and then import the template inside the layer options through its id.
Take a look at the Mustache documentation to get familiar with the use of templates.
Writing the layer template as Javascript code is prone to errors. The best approach is to wrap the layer template in a <script>
tag with a custom type so the browser doesn’t try to parse it as Javascript.
{
display: {
template: document.getElementById('df-layer-template').innerHTML
}
}
This is the default template:
<div class="df-embedded" id="{{ mainContainerId }}" hidden>
<div class="df-banner" id="df-banner__{{ mainContainerId }}"></div>
<div class="df-embedded__content">
<div class="df-aside" id="df-aside__{{ mainContainerId }}" data-role="aside"></div>
<div class="df-main">
<div class="df-header" id="df-header__{{ mainContainerId }}" data-role="header"></div>
<div class="df-results" id="df-results__{{ mainContainerId }}" data-role="result-list"></div>
<div id="df-pager__{{ mainContainerId }}"></div>
</div>
</div>
</div>
Placement
insertionPoint
By default the layer is appended to the <body>
of your page. If you want to put the layer in a different place you can specify a CSS selector pointing to an element of your page.
Setting this option only just changes the place where the layer is appended. If you want to also change the way the layer is inserted relative to the insertion point take a look at insertionMethod.
{
display: {
insertionPoint: "#myresults"
}
}
The previous example would append the layer to an element with the provided id
attribute:
<div id="myresults">
<h1>I'm a header</h1>
<!-- layer will be appended here -->
</div>
insertionMethod
This option is used in conjunction with insertionPoint
and lets you specify the way the layer is inserted in the page.
Valid values for this option are:
append
: This is the default value. The layer is appended as the last element of the specified insertion point.prepend
: Inserts the layer as the first element of the specified insertion point.before
: Inserts the layer as a sibling BEFORE the specified insertion point.after
: Inserts the layer as a sibling AFTER the specified insertion point.html
: The layer replaces the content of the specified insertion point.
NOTICE: When insertionPoint
is the <body>
, only append
and prepend
are valid methods.
Results configuration
urlParams
Object (a dictionary) with parameters to be appended to the URL of each result.
{
display: {
results: {
urlParams: {
source: 'doofinder',
'campaign-id': 123456 // some keys need to be quoted
}
}
}
}
NOTICE: This only works if you use the {{#url-params}}{{/url-params}}
function in your results template (the default template uses it).
In the previous example, this URL:
http://www.example.com/products/P1
would be converted to:
http://www.example.com/products/P1?source=doofinder&campaign-id=123456
queryParam
This option makes the search parameters being added to the urlParams
in the specified key.
{
display: {
results: {
queryParam: 'q'
}
}
}
NOTICE: This only works if you use the {{#url-params}}{{/url-params}}
function in your results template (the default template uses it).
In the previous example, this URL:
http://www.example.com/products/P1
would be converted to:
http://www.example.com/products/P1?q=whatever-you-searched
initialLayout
The way the results are displayed by default when the layer loads. This option can be grid
or list
.
{
display: {
results: {
initialLayout: 'grid'
}
}
}
template
You can replace the default results template by your own template by using this option.
If you need to include custom fields from your data feed, you should check also the [transformer] option to see how to receive those fields in the json response from the server.
The templates are defined using the Mustache template system.
Here is an example which defines a very simple custom template that overrides the default:
layerOptions = {
display: {
results: {
template: '{{#results}}<h1>{{title}}</h1>{{/results}}'
}
}
}
Notice: To make templating easier, you can write your custom template inside a special <script type="text/x-mustache-template">
tag with a unique id
. Then you can reference the template by id
from the layer configuration script. The template must be declared before the Doofinder script.
Warning: Some templates are more sensitive to changes than others. Note that if you modify the default templates of the layer, the layer may malfunction in the future. To restore the correct operation of the layer simply deactivate the custom template in the layer options.
As you can see there are also some sections which are functions like: format-currency, remove-protocol, url-params
and translate
. You can also define your own functions with the Template Functions option.
{{#results}}
<div class="df-card" data-role="result">
<a class="df-card__main" href="{{#url-params}}{{{link}}}{{/url-params}}" data-role="result-link" data-dfid="{{dfid}}">
{{#image_link}}
<figure class="df-card__image">
<img src="{{#remove-protocol}}{{image_link}}{{/remove-protocol}}" alt="{{title}}">
</figure>
{{/image_link}}
<div class="df-card__content">
<div class="df-card__title">{{title}}</div>
<div class="df-card__description">{{{description}}}</div>
{{#price}}
<div class="df-card__pricing">
<span class="df-card__price {{#sale_price}}df-card__price--old{{/sale_price}}">
{{#format-currency}}{{price}}{{/format-currency}}
</span>
{{#sale_price}}
<span class="df-card__price df-card__price--new">
{{#format-currency}}{{sale_price}}{{/format-currency}}
</span>
{{/sale_price}}
</div>
{{/price}}
</div>
</a>
</div>
{{/results}}
{{^results}}
<p class="df-no-results">{{#translate}}Sorry, no results found.{{/translate}}</p>
{{/results}}
Facets configuration
Under the facets
key you can configure everything related to how filters work in the layer.
width
Setting this option overrides the width value (absolute or relative) set by CSS for the facets column.
{
display: {
facets: {
width: '20%' // relative width
}
}
}
NOTICE: You can use almost any valid CSS unit but it’s safer to use px
or %
. No explicit unit means px
.
shownTerms
The number of terms that are visible by default for all text widgets.
{
display: {
facets: {
shownTerms: 10
}
}
}
attached
Whether you want the facets layer “attached” to the right side or the left side of the results layer.
Possible values are:
right
left
auto
The default value for this option is auto
.
{
display: {
facets: {
attached: 'auto'
}
}
}
startCollapsed
If you want to display the facet panels collapsed by default just set this option to true
.
The default value for this option is <false<.
{
display: {
facets: {
startCollapsed: false
}
}
}
selectedFilters
If you want to display a summary of the selected filters at the top of the facets aside, set this option to true
.
The default value for this option is false
.
{
display: {
facets: {
selectedFilters: false
}
}
}
NOTICE: This option is not available in the mobile version due to screen size limits.
custom
The custom
key is very powerful. It allows you to customize facets by changing the type of widget to use to render filters and even the options of those widgets.
There are some rules to take into account with this option:
- The value of this option is an array (list) of facet configuration objects.
- You can customize the facets you defined in the Doofinder Admin but nothing else.
- If you use these options you must declare all the facets you want to display.
{
display: {
facets: {
custom: [
// Your facet definitions go here
]
}
}
}
A facet definition is an object (a dictionary). Properties of the definition may vary depending on the type of facet and the widget used to display it.
The minimal definition you can use to specify that a widget for certain facet must be added is an object with a name
property:
{
display: {
facets: {
custom: [
{name: 'brand'},
{name: 'best_price'}
]
}
}
}
In that case, the rest of the properties will be obtained from the server:
{
"facets": [
{
"name": "best_price",
"type": "range",
"label": "Best Price"
},
{
"name": "brand",
"type": "terms",
"label": "Brand"
}
]
}
Any of the properties of the facets (except the name) could be changed if needed:
{
display: {
facets: {
custom: [
{name: 'brand', label: 'Available Brands'},
{name: 'best_price'}
]
}
}
}
The type of the facet determines the widget that will render it. There are 4 types of facets you can use:
- terms: A collapsible list of terms. This is the default widget for text fields.
- color: A color selector. Useful for clothes, or other items that can be filtered by color.
- grid: A grid of terms. Useful to filter by size. Displays text terms in a compact way.
- range: A slider to filter numbers. Useful for prices or other numeric fields.
This is a complete example to see the available options (less commonly used options are commented):
{
display: {
facets: {
custom: [
{
name: 'brand',
label: 'Available Brands',
type: 'terms',
shownTerms: 10,
// startCollapsed: true,
// templateVars: {
// viewMoreLabel: "View more…",
// viewLessLabel: "View less…"
// },
// templateFunctions: {},
// template: '…'
},
{
name: 'color',
label: 'Color',
type: 'color',
// map color names to real colors
// any valid CSS color can be used
colors: {
'beige': '#ffcc99',
'black': '#000000',
'blue': '#0059b3',
'brown': '#734d26',
'gold': '#cccc00',
'gray': '#666666',
'green': '#009933',
'navy blue': '#000099',
'orange': '#ff9900',
'red': '#b30000',
'white': '#ffffff',
'yellow': '#b3b300'
},
// templateVars: {},
// templateFunctions: {},
// template: '…'
},
{
name: 'size',
label: 'Size',
type: 'grid',
// you can specify all possible
// terms in a list and values
// will be ordered "as is" (any
// extra value will appear at
// the end)
order: [
'XS', 'S', 'M',
'L', 'XL', 'XXL'
],
// templateVars: {},
// templateFunctions: {},
// template: '…'
},
{
name: 'tags',
label: 'Tags',
type: 'grid',
// if you prefer you can
// just specify if you want
// terms sorted ascending or
// descending
order: 'asc', // asc | desc
// templateVars: {},
// templateFunctions: {},
// template: '…'
},
{
name: 'best_price',
label: 'Price',
type: 'range',
// you can pass a currency-like
// object to format values
numericFormat: {
symbol: '£',
decimal: '.',
thousand: ',',
precision: 2,
format: '%s%v'
},
// templateVars: {},
// templateFunctions: {},
// template: '…',
//
// you can change the scale of the slider to
// "zoom in" the range selected by users in
// case you have very low and high limits so
// they can refine the range in a proper way.
//
// zoom represents the percentage of the slider
// used to zoom the selected range.
//
// a value of `false` (default) disables zoom.
//
// zoom: 60 // min: 50, max: 90
},
{
name: 'length',
label: 'Length',
type: 'range',
// you can provide a formatting
// function directly
numericFormat: function(value){
return value.toFixed(2) + "cm";
},
// templateVars: {},
// templateFunctions: {},
// template: '…'
}
]
}
}
}
Although is not common to do it, all widgets can be customized by setting a template
and you can also add additional templateVars
and templateFunctions
to be used in the template.
If you want to see the default template for each widget type take a look at these options, which let you customize the template for all widgets globally:
panelTemplate
Facets are rendered inside collapsible panels. You can use this option to customize the template of those panels.
Notice: To make templating easier, you can write your custom template inside a special <script type="text/x-mustache-template">
tag with a unique id
. Then you can reference the template by id
from the layer configuration script. The template must be declared before the Doofinder script.
Warning: Some templates are more sensitive to changes than others. Note that if you modify the default templates of the layer, the layer may malfunction in the future. To restore the correct operation of the layer simply deactivate the custom template in the layer options.
The default template for panels:
<div class="df-panel" id="{{panelElement}}"
data-role="panel" data-facet="{{name}}">
<a class="df-panel__title" id="{{labelElement}}"
data-role="panel-label" data-toggle="panel"
href="#">
{{label}}
</a>
<div class="df-panel__content" id="{{contentElement}}"
data-role="panel-content"></div>
</div>
{
display: {
facets: {
panelTemplate: document.getElementById('…').innerHTML
}
}
}
termsTemplate
You can use this option to customize the template of the terms widget.
Notice: To make templating easier, you can write your custom template inside a special <script type="text/x-mustache-template">
tag with a unique id
. Then you can reference the template by id
from the layer configuration script. The template must be declared before the Doofinder script.
Warning: Some templates are more sensitive to changes than others. Note that if you modify the default templates of the layer, the layer may malfunction in the future. To restore the correct operation of the layer simply deactivate the custom template in the layer options.
This is the default template for the terms widget:
{{#terms}}
<div class="df-term" data-facet="{{name}}" data-value="{{key}}"
{{#extra-content}}{{index}}{{/extra-content}}
{{#selected}}data-selected{{/selected}}>
<span class="df-term__value">{{key}}</span>
<span class="df-term__count">{{doc_count}}</span>
</div>
{{/terms}}
{{#show-more-button}}{{terms.length}}{{/show-more-button}}
{
display: {
facets: {
termsTemplate: document.getElementById('…').innerHTML
}
}
}
colorTemplate
You can use this option to customize the template of the color widget.
Notice: To make templating easier, you can write your custom template inside a special <script type="text/x-mustache-template">
tag with a unique id
. Then you can reference the template by id
from the layer configuration script. The template must be declared before the Doofinder script.
Warning: Some templates are more sensitive to changes than others. Note that if you modify the default templates of the layer, the layer may malfunction in the future. To restore the correct operation of the layer simply deactivate the custom template in the layer options.
This is the default template for the color widget:
<div class="df-color-list">
{{#terms}}
<div class="df-color {{^color}}df-no-color{{/color}}"
data-facet="{{name}}" data-value="{{key}}"
{{#selected}}data-selected{{/selected}}
{{#color}}style="background-color: {{color}};"{{/color}}
title="{{key}}">
{{key}}
</div>
{{/terms}}
</div>
{
display: {
facets: {
colorTemplate: document.getElementById('…').innerHTML
}
}
}
gridTemplate
You can use this option to customize the template of the grid widget.
Notice: To make templating easier, you can write your custom template inside a special <script type="text/x-mustache-template">
tag with a unique id
. Then you can reference the template by id
from the layer configuration script. The template must be declared before the Doofinder script.
Warning: Some templates are more sensitive to changes than others. Note that if you modify the default templates of the layer, the layer may malfunction in the future. To restore the correct operation of the layer simply deactivate the custom template in the layer options.
This is the default template for the grid widget:
<div class="df-grid">
{{#terms}}
<div class="df-grid-item" data-facet="{{name}}"
data-value="{{key}}"
{{#selected}}data-selected{{/selected}}>
{{key}}
</div>
{{/terms}}
</div>
{
display: {
facets: {
gridTemplate: document.getElementById('…').innerHTML
}
}
}
rangeTemplate
You can use this option to customize the template of the range widget.
Notice: To make templating easier, you can write your custom template inside a special <script type="text/x-mustache-template">
tag with a unique id
. Then you can reference the template by id
from the layer configuration script. The template must be declared before the Doofinder script.
Warning: Some templates are more sensitive to changes than others. Note that if you modify the default templates of the layer, the layer may malfunction in the future. To restore the correct operation of the layer simply deactivate the custom template in the layer options.
This is the default template for the range widget:
<div class="df-slider" data-facet="{{name}}"></div>
{
display: {
facets: {
rangeTemplate: document.getElementById('…').innerHTML
}
}
}
Banner configuration
template
You can customize the way the banner is displayed by modifying its template:
{
display: {
banner: {
template: '…'
}
}
}
This is the default template for the bannner container:
{{#banner}}
<a {{#blank}}target="_blank"{{/blank}} href="{{link}}" data-role="banner" data-banner="{{id}}">
<img src="{{#remove-protocol}}{{image}}{{/remove-protocol}}">
</a>
{{/banner}}
Mobile Version
If you have the mobile version enabled for your search engine you can tweak some aspects of the integration.
Under the mobile
key you can set or override most of the options of the layer. Most of the options available in the main layer are available for the mobile layer as well.
If you want to deactivate the mobile version for a layer just set the mobile
option to false
.
{
mobile: false
}
maxWidth
The maximum viewport width where the mobile version will be shown. By default 767px
.
{
mobile: {
maxWidth: 767
}
}
toggleInput
If you have different search boxes for desktop and mobile, you can set the toggleInput
option under the mobile
key.
NOTICE: In V5 this option could be set via queryInput
or toggleInput
indistinctly. Since V6 the only valid option is toggleInput
.
{
mobile: {
toggleInput: '#mobile_search_box'
}
}
hashid
If you want to use a different SearchEngine for your mobile version you can set a different hashid
.
{
mobile: {
hashid: '1955a94db43999fcc211f925fc85f12c'
}
}
voiceSearch
Voice Search is enabled by default in compatible mobile devices. Devices that don’t support Voice Search won’t see anything special in the UI.
The default value for this options is true. If you want to disable Voice Search for all users just set this option to false.
{
mobile: {
voiceSearch: false
}
}
Display Options
You can set the display options, since the mobile layer is fullscreen, positioning options have no sense.
initialSearch
Displays the top searches when the mobile layer is opened and no query has been done.
The value of this option is true by default, unless there’s a background image defined in the templateVars
.
If you want to show an empty layer, set this option to false.
{
mobile: {
display: {
initialSearch: false
}
}
}
If you want to perform a custom search just put the search terms there:
{
mobile: {
display: {
initialSearch: 'red boots'
}
}
}
closeOnHit
Closes the mobile layer when a result is clicked. This is disabled by default but you should consider using it when your site is a one-page-application that loads URLs with AJAX without changing page location.
The default value of this option is false.
{
mobile: {
display: {
closeOnHit: false
}
}
}
template
You can customize your layer template like in the desktop version but the template in this case is different:
<div class="df-mobile"{{#images.body}} style="background-image:url('{{images.body}}');"{{/images.body}} id="{{ mainContainerId }}" hidden>
<div class="df-mobile__wrapper">
<div class="df-mobile__header" id="df-mobile__header__{{ mainContainerId }}">
{{#images.header}}
<div class="df-mobile__header__image">
<img src="{{ images.header }}">
</div>
{{/images.header}}
<form action="" method="get">
<div class="df-mobile__searchbox" data-empty="true">
{{#voicesearch}}
<button type="button" data-role="voicesearch">
<svg fill="#606569" width="24" height="24" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M12,14c1.66,0,3-1.34,3-3V5c0-1.66-1.34-3-3-3S9,3.34,9,5v6C9,12.66,10.34,14,12,14z"/>
<path d="M17,11c0,2.76-2.24,5-5,5s-5-2.24-5-5H5c0,3.53,2.61,6.43,6,6.92V21h2v-3.08c3.39-0.49,6-3.39,6-6.92H17z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>
</button>
{{/voicesearch}}
{{#imagesearch}}
<button type="button" data-role="imagesearch" class="fileContainer">
<svg id="df-mobile__imagesearch_icon__{{ mainContainerId }}" fill="#606569" width="24" height="24" viewBox="0 0 24 24"
enable-background="new 0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="3.2"/>
<path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>
<svg id="df-mobile__imagesearch_loading__{{ mainContainerId }}" class="spinner" width="24px" height="24px"
viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">
<circle class="path" fill="none" stroke-width="6"
stroke-linecap="round" cx="33" cy="33"
r="30"></circle>
</svg>
<input data-role="image-search-control" id="df-mobile__imagesearch__{{ mainContainerId }}" type="file" accept="image/*"/>
</button>
{{/imagesearch}}
<svg class="df-mobile__searchbox__loupe" fill="#606569" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>
<input type="search" placeholder="{{#translate}}{{placeholderText}}{{/translate}}" id="df-mobile__searchbox__{{ mainContainerId }}" autocapitalize="off" autocomplete="off" autocorrect="off">
<button type="button" data-role="clear">{{#translate}}CLEAR{{/translate}}</button>
<button type="button" data-role="close">{{#translate}}CLOSE{{/translate}}</button>
</div>
<div class="df-mobile__header__actions" id="df-mobile__header__actions__{{ mainContainerId }}"></div>
<div class="df-mobile__suggestions" id="df-mobile-suggestions__{{ mainContainerId }}" data-role="suggestion-list" hidden>
</div>
</form>
</div>
<div class="df-mobile__content" id="df-mobile__content__{{ mainContainerId }}"></div>
<button class="df-mobile__action-button df-in df-out" type="button" data-role="close" data-scroll-in-out>
<svg fill="#606569" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>
</button>
<button class="df-mobile__action-button df-in df-out" type="button" data-role="scrolltop" data-scroll-in-out>
<svg fill="#606569" height="48" viewBox="0 0 24 24" width="48" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M4 12l1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/>
</svg>
</button>
</div>
<div class="df-mobile__overlay" data-role="toggle-filters"></div>
<div class="df-mobile__aside">
<div class="df-mobile__aside__actions">
<button class="df-mobile__button" type="button" data-role="toggle-filters">{{#translate}}CLOSE{{/translate}}</button>
<button class="df-mobile__button" type="button" data-role="clear-filters">{{#translate}}CLEAR{{/translate}}</button>
</div>
<div id="df-mobile__aside__sorting__{{ mainContainerId }}"></div>
<div class="df-mobile__aside__content" id="df-mobile__aside__content__{{ mainContainerId }}"></div>
</div>
</div>
Notice: To make templating easier, you can write your custom template inside a special <script type="text/x-mustache-template">
tag with a unique id. Then you can reference the template by id from the layer configuration script. The template must be declared before the Doofinder script.
Warning: Some templates are more sensitive to changes than others. Note that if you modify the default templates of the layer, the layer may malfunction in the future. To restore the correct operation of the layer simply deactivate the custom template in the layer options.
{
mobile: {
display: {
template: document.getElementById('…').innerHTML
}
}
}
templateVars
Like in the desktop version of the layer, you can set variables to be used in the template. In this case some variables are available by default:
{
mobile: {
display: {
templateVars: {
placeholderText: 'Search…',
images: {
header: null,
body: null
}
}
}
}
}
placeholderText
This is a special variable that corresponds to the text that will be used as the placeholder for the search input in the mobile layer.
images
This is a special variable / object that you can use to configure some images to adapt the look and feel of the layer:
header
: will appear before the search box.body
: will appear as a centered background image in the layer. Using this option impliesmobile.display.showMostSearched
being false.
NOTICE: You’re not forced to use these values, they’re provided to avoid modifying the layer template just to add a custom header/body image. If you need a third image just add it and adapt the template to your needs.
templateFunctions
Like in the desktop version you can create your own template functions. Refer to templateFunctions for more information.
{
mobile: {
display: {
templateFunctions: {}
}
}
}
Results Configuration
template
You can set your own results template to show extra content in your results or shape the content as you want.
Refer to Results Template Configuration in the desktop version for more information.
{
mobile: {
display: {
results: {
template: document.getElementById('…').innerHTML
}
}
}
}
Facets Configuration
You can override almost any facets options from the desktop configuration for the mobile version of the layer. Refer to Facets Configuration for more details.
{
display: {
facets: {
startCollapsed: false
}
},
mobile: {
display: {
facets: {
startCollapsed: true
}
}
}
}
Plugins
Search History
WARNING: You need to activate the Search History add-on in your account before you can use this plugin. If you have any questions, please do not hesitate to contact us.
The Search History plugin displays a layer with the latest searches done in Doofinder by the user. It’s displayed when the user puts the focus in the search box by clicking on it.
To enable this plugin with the default options set historyPlugin
as true in the layer options:
{
historyPlugin: true
}
NOTICE: This plugin is disabled by default.
To customize the plugin options use a plain Object instead:
{
historyPlugin: {
// options go here…
}
}
Continue reading to see the available options.
label
By default a label is displayed above the entries in the searches list. There are translations for the main languages.
{
historyPlugin: {
label: "Latest Searches"
}
}
To disable the label just set it to false:
{
historyPlugin: {
label: false
}
}
min, max
You can customize the plugin behaviour by changing the minimum and maximum number of entries.
min
is the minimum number of entries that the layer must contain to be displayed.
max
is the maximum number of entries the layer may contain.
{
historyPlugin: {
min: 1,
max: 10
}
}
position
The layer is positioned like the Classic Layer itself and it accepts most of the positioning options (top, left, dtop, dleft
, …) but you should change as few options as possible.
The options you may want to change are represented in this sample:
{
historyPlugin: {
position: {
width: 300,
dtop: 4,
dleft: 0
}
}
}
Advanced Concepts
Search Parameters
filter
If you want to include only results matching some condition you can use the filter parameter in your layer configuration.
See Filter Parameters in the Search API reference to learn more about filters.
{
searchParams: {
filter: {
categories: ['Awesome', 'Other'],
brand: ['My Brand'],
best_price: {
gte: 100
}
}
}
}
exclude
If you want to exclude results matching some condition you can use the exclude parameter in your layer configuration.
{
searchParams: {
exclude: {
categories: ['This not', 'Other'],
brand: ['Excluded brand']
}
}
}
sort
You can specify fields to sort results. See Sort Parameters:
{
searchParams: {
sort: [
{'_score': 'desc'},
{'price': 'asc'},
{'brand': 'desc'}
]
}
}
WARNING: In order to being able to sort by multi-word fields (like title), that field must be included into the Custom Sorting fields of the Advanced Settings of our administration panel.
type
If you want to restrict the searches to some of your data types. Just set this property:
{
searchParams: {
type: ['product', 'posts']
}
}
In the example above you will be searching just products and posts.
query_name
If you want to execute always a specific kind of query you can set this option (see query_name in Dark magic parameters):
{
searchParams: {
query_name: 'fuzzy'
}
}
min_score
The minimum score required for results to be considered valid. Results with scoring below this won’t be included in the search results.
{
searchParams: {
min_score: 0.5
}
}
transformer
Doofinder search servers return the results transformed so layers can work without too much configuration.
The default transformer is basic
which populates these fields:
title
: The title of the product.type
: This usually will be product.hashid
: The unique identificator of your search engine.dfid
: The doofinder id of your product.link
: The link to your product.image_link
: The image source of the product.description
: The description of the product.price
: The price of the product.sale_price
: The sale price of the product, if any.
If you want to display any other data you’ve indexed or the original field names, first you have to set this option to null:
{
searchParams: {
transformer: null
}
}
This will make the server to return all your data when querying.
rpp
Results per page, how many results you want to be returned on each query.
{
searchParams: {
rpp: 20
}
}
The Standalone Script
The easiest way to try Doofinder is by using one of our layers. You get a snippet of text and then you just include it in the HTML of your website.
This is how the standard Doofinder script looks like:
<script>
var doofinder_script ='//cdn.doofinder.com/media/js/doofinder-embedded.7.latest.min.js';
(function(d,t){var f=d.createElement(t),s=d.getElementsByTagName(t)[0];f.async=1;
f.src=('https:'==location.protocol?'https:':'http:')+doofinder_script;
f.setAttribute('charset','utf-8');
s.parentNode.insertBefore(f,s)}(document,'script')
);
var dfEmbeddedLayers = [{
"queryInput": "#query",
"hashid": '1955a94db43999fcc211f925fc85f12c',
"zone": "eu1",
"display": {
"lang": 'en'
}
}];
</script>
And this is what it does:
- First of all, the URL of the layer’s source code is saved in a variable.
- Then a function is called to create a <script> tag wherever the rest of <script> tags are in your site.
- Then it declares the layers to be used in the page in the dfEmbeddedLayers variable.
- All this time the <script> tag we created has been downloading the code asynchronously. When the code is ready then the layers are instantiated and configured.
But, what if I use RequireJS in my page? That would break my site… Well, let’s see that case.
RequireJS: the easy way
This is the easiest script you can use in a RequireJS environment if you only want to copy and paste a snippet with no hassle:
<script>
var dfUrl = '//cdn.doofinder.com/media/js/doofinder-embedded.7.latest.min.js';
(function(c,o,k,e){var r,t,i=setInterval(function(){t+=c;r=typeof(require)==='function';
if(t>=o||r)clearInterval(i);if(r)require([k],e)},c)})(100, 10000, dfUrl, function(doofinder){
doofinder.embedded.setLayers([{
"queryInput": "#query",
"hashid": '1955a94db43999fcc211f925fc85f12c',
"zone": "eu1",
"display": {
"lang": 'en'
}
}]);
});
</script>
And this is what it does:
- First of all, the URL of the layer’s source code is saved in a variable.
- Then a function is called to check periodically that the require function exists in the page. The function stops if the function is not available after 10s.
- Then, the layer initialization is executed manually via setLayers.
RequireJS: the hard way
If you want to add Doofinder to your existing RequireJS workflow then you can just require its source code from a URL:
// DON'T COPY AND PASTE THIS CODE DIRECTLY INTO YOUR HTML!
// This code must be integrated into your existing RequireJS-based code.
require(['//cdn.doofinder.com/media/js/doofinder-embedded.7.latest.min.js'], function(doofinder){
doofinder.embedded.setLayers([{
"queryInput": "#query",
"hashid": '1955a94db43999fcc211f925fc85f12c',
"zone": "eu1",
"display": {
"lang": 'en'
}
}]);
});
Configure the layers manually
In case you want to control when the layers are loaded, just pass the list of layers to the setLayers
method. Just make sure that doofinder is completely loaded in your page.
doofinder.embedded.setLayers([{
"queryInput": "#query",
"hashid": '1955a94db43999fcc211f925fc85f12c',
"zone": "eu1",
"display": {
"lang": 'en'
}
}]);
Change options after load
When doofinder is loaded, dfEmbeddedLayers
is replaced by an array of Doofinder Configuration Objects or, if run as a module on a requirejs
environment, the dfEmbeddedLayers
array is created and made accesible at the browser’s window
object. These objects can be used to change some configuration during the execution.
This could be useful, if you wanted to change the layer behavior depending on the user in session, the screen size…
So you can do this in two steps:
- Change an option in the property
dfEmbeddedLayers[0].layerOptions
. - Execute
dfEmbeddedLayers[0].configure()
.
If you have more than one layer, you could use dfEmbeddedLayers[k]
as well (where k
is the position of the layer you want to change in dfEmbeddedLayers
).
Example:
Imagine you want to change the place where the layer is inserted. The code you need to do this would be:ç
dfEmbeddedLayers[0].layerOptions.display.insertionPoint = "#container";
dfEmbeddedLayers[0].configure();
Scrolling up when changing the page
If you want the page to scroll up when the user changes the page from the pager at the bottom of the layer, you only have to add the next callback into layer configuration:
callbacks: {
loaded: function(instance) {
instance.layer.pagerWidget.element.on('click', '.df-page', function() {
window.scroll({top: 0, left: 0, behavior: 'smooth'})
});
}
}
If your page uses jQuery, you can use it to have more customization options:
callbacks: {
loaded: function(instance) {
instance.layer.pagerWidget.element.on('click', '.df-page', function() {
$("html, body").animate({ scrollTop: 0 }, 600); // 600 ms
});
}
}
How to modify the search response
In certain situations you may want to customize the search response to modify some data. To do that you can register response processors that will receive the response object as an argument and must return it (modified or not) as the result so it can be passed to the layer components for rendering.
WARNING: Be careful, if you perform resource-intensive operations the layer rendering may slow down. Try to perform very simple operations and only when there’s no other way to get the desired results.
The best way to register a processor is to use the loaded callback:
var changeTitle = function(result){
result.title += ' (processed)';
return result;
};
// …
{
callbacks: {
loaded: function(instance) {
// Register a processor that changes the title of the results
instance.layer.controller.processors.push(function(response){
response.results = response.results.map(changeTitle);
return response;
});
}
}
}
You can add more than one processor. All processors will be executed in order, receiving the result returned by the previous processor executed.