Some time ago I worked on a listing page in SharePoint online and I had to customise the way the search results were displayed. One of the solutions would have been to create a new result type and define the display template for it. I ended up discarding this because I would have needed to duplicate the result types I had already to apply the change in all of them. Finally, I decided to investigate how the search controls are rendered by default, and whether I would be able to execute custom code in between.
This blog post is a summary of what I found, a sneak peek into the JavaScript library search.clientcontrols.js. This library defines the namespace “Srch” and contains the methods that search related controls (refiners, search box, search results…) execute.
In my case, I included some custom functionality in a control display template, and I configured the search results component to use it. The general idea is to load the control you want to customise and execute custom code after they are rendered through the events provided by the control.
Get page search controls
Using a display template, it is pretty easy to get the current control, using the context. Use the following:
AddPostRenderCallback(ctx, function()
{
var ctrlId = ctx.ClientControl.get_id();
var currentControl = $find(ctrlId);
}
To get any other control, you can use the following method:
function getSearchComponentId(componentType)
{
for (var prop in Sys.Application._components)
{
if (Sys.Application._components[prop] instanceof componentType)
return prop;
}
return null;
}
$find(getSearchComponentId(Srch.Result)) //Search results webpart
$find(getSearchComponentId(Srch.SearchBox)) //Search box webpart
$find(getSearchComponentId(Srch.Refinement)) //Search refiners webpart
Customising search results
Search results control calls an event called "resultRendered" every time a new page is rendered. With the method add_resultRendered you can push a function that will be executed when the event is fired:
var resultsWPObj = $find(getSearchComponentId(Srch.Result));
resultsWPObj.add_resultRendered(function(context, eventArgs){
renderGroup(context.get_currentResultTableCollection());
});
function renderGroup(resultTables) {
var resultTable = resultTables.ResultTables.filter(function(x) {
return x.TableType == "RelevantResults";
})[0];
if(resultTable){
for (var i = 0; i < resultTable.ResultRows.length; i++) {
var currentResult = resultTable.ResultRows[i];
var idElement = currentResult.csr_id + "_itemBody";
var resultElement = document.getElementById(idElement);
var testContent = document.createElement("div");
testContent.innerHTML = "Hello world";
resultElement.appendChild(testContent);
}
}
}
Customising refiners
In a similar way, we can apply customisations to refiners without modifying the display template (perhaps recording which refiners are most used?)
var refinementWPObj = $find(getSearchComponentId(Srch.Refinement));
refinementWPObj.add_resultRendered(function(context, eventArgs){
var refinersArray = eventArgs.result.ResultTables.filter(function(x) {
return x.TableType == "RefinementResults";
})[0];
if(refinersArray){
for (var i = 0; i < refinersArray.ResultRows.length; i++) {
var currentRefiner = refinersArray.ResultRows[i];
//Your customization
}
}
});
Last step
When you have your code ready, you need a way to execute it when the page loads. You can use the following:
Sys.Application.get_events()._list.load.push(#yourFunctionName#);
And that's it. Hopefully this information is useful for somebody. Ideally we should use display templates for these customisations, but it is always good to have alternatives, right?