Monday, September 12, 2011

Using jQuery To Ignore The OSSSearchResults.aspx Page With The SharePoint 2010 SmallSearchInputBox

Chris Domino, Director, Enterprise Architect

Whenever I see "OSSSearchResults.aspx" in my browser's address bar while developing a SharePoint site, I immediately know one of two things: the search requirements have not yet been implemented, or I'm going to get a bug that search is not working. I am a huge fan of writing custom search queries from scratch using FullTextSqlQuery behind a service and returning a custom search results DTO that I can bind to on my custom search results page. Why? Because whenever I've had any customization to do around search queries, search terms, or search results, no amount of XSLT could possibly get me there.

For this one site, our designer had already produced the styles to skin the OOTB SmallSearchInputBox so we were forced to use that, rather than a simple textbox, label, and button. I don't know if it's a bug or my ineptitude or simply a depth of search administration that I'm subconsciously unwilling to descent to, but this control is inexorably tied to OSSSearchResults.aspx. It ignores the "Site Collection Search Results Page" URL setting in the site collection "Search Settings" page, even with custom scopes turned off.

It's like this control forces us to remain stuck in "WSS" mode for search. Even when using custom scopes pointing to the default Search Center sub site, (the top radio button in the image above) which uses settings configured in Central Admin, we're unable to escape to "MOSS" mode. By "WSS" and "MOSS" modes for search, (pardon the 2007 jargon) I'm referring to the difference between having a wimpy little search page do the work verses the full power of a Search Service Application. The later gives us a really nice, configurable, enterprisey search administration experience; the former gives us some XSLT and the finger.

So how do you use the SmallSerchInputBox delegate control to redirect to a custom search results page while not losing any if it's OOTB features (such as the alert for empty searches)? You programmatically punch it in the face. A little jQuery is all we need to hijack the processing this control does and inject our own functionality to redirect to whatever page we want, customize the error handling for blank queries, handle keyboard events, etc.

Here's the JavaScript:

  1. $(document).ready(function ()
  2. {
  3. //get all search boxes
  4. $("a[title='Search']").each(function ()
  5. {
  6. //clear OOTB href assignment (which will do the redirect before the click event happens)
  7. var button = $(this);
  8. button.attr("href","#");
  9. //get textbox and remove OOTB keypress [textbox is at [button -> button's parent (=table cell) -> table cell's pevious sibling (=other table cell) -> other table cell's fist child (=textbox)]
  10. var txt = button.parent().prev().children().first();
  11. txt.attr("onkeypress", "");
  12. //hook enter key down on textbox
  13. txt.keydown(function (e)
  14. {
  15. //isolate enter
  16. if (e.keyCode == "13")
  17. {
  18. //click
  19. ProcessClick(button);
  20. }
  21. });
  22. //hook click
  23. ()
  24. {
  25. //click
  26. ProcessClick(this);
  27. });
  28. });
  29. });

Yes, Line #10 is a total hack; there is probably a much more elegant, jQuery-ish way to get a reference to the inner textbox control. Line #4 gives us support for having multiple SmallSearchInputBoxes on a single page. The logic flow itself is straight forward: for each control, get the textbox and button, unhook the existing events, and hook up our own. To that end, let's look at what the "ProcessClick" method does:

  1. function ProcessClick(button)
  2. {
  3. //get search value [a - td - td - input]
  4. var query = $(button).parent().prev().children().first().val();
  5. //validate
  6. if (query == null || query == "" || query == "<optional watermark text>")
  7. {
  8. //no query (or watermark) - mimic OOTB behavior
  9. alert("Please enter a search value.");
  10. }
  11. else
  12. {
  13. //redirect to custom seach results page
  14. window.location.href = "/_layouts/<name of project>/Pages/SearchResults.aspx?k=" + query;
  15. }
  16. }

Line #4 is the same hack as above. Line #6 provides support for watermarked textboxes. The rest is trivial: take the button sent in, get the textbox, get its value, and pass it as a query string to a custom search results page. Wrap both of these methods into a JavaScript file and reference it in your master page somewhere, and you should be good to go.

That's really the whole story: a few minutes of jQuery to circumvent a few hours of SharePoint search configuration. Have fun OSSSearching!