Embedded Layer Reference (v7)

or send an email to support@doofinder.com

In this article

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 or false.
    • 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. If true then the layer will send search events as page views with a query 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.

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…": "…",
      "Search": "…",
      "CLOSE": "…",
      "CLEAR": "…",
      "FILTER": "…",
      "Query Too Large": "…"
    }
  }
}

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: '&pound;',
      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: '&pound;',
      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.

<script type="text/x-mustache-template" id="df-layer-template">
  <!-- Your HTML/Mustache code goes here -->
</script>

Now you can reference it as any other HTML element in your page. Just make sure the layer template is defined before the layer configuration.

{
  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 id="df-header__{{ mainContainerId }}"></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

scrollOffset

Distance in px from the bottom of the results content. When the user reached that point scrolling the results a new results page is triggered.

The default value for this option is 500.

{
  display: {
    results: {
      scrollOffset: 500 // no units allowed here
    }
  }
}

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
    }
  }
}

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:

Facet Type Widget
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: '&pound;',
            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
    }
  }
}

Header configuration

Layers come with a special header section which will be more or less complete depending on the type of layer you're using.

The header is usually in charge of giving details like the number of results in the search response, or providing buttons for certain actions.

show

Indicates if the header must be displayed or not.

The default value for this option is true.

 display: {
   header: {
     show: true
   }
 }

template

 display: {
   header: {
     template: '…'
   }
 }

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 is:

<div class="df-header df-embedded-header df-mb-3">
  <div class="df-icon-list">
    <a class="df-icon" href="#" data-change-layout="list">
      <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
          <path d="M4 18h17v-6H4v6zM4 5v6h17V5H4z"/>
          <path d="M0 0h24v24H0z" fill="none"/>
      </svg>
    </a>
    <a class="df-icon" href="#" data-change-layout="grid">
      <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
          <path d="M4 11h5V5H4v6zm0 7h5v-6H4v6zm6 0h5v-6h-5v6zm6 0h5v-6h-5v6zm-6-7h5V5h-5v6zm6-6v6h5V5h-5z"/>
          <path d="M0 0h24v24H0z" fill="none"/>
      </svg>
    </a>
  </div>
  <div class="df-header-title">
    <span>{{#translate}}Results{{/translate}}: <span data-role="total">{{ total }}</span></span>
  </div>
</div>

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">
          <svg 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 }}">
          <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>
      </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 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 implies mobile.display.showMostSearched being false.

Custom Look & Feel Example

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

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:

  1. First of all, the URL of the layer's source code is saved in a variable.
  2. Then a function is called to create a <script> tag wherever the rest of <script> tags are in your site.
  3. Then it declares the layers to be used in the page in the dfEmbeddedLayers variable.
  4. 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:

  1. First of all, the URL of the layer's source code is saved in a variable.
  2. 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.
  3. 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:

  1. Change an option in the property dfEmbeddedLayers[0].layerOptions.
  2. 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();