[转]SharePoint Search-as-You-Type with jQuery

Already since a long time I’ve been thinking about a web part that would search-as-you-type using SharePoint’s search engine. The idea behind this concept is that while you’re typing a query in the search box, asynchronously the query is already executed and the results are being displayed. Every time the query is changed, the results will be updated dynamically. This will allow users to see the results without going to the Search Results page, and even more: they don’t have to go back to alter their query. In such a scenario you want to avoid full page postbacks of course, so AJAX-techniques have to be used to accomplish this. A while back my first approach would be to make use of ASP.NET AJAX to build the necessary functionality in a web part for example. But during the last couple of weeks I’ve become a really big fan of using jQuery Javascript Library in SharePoint sites, and it happens to be that the search-as-you-type functionality can be created with the help of jQuery very easily. The beauty of this solution is that everything is happening on the client (in the web browser), so there is absolutely no requirement to deploy something to the server (nowadays this seems to be called ‘Assembly Free’).

Before you get too excited; it’s quite obvious that when you use this sample on a production server with lots of users, the SharePoint Search component can be hammered with lots of requests (which can be bad for the performance). So use it wisely! To minimize the impact on the performance of the server, the code will only execute a search query when (by default) three characters are entered, and while the user is still typing no queries are executed at all (there’s a configurable delay).

So how is all of this implemented? Well the idea is to display an HTML textbox in a Content Editor Web Part. Using the jQuery library, an eventhandler is added to that textbox for every keypress. When there are more than three characters (value is configurable) entered in the textbox, jQuery will make an asynchronous call to the Search web services (/_layouts/search.asmx). The resulting found items are displayed in an HTML div element which is positioned right below the textbox, on top of all other HTML elements. The user can select a result using the arrow keys, or by hovering the mouse over a result. When a result is selected by pressing the enter key or by clicking on it, the user is redirected to the corresponding page or document. You can see the code working in a web part in the following animated screenshot.

 

If you want to try this out yourself, just follow these steps. Once again, there is absolutely nothing you need to tweak and/or deploy on your server. You can do all of this through the web user interface of SharePoint.

  1. Download the code file here.
  2. Add a new Content Editor Web Part, which is available in SharePoint out-of-the-box, to a page.
  3. Modify the newly added web part, use the Source Editor button in the properties task pane to add the downloaded code.
  4. Optionally you can give the web part a meaningful Title in the Appearance group (e.g. Quick Search).

When you check out the code, notice that the first line is a reference to the jQuery library, hosted on Google’s servers. If you plan to use this in production, I’d recommend you to host the jQuery library in your own environment so you have to update the reference with your own URL.

[Update July 3th 2009] If you are using a non-English version of SharePoint, you need to change the script because by default the All Sites Search Scope is being used. On top of the script look for the quickSearchConfig variable and set the scope appropriately (you can look up the name of your Search Scopes in Central Admin/SSP). Also notice that currently using WSS is not supported (the MOSS Search Engine is used)!

var quickSearchConfig = {
    delay: 500,           
    minCharacters: 3,     
    scope: "All Sites",   
    numberOfResults: 15,  
    resultsAnimation: 200,
    resultAnimation: 0    
};

原文地址:http://weblogs.asp.net/jan/archive/2009/07/02/sharepoint-search-as-you-type-with-jquery.aspx

代码:

<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: 15, // 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>

posted @ 2011-04-02 13:47  johLife  阅读(346)  评论(0编辑  收藏  举报