Friday, August 27, 2010

Using jQuery for Search in SharePoint

Hi All,

I came across to a wonderful jQuery technique for using the search web service in SharePoint. This jQuery returns the same result which you can see after hitting the search button on top of the page after giving some words to find.

This jQuery is completely configurable in the sense of taking time to return the result, number of results to be returns and so on.

To achieve this fantastic trick and to achieve the most common task done by jQuery, fetching the result and showing as a suggestion under textbox, copy below code and paste it in the content editor web part of your page.


<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
// QuickSearch v0.1
// Created by Jan Tielens, http://weblogs.asp.net/jan
// This sample code is provided on an “as is” basis and without warranty of any kind.

// *** Customizable parameters ***
var quickSearchConfig = {
delay: 500, // time to wait before executing the query (in ms)
minCharacters: 3, // minimum nr of characters to enter before search
scope: "All Sites", // search scope to use
numberOfResults: 75, // number of results to show
resultsAnimation: 200, // animation time (in ms) of the search results
resultAnimation: 0 // animation time (in ms) of individual result (when selected)
};
</script>

<style type="text/css">
.quickSearchResultDivUnselected
{
background: white;
border: 1px solid white;
margin-left: 2px;
overflow: hidden;
text-overflow: ellipsis;
}
.quickSearchResultDivSelected
{
background: #EEEEEE;
border: 1px solid Gray;
margin-left: 2px;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
<table id="quickSearchTable" class="ms-sbtable ms-sbtable-ex" border="0">
<tbody>
<tr class="ms-sbrow">
<td class="ms-sbcell">
<input style="width: 100%" id="quickSearchTextBox" class="ms-sbplain" title="Enter search words"
style="width: 170px" alt="Enter search words" maxlength="200" value="" />
</td>
<td class="ms-sbgo ms-sbcell" style="width: 14px">
<img title="Go Search" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px;
border-right-width: 0px" alt="Go Search" src="/_layouts/images/gosearch.gif" />
</td>
<td style="width: 1px">
</td>
</tr>
</tbody>
</table>
<div id="quickSearchResults" style="display: none;">
</div>

<script type="text/javascript">
var quickSearchTimer;
var quickSearchSelectedDivIndex = -1;

function showResultsDiv(text) {
var div = $("#quickSearchResults");
var prevTable = div.prev();

var divCss = {
"left": prevTable.offset().left,
"padding": 2,
"position": "absolute",
"top": prevTable.offset().top + prevTable.height() + 1,
"border": "1px solid #7f9db9",
"width": prevTable.width() - 3,
"background": "white",
"max-width": prevTable.width() - 3
};

div.css(divCss).append(text).slideDown(quickSearchConfig.resultsAnimation);
}

$(document).ready(function() {
$('#quickSearchTextBox').keyup(function(event) {
var previousSelected = quickSearchSelectedDivIndex;

// catch some keys
switch(event.keyCode) {
case 13: // enter
var selectedDiv = $("#quickSearchResults>div:eq(" + quickSearchSelectedDivIndex + ") a");
if(selectedDiv.length == 1)
window.location = selectedDiv.attr("href");
break;
case 38: // key up
quickSearchSelectedDivIndex--;
break;
case 40: // key down
quickSearchSelectedDivIndex ++;
break;
}

// check bounds
if(quickSearchSelectedDivIndex != previousSelected) {
if(quickSearchSelectedDivIndex < 0)
quickSearchSelectedDivIndex = 0;
if(quickSearchSelectedDivIndex >= $("#quickSearchResults>div").length -1)
quickSearchSelectedDivIndex = $("#quickSearchResults>div").length - 2;
}

// select new div, unselect the previous selected
if(quickSearchSelectedDivIndex > -1) {
if(quickSearchSelectedDivIndex != previousSelected) {
unSelectDiv( $("#quickSearchResults>div:eq(" + previousSelected + ")"));
selectDiv($("#quickSearchResults>div:eq(" + quickSearchSelectedDivIndex + ")"));
}
}

// if the query is different from the previous one, search again
if($('#quickSearchTextBox').data("query") != $('#quickSearchTextBox').val()) {
if (quickSearchTimer != null) // cancel the delayed event
clearTimeout(quickSearchTimer);
quickSearchTimer = setTimeout(function() { // delay the searching
$("#quickSearchResults").fadeOut(200, initSearch);
} , quickSearchConfig.delay);
}
});
});

function unSelectDiv(div) {
// first stop all animations still in progress
$("#quickSearchResults>div>div").stop(true,true);

div.removeClass("quickSearchResultDivSelected").addClass("quickSearchResultDivUnselected");
$("#details", div).hide();
}

function selectDiv(div) {
div.addClass("quickSearchResultDivSelected");
$("#details", div).slideDown(quickSearchConfig.resultAnimation);
}

function initSearch() {
// first store query in data
$('#quickSearchTextBox').data("query", $('#quickSearchTextBox').val());

// clear the results
$("#quickSearchResults").empty();

// start the search
var query = $("#quickSearchTextBox").val();
if(query.length >= quickSearchConfig.minCharacters) {
showResultsDiv("Searching ..."); // display status
search(query);
}
}

function search(query) {
quickSearchSelectedDivIndex = -1;
var queryXML =
"<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'> \
<Query domain='QDomain'> \
<SupportedFormats><Format>urn:Microsoft.Search.Response.Document.Document</Format></SupportedFormats> \
<Context> \
<QueryText language='en-US' type='STRING' >SCOPE:\"" + quickSearchConfig.scope + "\"" + query + "</QueryText> \
</Context> \
<SortByProperties><SortByProperty name='Rank' direction='Descending' order='1'/></SortByProperties> \
<Range><StartAt>1</StartAt><Count>" + quickSearchConfig.numberOfResults + "</Count></Range> \
<EnableStemming>false</EnableStemming> \
<TrimDuplicates>true</TrimDuplicates> \
<IgnoreAllNoiseQuery>true</IgnoreAllNoiseQuery> \
<ImplicitAndBehavior>true</ImplicitAndBehavior> \
<IncludeRelevanceResults>true</IncludeRelevanceResults> \
<IncludeSpecialTermResults>true</IncludeSpecialTermResults> \
<IncludeHighConfidenceResults>true</IncludeHighConfidenceResults> \
</Query></QueryPacket>";

var soapEnv =
"<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'> \
<soap:Body> \
<Query xmlns='urn:Microsoft.Search'> \
<queryXml>" + escapeHTML(queryXML) + "</queryXml> \
</Query> \
</soap:Body> \
</soap:Envelope>";

$.ajax({
url: "/_vti_bin/search.asmx",
type: "POST",
dataType: "xml",
data: soapEnv,
complete: processResult,
contentType: "text/xml; charset=\"utf-8\""
});

function processResult(xData, status) {
var html = "";
$(xData.responseXML).find("QueryResult").each(function() {
var divWidh = $("#quickSearchTable").width() - 13;

var x = $("<xml>" + $(this).text() + "</xml>");
x.find("Document").each(function() {
var title = $("Title", $(this)).text();
var url = $("Action>LinkUrl", $(this)).text();
var description = $("Description", $(this)).text()

html +=
"<div class='quickSearchResultDivUnselected' style='width:" + divWidh + "px;max-width:" + divWidh +"px'> \
<a href='" + url + "'>" + $("Title", $(this)).text() + "</a> \
<div style='display:none' id='details' style='margin-left:10px'>"
+ description +
"<br/>" + url + " \
</div> \
</div>";
});
if(x.find("TotalAvailable").text() != "")
html += "<div style='text-align:right'>Total results: " + x.find("TotalAvailable").text() + "</div>";
else
html += "<div style='text-align:right'>Total results: 0</div>";
});

$("#quickSearchResults").empty().append(html);
$("#quickSearchResults>div>a").hover(
function() { selectDiv($(this).parent()); },
function() { unSelectDiv($(this).parent()); }
);
showResultsDiv();
}
}

function escapeHTML (str) {
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
</script>


You can replace the configuration section in this script which is quickSearchConfig in the start of the script. You can also change the jQuery link at the start script tag by downloading that js file, storing in your document library and then giving url of document library till the js file.

As you can see delay, minimum number of characters to use for start calling a web service, scope, number of results and animation time are configurable options.

Here is the result out of this.



Thanks for the wonderful Post by Jan.

12 comments:

Robot with Laserbeams said...

Works great. Confused me for a moment, I clicked the magnifying glass to intiate the search. Just type in the textbox and press the spacebar.

aravind said...

But this is not working with Safari browser...

SharePoint Kings said...

sorry aravind,
we had not tried in safari browser.

just check its jquery which is not working or sharepoint it self is giving some error???

if both separately working fine then check is there any setting in browser that stop to call sharepoint webservice from javascript.

rock'n'roll star said...

This works great. Is it possible to also take the user to the search results page by clicking the magnifying glass?

Anonymous said...

This works great - however it scanned through *all* sharepoint sites and not just the list that hosts the CEWP. Is that intentionally?

Parth Patel said...

yes, its intentionally
you can change the
scope to change as per your required.

Nav said...

How does one go about changing the scope? I tried various methods to no avail.

SharePoint Kings said...

you can change
scope: "All Sites" in
// *** Customizable parameters ***

Nvz said...

Nice Article..

i want to set scope for the particular site, how can i achive..

Nvz

SharePoint Kings said...

Hi NvZ,

you can change
scope: "All Sites" in
// *** Customizable parameters ***

batalia said...

Hey guys, this is great. But as the above commenters already asked, how do I limit the scope to 'this site' as opposed to 'all sites'? The scope called 'this site' does exist, but I can't get the script to recognize it.

Thanks!

Anonymous said...

How do I change the scope do just a contact list?




Share your SharePoint Experiences with us...
As good as the SharePointKings is, we want to make it even better. One of our most valuable sources of input for our Blog Posts comes from ever enthusiastic Visitors/Readers. We welcome every Visitor/Reader to contribute their experiences with SharePoint. It may be in the form of a code stub, snippet, any tips and trick or any crazy thing you have tried with SharePoint.
Send your Articles to sharepointkings@gmail.com with your Profile Summary. We will Post them. The idea is to act as a bridge between you Readers!!!

If anyone would like to have their advertisement posted on this blog, please send us the requirement details to sharepointkings@gmail.com