Structuring a search page - Bloomreach Experience - Open Source CMS

Structuring a search page

Assumptions

There are multiple ways to structure a search page. The examples and recommendations in this documentation make the following assumptions:

  • The whole website has a header and a footer, including search pages.
  • The page center is an HTML div.
  • All dynamic content is located in the page center. 
  • Use cases and code samples are adding the following components to the center div:
    • Filter pane

    • Sort pane

    • Navigation pane

    • JFY button

    • Product grid

    • MLT buttons

Your own page structure might be different, so use your judgment about how to work with the use cases and code samples.

Adding the Search plugin to a container

Using searchConfig and the ID, brm-search, bind the Search plugin to a div container:

Add the Search plugin to a container:

$('#brm-search').brm_search(searchConfig);
Tips
1. Call this plugin only if the search container is in the page.
2. This call works best under the $(document).ready() block.
3. This call initializes the search plugin with all of the configuration values.

Rendering search results

In this section, we provide sample code to help you, as a developer, to render the search results page using the BloomReach API response, and leveraging the br-mob.js framework provided by the BloomReach Integration team. You need to modify this sample code for your website, but the examples give you an idea of how an implementation can work. 


Rendering the product grid

To render the product grid, you need to define the key functions, renderProduct and renderProducts.


Defining the renderProduct function

Given product information, the renderProduct function builds a productCell variable, which contains the appropriate HTML to display the product in the product grid. The productCell variable is appended  to the productGrid.

Define renderProduct:

/* Render an individual product */
function renderProduct(product, productGrid, brTrkData) {
    var productCell = $([
        '<a href="" onclick="return false;">',
        '<div class="product">',
        '<img src="', product.thumb_image, '" />',
        '<span class="name">', product.title, '</span>',
        '<div class="pricing">',
        '<div class="price">$', product.sale_price ? product.sale_price.toFixed(2) : product.price.toFixed(2), '</div>',
        '</div>',
        '<div class="clear"></div>',
        '</div>',
        '</a>',
        ].join(''));
    productCell.data('brTrkData', brTrkData);
    productGrid.append(productCell);
}

Defining the renderProducts function

The renderProducts function loops through a list of products and runs the renderProduct function for each of them.

Define renderProducts:

/* Render the product list */
function renderProducts(products, productGrid, brTrkData) {
    for (var i = 0; i < products.length; i++) {
        renderProduct(products[i], productGrid, brTrkData);
    }
    productGrid.append($('<div></div>', {'class': 'clear'}));
}


Rendering filters

To render filters, you need to define the following functions:

  • filterDisplayNames
  • renderFilter
  • renderFilters


Defining the filterDisplayNames function

The filterDisplayNames function allows you to specify custom names for each filter displayed to users in the fully rendered search page. As a best practice, provide user-friendly names for these filters. For example, use color for the filter, color_group.

Define filterDisplayNames:

/* display names for filters */
var filterDisplayNames = {
  "color_group": "color"
}


Defining the renderFilter function

The renderFilter function creates a block of content in HTML for particular filter options.

Define renderFilter:

/* Render an individual filter */
function renderFilter(filter, currentFilters, container) {
  var displayName = filterDisplayNames[filter.name] || filter.name;
  displayName = displayName[0].toUpperCase() + displayName.substr(1);
   
  if (currentFilters && currentFilters[filter.name]) {
    var filterContainer = $('<span></span>', {'class': 'filter'});
    filterContainer.append(displayName + ': ' + currentFilters[filter.name].value + ' ');
    var clearButton = $('<a href="' + currentFilters[filter.name].removalUrl + '">[X]</a>');
    filterContainer.append(clearButton);
    container.append(filterContainer);
    clearButton.on('click', function(event) {
      location.hash = currentFilters[filter.name].removalUrl;
      event.preventDefault();
      return false;
    });
  } else {
    var renderedHTML = [
                         '<select>',
                         '<option value="">', displayName, '</option>'
                       ];
    for (var i = 0; i < filter.options.length; i++) {
      renderedHTML.push(['<option value="', filter.options[i].value, '" data-url="', filter.options[i].url, '">', filter.options[i].label,'</option>'].join(''));
    }
    renderedHTML.push('</select>');
    container.append($(renderedHTML.join('')));
  }
}


Define the renderFilters function

The renderFilters function iterates through the list of available filters in the br-mob.js response, and builds the appropriate HTML content for each filter option.

Define renderFilters:

/* Render the group of filters */
function renderFilters(availableFilters, currentFilters, filtersContainer) {
  filtersContainer.html('');
   
  var controlGroup = $('<fieldset style="text-align: center"></fieldset>');
  filtersContainer.append(controlGroup);
   
  for (var i = 0; i < availableFilters.length; i++) {
    renderFilter(availableFilters[i], currentFilters, controlGroup);
  }
   
  filtersContainer.trigger('create');
  filtersContainer.off('change');
  filtersContainer.on('change', function(event) {
    location.hash = $(event.target[event.target.selectedIndex]).data('url');
  });
}


Rendering sorts

To render sorts, you need to define the following functions:

  • sortDisplayNames
  • renderSort

Define the sortDisplayNames function

The sortDisplayNames function allows you to specify custom names for each sort option displays to users in the fully rendered search page. As a best practice, provide user-friendly names for these sort options. For example, use "Price: Low to High" for the sort option, sale_price asc.

Define sortDisplayNames

/* display names for sorting control */
/* any sorting controls without a name is disabled */
var sortDisplayNames = {
  "sale_price asc": "Price: Low to High",
  "sale_price desc": "Price: High to Low",
  // there is typically no reason to sort by list price (price asc, price desc)
  "launch_date desc": "Newest",
  // "launch_date asc": "Oldest" is available, but is a questionable use case
  "relevance": "Relevance"
};


Define the renderSort function

The rendersort function creates a block of content in HTML for all of the sort options available for search results.

Define renderSort

/* Render the sorting control */
function renderSort(availableSorts, currentSort, sortContainer) {
  sortContainer.append('<br>Sort by: ');
  var renderedHTML = ['<select>'];
  for (var i = 0; i < availableSorts.length; i++) {
    if (sortDisplayNames[availableSorts[i].value]) {
      var selectOption = ['<option value="', availableSorts[i].value, '" data-url="', availableSorts[i].url, '"'];
      if (availableSorts[i].value === currentSort) {
        selectOption.push(' selected="selected"');
      }
      selectOption = selectOption.concat(['>', sortDisplayNames[availableSorts[i].value], '</option>']);
      renderedHTML.push(selectOption.join(''));
    }
  }
  renderedHTML.push('</select>');
  sortContainer.append($(renderedHTML.join('')));
}


Rendering the full search results page

After you finish defining the rendering functionality for each component of the search results page, you can write the logic to fully render the search results page between the header and the footer of the page.

Render the search results page:

/* Render the entire page of search results. */
function renderResultsPage(results) {
    window.scrollTo(0, $('#page').offset().top); // scroll to top
    var resultsContainer = $('#brm-results');
    if (!resultsContainer.length) {
        resultsContainer = $('<div id="brm-results"></div>');
        brmSearchContainer.append(resultsContainer);
    }
    resultsContainer.html('');
  
    /* Filters */
    var filtersContainer = $('#brm-filters');
    if (!filtersContainer.length) {
        filtersContainer = $('<div id="brm-filters"></div>');
        resultsContainer.append(filtersContainer);
    }
    renderFilters(results.availableFilters, results.currentFilters, filtersContainer);
  
    /* Sorting */
    renderSort(results.availableSorts, results.currentSort, $('fieldset', filtersContainer));
  
    /* Products */
    var productGrid = $('<div></div>', { class: 'productgridpage' });
    resultsContainer.append($('<div></div>', { class: 'productgrid' }).append(productGrid));
    renderProducts(results.products, productGrid, results.brTrkData);
  
    /* View More Button */
    var loadMoreButton = $('<a href="#" class="viewmore" onclick="return false;">View More</a>');
    loadMoreButton.on('click', function(event) {
        brmSearchContainer.brm_search('getNextPage', function(results) {
            renderProducts(results.products, productGrid, results.brTrkData);
        });
        event.preventDefault();
        return false;
    });
    resultsContainer.append(loadMoreButton);
  
    brmSearchContainer.trigger('brm-rendered');
}

 

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?