Providing Suggestions for the Omnibox

The Omnibox provides an interface that any custom component can interact with and provide data to so that the Omnibox renders it. When the Omnibox needs data, it triggers a populateOmnibox event. To listen to this event and provide suggestions, a component needs to bind a function on the root of the search interface.

The sections in this article describe the steps required to provide suggestions to the Omnibox.

Binding to the Root

Set a reference to the root of your search interface by binding the populateOmnibox event and the onPopulateOmnibox function to the searchInterface root element using JQuery $.on.

var searchInterface = $("#mySearchInterface"); //This is the root element
searchInterface.on("populateOmnibox" , onPopulateOmnibox); //root.on allows us to bind the event and our function. Here populateOmnibox is the event and onPopulateOmnibox is our function.

Implementing the onPopulateOmnibox Function

Implement the following code. This function will be executed everytime the populateOmnibox event is triggered.

function onPopulateOmnibox (e , populateOmniboxObject){
    // First we need to decide what we are searching for.
    // It is up to each component to decide what would suit best for their own need. For example, a facet will look for the whole content of the query box to provide suggestions.
    // Another custom component could instead decide to use only the active word (the position of the cursor in the query box).
    var wordToSearch = populateOmniboxObject.completeQueryExpression.word;
    var regexToSearch = populateOmniboxObject.completeQueryExpression.regex;
    // This is the main function that each custom component will need to implement. In this example, this function will be implemented in the next section.
    // Note that this would cover the case where you can immediately return HTML content. If you need to perform an asynchronous call (e.g., an Ajax request), refer to the
    // section "Populating Omnibox with content from an asynchronous call" of this page.
    var elementToReturn = buildOmniboxContent(regexToSearch, populateOmniboxObject);
    var objectToReturn = {
        zIndex: 100, //Arbitrary number used to determine where the row should render. > 50 will render before the facet. This value is optional.
        element : elementToReturn
    }
    // Use the populateOmniboxObject interface to push suggestions.
    populateOmniboxObject.rows.push(objectToReturn);
}

Implementing the buildOmniboxContent Function

Implement the following code. This function is required and used in the onPopulateOmnibox function to build the OmniboxComponent custom content.

This function will be custom for each custom component, and thus cannot be implemented in a general manner in this example.

What is important to know is that the Omnibox expects two things:

  1. It expects an HTML element (a div, a span, a table… Anything that is valid HTML will be added to the Omnibox). Note that if you provide really complex markup, you might need to write quite a bit of custom CSS to render it properly.
  2. If you want to be able to navigate up/down with the keyboard like for the facet suggestions, each selectable section needs to have the “coveo-omnibox-selectable” CSS class.

The Omnibox will scan for HTML sections with that CSS class in order to determine the next/previous keyboard navigation value.

function buildOmniboxContent(regex, populateOmniboxObject) {
    // This function won't be implemented in this example. Let's assume that it returns an array of values that correctly match the given regex.
    // The logic behind this function would be custom for each component.
    // For example, it could be a query to the Coveo index using the Search api
    var arrayOfValues = findValuesThatMatch(regex)
    // Let's assume that this component wants to build some kind of data table with its suggestions.
    // This is the kind of HTML structure that would indeed work very well with the Omnibox, without much customization.
    var table = $("<table></table>")
    for(var i = 0 ; i < arrayOfValues.length ; i++){
        // We won't implement this function in this example because it's highly related to your page.
        // Let's assume it returns one table row ("<tr></tr>") with the appropriate content of your choosing.
        var oneRowOfContent = buildOmniboxForOneRow(arrayOfValues[i])
        // Add the appropriate class so that this table row can be selected with the keyboard and the mouse by the user.
        $(oneRowOfContent).addClass("coveo-omnibox-selectable")
        // Add the appropriate (custom) event handler for this value.
        // The click event is triggered by the mouse when the user clicks one table row.
        // The keyboardSelect event is triggered when the user selects a value with the keyboard (arrow key up/down + enter)
        // We also use two of the helper functions provided by the Omnibox to clear its content, and then close it.
        $(oneRowOfContent).on("click keyboardSelect" , function(){
            doSomethingWhenThisValueIsSelected();
            populateOmniboxObject.clear();
            populateOmniboxObject.close()
        })
        table.append(oneRowOfContent);
    }
    return table[0];
}

Populating the Omnibox With Content From an Asynchronous Call

In some cases, you may need to fetch your Omnibox suggestions with an Ajax call to retrieve your data. In this case, you will need a JQuery Deferred Object.

function onPopulateOmnibox(e, populateOmniboxObject) {
    // Construct a new deferred object and add it to the Omnibox object. We will resolve it later when our Ajax call returns
    var deferred = $.Deferred();
    populateOmniboxObject.rows.push({deferred : deferred});
    // Let's say that we make an Ajax call to some service
    $.ajax({
        url : "http://get/some/data.com",
        success : function(results){
            // When we get our results from the service, we can build the HTML element that the Omnibox will use
            // and resolve the deferred object that we passed to the Omnibox
            var elementToReturn = buildOmniboxContent(results);
            var objectToReturn = {
                zIndex: 100, //Arbitrary number
                element : elementToReturn
            }
            deferred.resolve(objectToReturn);
        },
        error : function(){
            // Don't forget to handle failures, otherwise the Omnibox will wait indefinitely for your results
            deferred.resolve({ element : undefined})
        }
    })
}

Important Information to Remember

  • The keyboardSelect event is what is triggered by the Omnibox when a user selects your element by hitting enter on his keyboard.
  • The CSS class coveo-omnibox-selectable needs to be added to each row of our table. This is what the Omnibox uses to determine what item should be selected when navigating with the keyboard arrows.
  • The structure that is built and returned to the Omnibox can be as complex as you like and nested with any other HTML element, like a table with many rows. It might require quite a bit of CSS on your end though.
  • You can use the helper functions provided by the PopulateOmniboxObject interface to act on the content of the Omnibox and the Querybox. You can clear it, replace some parts of the query, close it, etc.