1. 程式人生 > >使用Ajax模仿Google suggest的搜尋提示(Java+JSP+JS實現)

使用Ajax模仿Google suggest的搜尋提示(Java+JSP+JS實現)

最近專案需要作一個類似google suggest的搜尋提示功能,在網上找了一些資料,自己弄弄就出來了。專案的關鍵是JS。

實際執行效果:
即使在輸入的時候前面有空格,也可以正確判斷。

工程結構:



Keywords.java

package com.bird.bean;

public class Keywords {
	private String content;

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public Keywords(String content) {
		this.content = content;
	}

	public Keywords() {

	}
}

DBManager.java

package com.bird.common;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DBManager {
	public DBManager() {
	}

	public static Connection getConnection() {
		// String driver = "oracle.jdbc.driver.OracleDriver";
		String driver = "com.mysql.jdbc.Driver";
		// String url = "jdbc:oracle:thin:@133.0.129.6:1521:oracle";
		String url = "jdbc:mysql://127.0.0.1:3306/itbird";
		// String username = "nrmdb";
		// String passwd = "nrmoptr";
		String username = "root";
		String passwd = "root";
		Connection connection = null;
		try {
			Class.forName(driver);
			connection = DriverManager.getConnection(url, username, passwd);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return connection;
	}

	public static void closeAll(Connection con, PreparedStatement pstmt,
			ResultSet rs) {
		try {
			if (rs != null) {
				rs.close();
			}
			if (pstmt != null) {
				pstmt.close();
			}
			if (con != null) {
				con.close();
			}
		} catch (SQLException ex) {
			ex.printStackTrace();
		}
	}
}
GetKeywordsResults.java
package com.bird.DAO;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;

import com.bird.bean.Keywords;
import com.bird.common.DBManager;

public class GetKeywordsResults {
	@SuppressWarnings("null")
	public ArrayList<Keywords> getKeywordsResults(String keyword) {
		ArrayList<Keywords> list = new ArrayList<Keywords>();
		Keywords key = null;
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		String sql = "select content from keywords where content like '%"
				+ keyword + "%'";

		try {
			conn = DBManager.getConnection();
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.executeQuery();

			while (rs.next()) {

				key = new Keywords();

				System.out.println("類似關鍵詞:" + rs.getString(1));

				key.setContent(rs.getString(1));

				list.add(key);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			DBManager.closeAll(conn, pstmt, rs);
		}

		return list;
	}
}
GetKeywordServlet.java
package com.bird.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.bird.DAO.GetKeywordsResults;
import com.bird.bean.Keywords;

public class GetKeywordServlet extends HttpServlet {

	/**
	 * 2014-12-18
	 * 
	 * @author GHYZ
	 */
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		System.out.println("123");
		resp.setContentType("text/xml;charset=UTF-8");
		resp.setHeader("Cache-Control", "no-cache");

		// save keyword
		String param = null;

		String rawQueryStr = req.getQueryString();
		String queryStr = java.net.URLDecoder.decode(rawQueryStr, "utf-8");
		String[] result = queryStr.split("=");		
		
		// keyword
		try {
			param = result[1];
			String pattern = "([\'\"-+*/^()\\]\\[])";
			param = param.replaceAll(pattern, "");
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("error input!");
			return ;
		}
		param = param.trim();

		System.out.println("--->" + param);
		ArrayList<Keywords> list = null;

		try {
			System.out.println("in list Size");
			list = new ArrayList<Keywords>();
			list = (new GetKeywordsResults().getKeywordsResults(param));
			System.out.println(list.size() + "listSize");

		} catch (Exception e) {
			System.out.println("input error!");
		}

		String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
		xml += "<message>";
		Iterator<Keywords> iter = list.iterator();
		String content;
		while (iter.hasNext()) {
			Keywords key = iter.next();
			content = key.getContent();
			xml += "<info>" + content + "</info>";
		}
		xml += "</message>";
		resp.getWriter().write(xml);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		this.doGet(req, resp);

	}

}

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<servlet>
		<servlet-name>getKeywordServlet</servlet-name>
		<servlet-class>com.bird.servlet.GetKeywordServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>getKeywordServlet</servlet-name>
		<url-pattern>/getKeywordServlet</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
</web-app>

build.js

/*
 * 判斷按鍵的JavaScript程式碼
 */

var arrOptions = new Array(); //定義一個儲存伺服器返回的陣列
var strLastValue = ""; //定義儲存每次向伺服器傳送請求的引數
var theTextBox; //定義表示文字輸入變數
var currentValueSelect = -1; //定義下拉提示框中預設的選項

window.onload = function() {	
	var elemSpan = document.createElement("span");
	elemSpan.id = "spanOutput";
	elemSpan.className = "spanTextDropdown";
	document.body.appendChild(elemSpan);
	document.Form1.txtUserInput.onkeyup = GiveOptions;
};

function GiveOptions() { //按下鍵的呼叫方法
	var intKey = -1;
	
	if (window.event) {
		intKey = event.keyCode;
		theTextBox = event.srcElement;		
	}

	if (theTextBox.value.length == 0) { //文字框內容為空		
		HideTheBox();
		strLastValue = "";
		return false;
	}

	if (intKey == 13) {
		//按下Enter鍵
		GrabHighlighted();
		theTextBox.blur();
		return false;
	} else if (intKey == 38) {
		//按下 up 鍵
		MoveHightlighted(-1);
		return false;
	} else if (intKey == 40) {
		//按下 down 鍵
		MoveHightlighted(1);
		return false;
	}
	
	//進行內容比較
	if (theTextBox.value.indexOf(strLastValue) != 0 || arrOptions.length == 0 
	|| (strLastValue.length == 0 && theTextBox.value.length > 0) 
	|| (theTextBox.value.length <= strLastValue.length) ) {
		strLastValue = theTextBox.value;
		strLastValue = strLastValue.replace(/^\s*\'|\s*$/g,""); 			
		TypeAhead(theTextBox.value);
	} else {
		BuildList(theTextBox.value);
	}

};

function removeAllSpace(str) {
	  return str.replace(/\s+/g, "");
};

function TypeAhead(xStrText) { //傳送請求方法
	xStrText = removeAllSpace(xStrText);
	if (xStrText == "") return; //判斷如果輸入框內容為空
	var url = "getKeywordServlet?param=" + xStrText; //建立傳送地址變數
	if ( window.XMLHttpRequest ) { //判斷瀏覽器
		req = new XMLHttpRequest();
	} else if (window.ActiveXObject()) {
		req = new ActiveXObject("Microsoft.XMLHTTP");
	}
	if (req) {
		req.open("GET" , url, true);  //開啟連線		
		//設定回撥函式
		req.onreadystatechange  = function() {
			if (req.readyState==4 && req.status==200) {
				parseMessage();
			}
		  };
		req.send(null); //實現傳送
	}
};

function parseMessage() { //分析伺服器返回資料
	var xmlDoc = req.responseXML.documentElement; //獲取返回的XML檔案物件
	var nodeInfo = xmlDoc.getElementsByTagName("info");  //獲取標記<info>
	arrOptions = new Array(); //建立一個數組物件
	for (var i = 0; i < nodeInfo.length; i ++) {
		arrOptions[i] = nodeInfo[i].firstChild.nodeValue; //儲存<info>到陣列物件中
	}
	BuildList(theTextBox.value);	
	strLastValue = theTextBox.value;
};

function BuildList(theText) { //建立下拉提示框方法
	//alert("buidList");
	theText = removeAllSpace(theText);
	SetElementPosition();
	var inner = "";
	var theMatches = MakeMatches(theText); //獲取索要匹配的物件
	for (var i = 0; i < theMatches.length; i++) {
		inner += theMatches[i];
	}
	if (theMatches.length > 0) {
		document.getElementById("spanOutput").innerHTML = inner;
		document.getElementById("OptionsList_0").className = "spanHighElement";
		currentValueSelect = 0;
	} else {
		HideTheBox();
	}
};

function SetElementPosition() {
	var selectedPosX = 0;
	var selectedPosY = 0;
	//建立關於提示框的長度和寬度的變數
	var theElement = document.Form1.txtUserInput;
	//var theTextBoxInt = document.Form1.txtUserInput;
	if (!theElement) { 
			alert("not found");
			return; 
		}
	//為提示框的長度和寬度賦值
	var theElemHeight = theElement.offsetHeight;
	var theElemWidth = theElement.offsetWidth;
	//設定提示框的位置
	while ( theElement != null ) {
		selectedPosX += theElement.offsetLeft;
		selectedPosY += theElement.offsetTop;
		theElement = theElement.offsetParent;
	}
	xPosElement = document.getElementById("spanOutput");
	xPosElement.style.left = selectedPosX;
	xPosElement.style.width = theElemWidth;
	xPosElement.style.top = selectedPosY + theElemHeight;
	xPosElement.style.display = "block";
};

var countForId = 0;
function MakeMatches(xCompareStr) {
	countForId = 0;
	var matchArray = new Array();
	for ( var i = 0; i < arrOptions.length; i++) {
		//遍歷arrOptions物件
		var regExp = new RegExp(xCompareStr , "ig");
		
		if ((arrOptions[i].search(regExp)) >= 0 ) {
			//當有匹配的項,呼叫CreatUnderline()方法返回字串
			matchArray[matchArray.length] = CreateUnderline(arrOptions[i], 
					xCompareStr, i);
		} else {
			continue;
		}
	}
	return matchArray;
};

function MoveHightlighted(xDir) {
	if (currentValueSelect >= 0) {
		//獲取按鍵的值
		newValue = parseInt(currentValueSelect) + parseInt(xDir);
		if (newValue > -1 && newValue < countForId) {
			currentValueSelect = newValue;
			SetHighColor(null);
		}
	}
};

function ReDraw() {
	BuildList(document.Form1.txtUserInput.value);
};

function GrabHighlighted() {
	if (currentValueSelect >= 0) {
		xVal = document.getElementById("OptionsList_" + currentValueSelect).getAttribute("theArrayNumber");
		SetText(xVal);
		HideTheBox();
	}
};

function HideTheBox() {
	document.getElementById("spanOutput").style.display = "none";
	currentValueSelect = -1;
};

var undeStart = "<span class='spanMatchText'>";
var undeEnd = "</span>";
var selectSpanStart = "<span style='width:100%;display:block;' " +
		" class='spanNormalElement' onmouseover='SetHighColor(this)'";
var selectSpanEnd = "</span>";

function CreateUnderline(xStr, xTextMatch, xVal) {
	selectSpanMid = "onclick='SetText(" + xVal + ")'" + " id='OptionsList_" + countForId + "' theArrayNumber='" + xVal + "'>";
	countForId++;
	var regExp = new RegExp(xTextMatch , "ig");
	var start = xStr.search(regExp);
	var matchedText = xStr.substring(start, start + xTextMatch.length);
	var Replacestr = xStr.replace(regExp, undeStart + matchedText + undeEnd);
	return selectSpanStart + selectSpanMid + Replacestr + selectSpanEnd;
};
function SetHighColor(theTextBox) {
	if (theTextBox) {
		currentValueSelect = theTextBox.id.slice(
			theTextBox.id.indexOf("_") + 1 , theTextBox.id.length
		);
	}
	for (var i = 0; i< countForId; i++) {
		document.getElementById('OptionsList_' + i).className = 'spanNormalElement';
	}
	document.getElementById('OptionsList_' + currentValueSelect).className = 'spanHighElement';
};
function SetText(xVal) {
	theTextBox = document.Form1.txtUserInput;
	theTextBox.value = arrOptions[xVal];
	document.getElementById("spanOutput").style.display = "none";
	currentValueSelect = -1;
};
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>Google Suggest</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type="text/javascript" src="build.js"></script>
<style type="text/css">
.spanTextDropdown {
	left: 88px;
	width: 171px;
	top: 29px;
	display: none;
	margin-left: 80px;
	border: 1px solid gray;
	margin-top: -18px;
	
}
.spanHighElement {
	background:navajowhite;
}
</style>
</head>

<body onResize="ReDraw()">
	<div>
		<form name="Form1" autocomplete="off" id="Form1">
			Text Box:
			<input type="text" name="txtUserInput" style="width: 182px;"/>
			<!-- hidden zone -->
			<input type="hidden" name="txtUserValue" id="hidden1"/>
			<!-- help text input  -->
			<input type="text" name="txtIgnore" style="display: none;"/>
		</form>
	</div>
</body>
</html>