Integrating Search Expander
Integrating Search Expander into your search results page consists of the following steps:
- Creating a search engine project within your Search Expander account
- Adding some markup to your search engine results page
- Loading the Search Expander JavaScript library on the page
- Calling the global
sxpr()
JavaScript function after your search results have loaded
The sxpr()
function will call the Search Expander API, fetch widget data, and render it in the
HTML containers you have specified. It must be passed an object with an se
property whose value
is your search engine project ID. Widget behaviour and appearance can be customised by adding
additional properties to this object.
You can include the Search Expander JavaScript library using:
<script src="https://cdn.searchexpander.com/js/sxpr.js"></script>
To run Search Expander, call sxpr()
after the script has loaded:
sxpr({
se: 'your-search-engine-id', // Replace 'your-search-engine-id' with your search engine's ID
// Add other Search Expander settings here...
});
To create the appropriate widgets, Search Expander needs the following information about the page:
- Where to render the widgets
- The user's search query
- The search query's results
- How to transform a search query into a SERP request
Although Search Expander will try to guess 2-4, it is recommended to specify them explicitly. This
is easy to accomplish via some simple changes to your markup or by adding properties to the object
passed to sxpr()
.
Widget containers
Search Expander widgets will render inside a number of HTML container elements:
- Knowledge panel
- Instant answers
- Media thumbnail bar
- Top bar
Container placement
Typically, the widget containers will be placed in the page locations described below. However, they can be placed anywhere on the page and their contents will adjust responsively.
- The knowledge panel will be a sidebar next to the mainline search results.
- The instant answers widget will be above the mainline search results.
- The media thumbnail bar will run across the top of the search results.
- The top bar will run across the top of the search results, above the media thumbnail bar.
You can identify widget containers in your markup using data-sxpr-...
attributes, or reference
them directly on the settings object passed to sxpr()
:
Widget | HTML attribute | Settings property |
---|---|---|
Knowledge panel | data-sxpr-knowledge-panel |
knowledgePanelContainer |
Instant answers | data-sxpr-instant-answers |
instantAnswersContainer |
Media thumbnail bar | data-sxpr-media-thumb-bar |
mediaThumbBarContainer |
Top bar | data-sxpr-top-bar |
topBarContainer |
If you are serving product ads, you can also provide a container for shopping results, which will typically be on a separate page from the web SERP:
Widget | HTML attribute | Settings property |
---|---|---|
Shopping | data-sxpr-shopping |
shoppingContainer |
Markup example
<div data-sxpr-top-bar>
<!-- Top bar, including search suggestions, will render here -->
</div>
<div data-sxpr-media-thumb-bar>
<!-- Media thumbnail bar will render here -->
</div>
<div id="main-content">
<div id="mainline">
<div data-sxpr-instant-answers>
<!-- Instant answers will render here -->
</div>
<div>
<!-- ... your search results here ... -->
</div>
</div>
<div id="sidebar">
<div data-sxpr-knowledge-panel>
<!-- Knowledge panel will render here -->
</div>
</div>
</div>
JavaScript example
sxpr({
se: 'your-search-engine-id',
knowledgePanelContainer: document.querySelector('#my-kp-container'), // or pass a selector string
mediaThumbBarContainer: document.querySelector('#my-mtb-container'),
instantAnswersContainer: document.querySelector('#my-ia-container'),
topBarContainer: document.querySelector('#my-top-bar-container'),
// ... other settings
});
Search query
Add the data-sxpr-input
attribute to a search input, and Search Expander will read its value when sxpr()
is called:
<input name="q" value="lorem ipsum" data-sxpr-input>
Alternatively, you can specify the current search query directly using the q
property of the sxpr()
settings argument:
sxpr({
se: 'your-search-engine-id',
q: 'lorem ipsum',
});
Or you can specify a search input using the searchInput
property:
sxpr({
se: 'your-search-engine-id',
searchInput: document.querySelector('input[name="q"]'), // or pass a selector string
});
Search results
Search Expander needs to know the page's current search results. The simplest way of allowing this is to add a data-sxpr-link
attribute to each of your search result links. Alternatively, you can pass your results directly to sxpr()
in the form of an array of { url: string; title: string }
objects.
In the absence of the above, Search Expander will make a guess about where your page's search results are, but it is more reliable to make things explicit.
Some markup examples:
<a data-sxpr-link href="https://example.com/">Example Page Title</a>
<div data-sxpr-link>
<a href="https://example.com/">Example Page Title</a>
...
</div>
<div data-sxpr-link="https://example.com/" data-sxpr-title="Example Page Title">
<!-- You can specify url and title as attribute values on any elements -->
...
</div>
Or for a direct approach:
sxpr({
se: 'your-search-engine-id',
results: [
{
url: "https://example.com/",
title: "Example Page Title"
},
{
url: "https://example.com/example2",
title: "Another Example"
},
// ...
],
// ... other settings
});
There may be cases where you cannot modify the HTML of your search result links or where the search results cannot be known at the time of page load. See Dynamic Search Results for how to deal with these cases.
Search requests
Search Expander widgets contain links for new user searches. In order to construct the link URLs, Search Expander needs a URL template. For example:
https://example.com/search?q={searchTerms}
…replacing example.com
with your domain.
Relative URLs with absolute paths are allowed, for example:
/search?q={searchTerms}
This URL template should be added as searchUrlTemplate
on the settings object passed to sxpr()
.
There are some situations where this approach may be inadequate. For example, where:
- Search requests are initiated by JavaScript code rather than standard page navigation
- Search requests are POST requests rather than GET requests
- Additional URL parameters need to be set dynamically for search requests
You can deal with these requirements by setting a handleSearchClick
function on the sxpr()
settings object. In this case, when a widget search link is clicked, the link's default behaviour will be overridden and the function will be left to handle the search request. handleSearchClick
is passed an object with the following interface, to provide the information with which a search query can be created:
{
q: string;
mediaThumbBar: string | undefined;
searchSuggestions: string | undefined;
wordToDefine: string | undefined;
link: HTMLAnchorElement;
event: MouseEvent;
url: string | undefined; // If searchUrlTemplate is set, this is the search URL that would be followed by default
}
More details with examples can be found here.
Additional notes
By default, searchUrlTemplate
will have a query string automatically added to it containing values for mediaThumbBar
, searchSuggestions
and wordToDefine
. These values trigger extra widget content. In order to control how these appear in the URL, you can add these tags yourself, for example:
https://example.com/search?q={searchTerms}&customkey1={mediaThumbBar}&customkey2={searchSuggestions}&customkey3={wordToDefine}
Dynamic search results
There are situations in which search results may be loaded via JavaScript after the rest of the page, or where you cannot edit the search results markup. For example, the results may be provided by a Google Programmable Search Engine. In these cases, the call to sxpr()
must be deferred until after the results have been rendered. You can handle this by calling sxpr()
inside a callback function or after an await
expression. Here's a simplified example:
async function main() {
// No search results on the page yet...
// Get the user search query
const q = document.querySelector('#search-input').value;
// Fetch results based on the search query
const results = await fetchSearchResults(q);
// Render the results on the page
renderSearchResults(results);
// Now it's safe to run Search Expander:
sxpr({
se: 'your-search-engine-id',
q,
results,
// ... other settings
});
}
// Fetches search results data from an external API, and returns an array of
// { url: string, title: string } objects
async function fetchSearchResults(q) {
// ...
}
// Renders the search results on the page
function renderSearchResults(results) {
// ...
}
main();
Below is an example of calling sxpr()
in a Google Programmable Search Engine (v2) callback:
window.__gcse = {
searchCallbacks: {
web: {
rendered: () => {
sxpr({
se: 'your-search-engine-id',
// ... other settings
});
}
}
}
};
Basic example
The example below assumes that the search results are rendered directly onto the page, before any JavaScript is executed.
...
<main>
<h1>Search Page</h1>
<form>
<input data-sxpr-input name="q" type="search" placeholder="Search">
<button type="submit">Search</button>
</form>
<div data-sxpr-top-bar>
<!-- Top bar, including search suggestions, will render here -->
</div>
<div data-sxpr-media-thumb-bar>
<!-- Media thumbnail bar will render here -->
</div>
<div id="content">
<div id="mainline">
<div data-sxpr-instant-answers>
<!-- Instant answers will render here -->
</div>
<div id="results">
<div class="result">
<a data-sxpr-link href="https://example.com/">Example Page</a>
<div class="url">https://example.com/</div>
</div>
<div class="result">
<a data-sxpr-link href="https://example.com/example2">Another Example Page</a>
<div class="url">https://example.com/example2</div>
</div>
<!-- ... more search results here -->
</div>
</div>
<div id="sidebar">
<div data-sxpr-knowledge-panel>
<!-- Knowledge panel will render here -->
</div>
</div>
</div>
</main>
<script src="https://cdn.searchexpander.com/js/sxpr.js"></script>
<script>
sxpr({
se: 'your-search-engine-id',
searchUrlTemplate: '/?q={searchTerms}',
});
</script>
...
Multilanguage Support
To set a language for Search Expander results, you can set the lang
property on the settings object passed
to sxpr()
. This property must be a two-letter ISO 639-1 language code. The currently supported languages are:
Language | lang value |
---|---|
English (default) | en |
Deutsch | de |
Español | es |
Français | fr |
Italiano | it |
Nederlands | nl |
If you want to allow your users to select their own language, you will need to read this value from somewhere and pass it into
the sxpr()
function. For example, you could read it from an input:
sxpr({
se: 'your-search-engine-id',
q,
results,
lang: document.querySelector('[name="language"]').value,
// ... other settings
});
Or from a URL query string:
sxpr({
se: 'your-search-engine-id',
q,
results,
lang: new URL(location.href).searchParams.get('lang'),
// ... other settings
});
If you're loading search results into your page dynamically, you could read a language value
from a <select>
element before calling sxpr()
:
<!-- Highly simplified example code! -->
<input type="search" id="search-input" />
<select id="language-selector">
<option value="en">English</option>
<option value="de">Deutsch</option>
<option value="es">Español</option>
<option value="fr">Français</option>
<option value="it">Italiano</option>
<option value="nl">Nederlands</option>
</select>
<script src="https://cdn.searchexpander.com/js/sxpr.js"></script>
<script>
async function main() {
// Get the user search query
const q = document.querySelector('#search-input').value;
// Fetch results based on the search query
const results = await fetchSearchResults(q);
// Render the results on the page
renderSearchResults(results);
// Run Search Expander
sxpr({
se: 'your-search-engine-id',
q,
results,
lang: document.querySelector('#language-selector').value,
// ... other settings
});
}
// Fetches search results from an API, and returns an array of
// { url: string, title: string } objects
async function fetchSearchResults(q) {
// ...
}
// Renders the search results on the page
function renderSearchResults(results) {
// ...
}
main();
</script>
YouTube search result thumbnails
If you wish, you can add thumbnail images for YouTube links into your search engine results by setting youTubeResultThumbs
to true
in the settings object passed to sxpr()
, and indicating where you would like the thumbnails to
render by adding the attribute data-sxpr-result-thumb
to image containers in your markup. The value of this
attribute must be the result URL. For example:
<div id="results">
<div data-sxpr-result class="result">
<!-- Thumbnail image will be added into the div below if it's a YouTube video URL -->
<div data-sxpr-result-thumb="https://example.com/" class="result-thumb"></div>
<div class="result-text">
<a data-sxpr-link href="https://example.com/">Example Page</a>
<div class="url">https://example.com/</div>
</div>
</div>
<!-- ... more search results here -->
</div>
<script src="https://cdn.searchexpander.com/js/sxpr.js"></script>
<script>
sxpr({
se: 'your-search-engine-id',
youTubeResultThumbs: true,
// ... other settings
});
</script>
The data-sxpr-result-thumb
elements will have their inner HTML deleted and replaced,
so they should be empty.
The thumbnail images come with some default styling, but you may need to update your CSS to make them fit nicely into the surrounding layout.
Note that if the data-sxpr-result-thumb
element has an ancestor with a data-sxpr-result
attribute,
then this ancestor element will be automatically given the class sx-thumb-result
, which may
help with styling in cases where you want to distinguish result containers with thumbnails from those
without.
By default, these images are served directly from YouTube's servers.
If you want to proxy the images, add an appropriate value for the imageUrlTemplate
setting to the
settings object passed to sxpr()
. See the page on Private Search Engines
for more information.
Google Custom Search Engines
If you are using a Google Custom Search Engine for your results, you may want to set
youTubeCSEThumbs
to true in the settings object passed to sxpr()
. This will attempt to insert
YouTube thumbnail images into your CSE result containers. No additional markup is required in this case.
Detecting request completion and errors
You may want to listen for certain Search Expander events while data is fetched and widgets are rendered.
To do this, you can add listeners for the following events which are fired on document
during the
execution of sxpr()
and sxpr.shopping()
:
Event | Description |
---|---|
sx-request-aborted |
Fires when no request should be made (e.g. because of lack of input data) |
sx-requesting |
Fires immediately before a request is made to the Search Expander API |
sx-request-complete |
Fires immediately after a response has been received from the Search Expander API |
sx-complete |
Fires when sxpr() has completed all of its main functions (including rendering widgets) |
sx-error |
Fires when a error is encountered and sxpr() must abort. Information about the error may be found on the detail property of the event. |
For example:
document.addEventListener('sx-complete', () => {
// Do something here when Search Expander completes successfully...
});
Troubleshooting
If Search Expander is not behaving as expected, you can set logLevel
on the settings object passed to sxpr()
or
sxpr.shopping()
to get information sent to the browser console log.
logLevel
must be set to one of the following values:
- debug - Logs information about normal operations as well as warnings and errors.
- warning - Logs information about warnings and errors only.
- error - Logs information about errors only.