Contextual search in SharePoint should be easy and intuitive. Apparently it is not, and I’ve investigated this a bit in order to come up with a configurable solution using out of the box functionality.
The challenge is simple: I want to search in the context of the site I’m browsing. At the same time I want to use a specific search center I have branded and configured. Easier said than done.
My research have found that I cannot get the contextual search to work using the standard search settings without being sent to the out of the box / _layouts/15/osssearchresults.aspx. This is achieved by selecting the “Turn on the drop-down menu inside the search box and use the first Search Navigation node at the destination results page”. The dropdown, however, doesn’t show up anyway.
In this post I’m going to explain how I solved this.
The u-parameter and the {contexturl}-token
The wanted result is acheived by setting the search result page to the search results page of your search center. The u-parameter lets the search know that you want to search only beneath the url which is the value of u. The {contexturl} is actually translated to the current site’s url when you perform a search, yielding the result you are after.
Searching within the site’s context, now what?
Performing a search from the search field in the masterpage will now search only in content of the site you were standing at. But the only way of seeing that is by investigating the URL, where you’ll see the the u-parameter with the value of your site’s url.
Not very user friendly, huh? What if the user doesn’t know he’s just searching within the context of the site he searched from? What if the user wants to search everything again?
Notification and a way out
The way I’ve solved this is through javascript on the search results page which checks for the u-parameter and adds a message to the page if it is present. This could be added in a script snippet web part or added to the masterpage directly. In my case I had several search result pages I wanted this to happen on, and I found it easier to just add the javascript to the masterpage instead of adding it to each results page. The result looks like the following.
From the masterpage I’m calling Pzl.Search.AddContextualMessage() which does the work.
Pzl.Search.AddContextualMessage = function () { //Check if the current page is the results page and the u-parameter is present var url = decodeURIComponent(window.location.href.toLowerCase()); if (url.indexOf('results.aspx') > -1 && (url.indexOf('?u=') > -1 || url.indexOf('&u=') > -1)) { //Get the site the search initiated from var contextualUrl = getParameterByName('u').toLowerCase(); //Create the 'search in everything' URL but removing unnecessary parameters var searchAllUrl = url.replace('&u=' + contextualUrl, '').replace('?u=' + contextualUrl + '&', '?').replace('?u=' + contextualUrl, '?'); //Create the contextual search html message var contextMessage = '<div class="contextualMsg ms-textLarge ms-srch-result-noResultsTitle">Du søker nå innenfor området <a href="' + contextualUrl + '">' + contextualUrl + '</a>. Du kan <a href="' + searchAllUrl + '">utvide søket</a> for å søke i alt.</div>'; //Wait until the search results are returned as they are loaded async (this could be done in a smoother way) setTimeout(function () { if (jQuery('.ms-srch-result .contextualMsg').length <= 0) { jQuery('.ms-srch-result').prepend(contextMessage); } }, 500); } };
The javascript should explain itself (especially with the inline comments, lol), but basically it checks if it’s a contextual search, grabs the contextual URL, creates the “search all”-URL, creates the contextual message-HTML and finally adds it to the top of the search results.
I thought I was ready to wrap up this functionality, but then I discovered an interesting feature/bug. On successive searches, the message is not displayed even though we’re still searching with the u-parameter. What’s up with that?
Adding the message after successive searches
It was apparent that my javascript did not run on successive searches. The reason is simple: SharePoint loads new search results asynchronously, meaning that my javascript method would not be called when the user searches for something else. I investigated the search input field and found that onkeypress the following javascript is called.
if (Srch.U.isEnterKey(String.fromCharCode(event.keyCode))) { $getClientControl(this).search(this.value);return Srch.U.cancelEvent(event); }
As you see, the enter keypressevent is canceled because SharePoint wants to load the results its own way – fair enough and we all like fewer page loads, right?
To work around this issue I hooked into the Srch.U.cancelEvent function and called my Pzl.Search.AddContextualMessage function from there after the event is canceled. I do this by overwriting the cancelEvent-function in the method Pzl.Search.QueueAddContextualMessageOnNewSearches(). This method looks like the following
Pzl.Search.QueueAddContextualMessageOnNewSearches = function() { var orginalCancelSearchEvent = Srch.U.cancelEvent; Srch.U.cancelEvent = function (event) { orginalCancelSearchEvent(event); Pzl.Search.AddContextualMessage(); }; };
That sealed the deal. Contextual search in SharePoint is now a little bit better in my opinion.
PS: My colleague Trond Øivind Eriksen suggested that the logic for adding the contextual message could also be added as part of the Control display template of the search results webparts, which I think is a good idea. Then you shouldn’t have to worry about how SharePoint load new searches, and can kill the QueueAddContextualMessageOnNewSearches function.