新闻搜索版块
老规矩,先贴下web service的地址:http://local.yahooapis.com/LocalSearchService/V3/localSearch?appid=thunderboltsoft&zip=94306&results=10&query=ajax
这个版块不像前面的自动填充那个,这个是通过你输入的关键字,然后帮你查找新闻,在把这些新闻填充到下面的DIV中。
一。 主界面search.jsp
代码清单8-18 search.jsp
< div id ="searchRoot" style ="left:500px; top:450px;" >
< div id ="searchHandle" >
< table width ="100%" border ="0" class ="textbox" >
< tr >
< td align ="left" class ="controls" >
Search Powered by Yahoo!
</ td >
</ tr >
</ table >
</ div >
< div id ="searchContent" class ="searchContent" >
Search term: < input class ="normalText" type ="text" name ="searchTerm" id ="searchTerm" onfocus ="onFocusFunction('searchTerm');" />
< br />
</ div >
</ div >
下面的搜索填充框是在ajaxDashboard.jsp中
</ div >
二。 ajaxDashboard.js在页面加载是运行函数initDomDrag("searchHandle", "searchRoot");这是鼠标拖动方法,但可惜的是新闻填充的DIV是在searchRoot的外面,所以拖动的话是会跟填充框脱离的,感觉这个版块的作者跟前面的版块的作者很大区别,特别是写的js,又长有难读懂。
加载页面时,ajaxDashboard.js运行了方法
AutocompleteManager.registerHandler(new AutocompleteHandler("searchTerm", "Search", 275, 3));
这个方法的定义是在autocomplete.js中,
var AutocompleteManager = new AutocompleteManagerImpl();
AutocompleteManager是函数AutocompleteManagerImpl()的一个对象。
function AutocompleteManagerImpl() {
this.textboxes = new Object();
............................................
this.registerHandler = function(handler) {
this.textboxes[handler.id] = handler;
}
}
这个AutocompleteManager对象的初始化是通过一个Object对象textboxes保存一个AutocompleteHandler(id, url, interval, minLength) 对象,调用这个textboxes很简单,就是知道id就行了,在这个例子中,AutocompleteManager.textboxes[ searchTerm ]就能得到这个对象了,反正id是固定的,所以这个不用想太多了。
function AutocompleteHandler(id, url, interval, minLength) {
this.id = id; //标识对象的id标志
this.url = url; //用到的Servlet
this.interval = interval; //循环定时器的设定时间
this.minLength = minLength; //查询字符串最少的字节
this.lastSearchString = "";
this.timeoutID; //循环定时器开关
this.state = "stopped"; //状态
}
启动Ajax的是<input class="normalText" type="text" name="searchTerm" id="searchTerm" οnfοcus="onFocusFunction('searchTerm');"/>中的οnfοcus="onFocusFunction('searchTerm');"
我不明白作者为什么用onfocus,其实用onchange跟onkeyup这些应该也可以的,这些先不管啦,看看
function onFocusFunction(id) {
AutocompleteManager.startTheInterval(id);
}
this.startTheInterval = function(id) {
var handler = this.textboxes[id]; //取得AutocompleteHandler对象,主要是要它里面的属性状态
if(handler.state == "running") { //如果在上一次启动AJAX没完成,handler.state是running的,防止多次启动AJAX服务
return;
}
var functionString = "handleInterval('" + id + "');" ;
handler.timeoutID = window.setInterval(functionString, handler.interval); //其实这个是用一次就行了,可以用一次定时器setTimeout,而不是用循环定时器setInterval,虽然用了searchString == handler.lastSearchString的判断使Ajax服务不会重复执行,但用一次定时器setTimeout就不用加多这些东西了,也不用加多定时器开关了
handler.state = "running";
}
这个方法中,主要是启动了函数function handleInterval(id) ,
function handleInterval(id) {
var searchString = document.getElementById(id).value; //取得输入框的值
var handler = AutocompleteManager.getHandlerFor(id); //取得AutocompleteHandler对象
if(searchString.length < handler.minLength || searchString.length == 0) {//输入值长度为0或小于规定最小长度
clearPopup(); //清除popupDIV
hideAutoComplete(); //隐藏popupDIV,这两个函数很简单,就不列出了
return;
}
if(searchString == handler.lastSearchString) {//跟上次输入字符串比较,防止重复启动AJAX服务
return;
}
//Made it this far so make the request一切ok,启动AJAX服务
doRequest(id, handler.url);
//Remember the last search string保存这次输入字符串
handler.lastSearchString = document.getElementById(id).value;
}
doRequest方法
function doRequest(textboxID, url) {
setOffsets(textboxID); //这个主要是设置popupDIV的偏移量跟输入框能一致,代码是一样的,所以不贴出来
var textbox = document.getElementById(textboxID);
if(textbox.value.length > 1) { //taconite框架,没什么好讲的啦
var ajaxRequest = new AjaxRequest(url);
ajaxRequest.addNamedFormElements(textboxID);
ajaxRequest.setPostRequest(showPopup);
ajaxRequest.sendRequest();
}
else if(textbox.value.length == 0) {
clearPopup(); //清除popupDIV的内容,并设置为隐藏
}
}
function showPopup(ajaxRequest) { //设置popupDIV为显示状态
document.getElementById("popup").style.display = "";
}
js方面的主要过程就是这样啦
如果你想让popupDIV能一起随着鼠标拖动,那要注意我这里给出的修改方法了
首先把ajaxDashboard.jsp中的
<div id="popup" style="position:absolute;overflow:auto;display:none;background-color:white" >
</div>
放到search.jsp中的searchContentDIV下面,像这样
<div id="searchContent" class="searchContent">
Search term: <input class="normalText" type="text" name="searchTerm" id="searchTerm" οnfοcus="onFocusFunction('searchTerm');"/>
<br/>
</div>
<div id="popup" style="position:absolute;overflow:auto;display:none;background-color:white" >
</div>
然后在autocomplete.js中的函数function setOffsets(textboxID)中的
popup.style.left = 76 + "px";
popup.style.top = 47 + "px";
设置数字是经过本人的反复试验出来的,使popupDIV能跟输入框的偏移量一致,因为popuoDIV放在searchRootDIV中,所以也能跟着随鼠标拖动
三。 SearchServlet.java,在AJAX服务中用到的servlet
在web.xml中添加
< servlet-name > SearchServlet </ servlet-name >
< servlet-class > ajaxdashboard.servlet.SearchServlet </ servlet-class >
</ servlet >
< servlet-mapping >
< servlet-name > SearchServlet </ servlet-name >
< url-pattern > /Search </ url-pattern >
</ servlet-mapping >
代码清单8-19 SearchServlet.java
import ajaxdashboard.service.SearchService;
import java.io. * ;
import java.util.Collection;
import javax.servlet. * ;
import javax.servlet.http. * ;
public class SearchServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 3137159287689087514L ;
private static final String AJAX_JSP = " /search/searchAutocomplete.jsp " ;
protected void processRequest(HttpServletRequest request
, HttpServletResponse response)
throws ServletException, IOException {
String searchTerm = request.getParameter( " searchTerm " );
System.out.println( " ==========Searching: " + searchTerm);
SearchService searchService = new SearchService();
Collection results = searchService.search(searchTerm);
// for(Iterator it = results.iterator(); it.hasNext();) {
// System.out.println(((SearchResult)it.next()).toString());
// }
request.setAttribute( " results " , results);
request.getRequestDispatcher(AJAX_JSP).forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
跟前面几个版块一样,SearchServlet中的数据的取得主要是用到了SearchService.java,Collection results = searchService.search(searchTerm);再把results放到request变量中,传递到taconite框架页面上
代码清单8-20 SearchService.java
package ajaxdashboard.service;
import ajaxdashboard.domain.SearchResult;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
/**
*
* @author Owner
*/
public class SearchService {
private static final String YAHOO_SEARCH_URL =
" http://local.yahooapis.com/LocalSearchService/V3/localSearch "
+ " ?appid=thunderboltsoftware "
+ " &zip=94306 "
+ " &results=10 "
+ " &query= " ;
private InputStream getResultsInputStream(String searchTerm) throws Exception {
String url = YAHOO_SEARCH_URL + searchTerm;
System.out.println( " url: " + url);
HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
con.setDoInput( true );
con.setDoOutput( true );
con.setRequestMethod( " GET " );
return con.getInputStream();
}
public Collection search(String searchTerm) {
Document resultsXML = getSearchResultsXML(searchTerm);
return parseResults(resultsXML);
}
private Document getSearchResultsXML(String searchTerm) {
SAXBuilder builder = new SAXBuilder();
Document news = null ;
try {
InputStream searchResultsInputStream = getResultsInputStream(searchTerm);
news = builder.build(searchResultsInputStream);
}
catch (JDOMException ex) {
System.out.println( " Error: " + ex.toString());
}
catch (IOException ex) {
System.out.println( " Error: " + ex.toString());
}
catch (Exception ex) {
System.out.println( " Exception: " + ex.toString());
}
return news;
}
private Collection parseResults(Document results) {
Collection searchResults = new ArrayList();
Namespace ns = Namespace.getNamespace( " urn:yahoo:lcl " );
Element root = results.getRootElement();
List items = root.getChildren( " Result " , ns);
System.out.println( " res size: " + items.size());
Element item = null ;
SearchResult result = null ;
for (Iterator it = items.iterator(); it.hasNext();) {
item = (Element)it.next();
result = new SearchResult();
result.setTitle(item.getChildText( " Title " , ns));
result.setAddress(item.getChildText( " Address " , ns));
result.setUrl(item.getChildText( " Url " , ns));
searchResults.add(result);
}
return searchResults;
}
}
其实理清整个过程,理解起来很简单的,这个跟新闻标题版块一样是通过链接获取服务的,所以先确定URL,下面的步骤基本都是一样的啦
HttpURLConnection con = (HttpURLConnection)new URL(url).openConnection(); //打开连接
con.setDoInput(true); //设置连接输入功能
con.setDoOutput(true); //设置连接输出功能
con.setRequestMethod("GET"); //设置请求方式,get或post或其他的
InputStream searchResultsInputStream = con.getInputStream(); //确定连接输入数据
SAXBuilder builder = new SAXBuilder();
Document news = null;
news = builder.build(searchResultsInputStream); //用SAXBuilder对象把输入的数据转换成一定格式的文档,这里我的理解是xml文档。
//可能是用org.jdom.input.SAXBuilder转换的,所以有命名空间的话,命名空间要设置
Namespace ns = Namespace.getNamespace("urn:yahoo:lcl"); //注意这里,这个是修改过的了,源代码中用到的是错误的
Element root = results.getRootElement(); //这个是固定的,取得xml文档的root元素
List items = root.getChildren("Result", ns); //取得标记名为<Result>的子元素的数组items
for(Iterator it = items.iterator(); it.hasNext();) { //取得子元素数组items中的元素
item = (Element)it.next(); //items中的元素
item.getAttribute("id", ns) //取得该元素的某个属性值
item.getChildText("Title",ns) //取得该元素的子元素的值(前提是该子元素已经没子元素了)
}
这里用到的jdom.jar
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
//然后你可以把这些数据放到实体类的集合中,再把这个集合保存到request变量中
//像新闻标题版块那个web service是没命名空间的,所以直接用List items = root.getChildren("Result");把ns去掉就行了
四。 searchAutocomplete.jsp
代码清单8-21 searchAutocomplete.jsp
<% @ taglib uri = " http://taconite.sf.net/tags " prefix = " tac " %>
< tac:taconiteRoot >
< tac:replaceChildren contextNodeID ="popup" parseOnServer ="true" >
< c:forEach var ="result" items ="${results}" >
< div onmouseover ="hilite(this);" onmouseout ="unhilite(this);" >
< a href ="${result.url}" class ="autocomplete" target ="_blank" >
${result.title}
</ a >
</ div >
</ c:forEach >
</ tac:replaceChildren >
</ tac:taconiteRoot >
taconite框架,没什么好说的,建议大家去看看taconite框架中的例子,可以学到很多taconite框架的用法,这些都很实用的。
到这里,ajax基础教程第八章的例子,在经过查找错误,改正,对一些地方改良以后,终于完成了,运行你的例子看看吧