SharePoint Search Box for Explore Experience

Thursday, June 30, 2016

1

This post will focus on some issues that make SharePoint's Search Box webpart clumsy when used in a Explore / Discovery scenario, and how to address them:

  • Retaining active refiners between searches
  • Clearing the search keyword
  • Building a custom client-side Search Box

Empty Keyword

By default, when you hit Enter on an empty Search Box, nothing happens.  Why would anyone search for empty string any way?  But, this actually comes up a lot in the Explore experience because users are likely to start with the refiners.  Consider this flow:

  1. Land on the Explore page
  2. I browse by refining on Location = Seattle and Subject = Expenses
  3. I search for the keyword "reports", but don't find anything of interest
  4. Now, I want to go back to #2 and browse the rest of Seattle/Expense items

Without the ability to search for the empty string (or a "clear" button), the only way to do #4 would be to reload the page and re-apply the Seattle/Expense filters.

Luckily, the Search Box webpart supports empty string search-- it's just disabled by default.  It is controlled by the AllowEmptySearch property, which AFAIK can not be managed from the UI.  To set this property, export your Search Box, and edit the .webpart file in a text editor:

Import it back to the page, and you are good to go!

Preserving Selected Refiners

A bigger problem is that when you search for a new keyword, all refiners are reset.  Consider this use case:

  1. Filter on Location = Seattle, Subject = Expenses
  2. Try a bunch of different keywords within those categories

Since the Search Box clears existing refinements on submit, I'd have to reapply Seattle/Expenses refiners after every new keyword.  So, this is not usable at all.

We can partially solve this by setting the MaintainQueryState property to True (False by default).  Again, this is not available from the UI.  You have to export and re-import the webpart:

This retains selected refiners between searches, but introduces a new problem.  It maintains ALL query state, including paging, which gives unexpected results in this specific scenario:

  1. Search for keyword A
  2. Go to next page of results.  The query state is now on page 2.
  3. Search for keyword B
  4. If keyword B has only one page of results, you will see NO results because the query state is still on page 2.  Worse, the paging controls aren't available because it thinks there are no results at all!

What we really want is to retain selected refiners, but start back at page 1.  I can not figure out any way to achieve this using the OOB Search Box webpart (would love to hear if anyone else has).  Which brings me to the more interesting part of this article...

Custom Javascript Search Box

The goal is to have a textbox that does the following on every submit:

  • Search for entered keyword
  • Retain selected refiners
  • Start at page 1
  • Accepts empty keyword (clear keyword)

This can easily be achieved with just Javascript and a Content Editor Webpart.  This custom JS approach also opens the door to other custom behaviors.  Go crazy.  The sky's the limit!

To be clear, the OOB Search Box comes with other goodies like query suggestions and scope selection.  However, those often are not needed on an Explore page.  I feel it's more important to perfect the basic filter/search use cases. 

You can drop the following markup into a Content Editor Webpart, which does the following:

  1. Create a text box (line 1)
  2. Attach an event handler (keyup) for the Enter key (line 5)
  3. Get a reference to the search DataProvider on the page (lines 8-9)
  4. Set query state's keyword to value from textbox, and paging to first page (lines 13-14)
  5. Trigger a query (line 16)
  6. If there's a Search Results or Content Search webpart on the page, it should automatically pick up the new results.

<input id="search-box" type="text" placeholder="Search..." />

<script type="text/javascript">
  jQuery(document).ready(function () {
    jQuery('#search-box').keyup(function (event) {
      if (event.keyCode == 13) { // Enter key
        if (Srch.ScriptApplicationManager) {
          var appManager = Srch.ScriptApplicationManager.get_current();
          var provider = appManager.queryGroups['Default'].dataProvider;

          var keyword = jQuery('#search-box').val();

          provider.get_currentQueryState().s = 0; // page = 0
          provider.get_currentQueryState().k = keyword; // new keyword

          provider.issueQuery(); // execute search
        }

        return false;
      }
    });
  });
</script>