本功能实现有些复杂,涉及的知识点:
*java的XML遍历——SAX实现
*java的XML更新——DOM实现
*HTML页面js的无刷新提交数据——XMLHTTP实现,需要MSXML2支持
*js的XML遍历——XMLDOM实现,需要Microsoft插件支持
*HTML及JSP页面让cache立即过期的代码实现
第三和第四点也是实现无刷新聊天室的核心技术!!!
总共包括四个文件:
tree2.htm——树型结构的客户端显示,用户的所有操作(打开、增加和删除节点)都从这个页面提交
tree2.xml——树型的数据和结构
tree2.jsp——XML树型结构的遍历
do_tree2.jsp——树型节点的增加与删除操作(测试用,如果只是显示树型,可以不要)
几点说明:
1、这个功能实现脱离了数据库DB,其数据来源是XML文件,tree2.xml文件的基本结构如下:
<?xml version="1.0" encoding="GB2312"?><!--XML的版本及编码方式申明-->
<root><!--XML的根节点,是XML文件必须的,根树型结构的实现关系不大-->
<xiruo parentid="0"><!--树型结构节点及父节点ID号-->
<id>2</id><!--树型节点ID号-->
<message>2</message><!--树型节点显示的信息-->
<childcount>0</childcount><!--本节点所包含的子节点数目-->
</xiruo>
</root>
2、HTML及JSP页面让cache立即过期的代码实现
HTML:
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="1970-1-1 00:00:00">
JSP:
response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
3、方便起见,所有的代码都在JSP中实现,很多功能可以自己做成javabean
4、为了清楚说明引用类的完整路径,所有的类都以完整路径引用
5、本文的版权属于beyond_xiruo,如果需要转载请注名出处
6、希望本文对你的学习有帮助,如果有什么不明白的地方可以来信,我的技术邮箱[email protected]
********************
* tree2.htm *
********************
<title>用jsp+js+xml种树</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="1970-1-1 00:00:00"><!--HTML让页面立即过期的实现-->
<style><!--页面mouseover,mouseout,mousedown不同效果的样式单的定义-->
BODY
{
font-family:宋体;
cursor:default;
font-size:9pt;
}
table
{
font-family:宋体;
cursor:default;
font-size:9pt;
}
.container {
position:relative;
top:4px;
padding:1px;
padding-top:5px;
width:13px;
height:13px;
border:solid 1px black;
background-color:#ffffff;
}
SPAN.clsCollapse
{
top:3px;
padding-left:1px;
padding-top:26px;
overflow:hidden;
line-height:3px;
font-size:13px;
}
SPAN.clsExpand
{
padding-left:1px;
overflow:hidden;
line-height:3px;
font-size:13px;
padding-top:3px;
}
SPAN.clsLeaf
{
top:0px;
padding-left:3px;
overflow:hidden;
line-height:0px;
font-size:11px;
cursor:normal;
}
SPAN.clsLabel
{
height:17px;
overflow:hidden;
padding-left:3px;
padding-top:2px;
border:1px solid #FFFFFF
}
SPAN.clsMouseOver
{
position:relative;
height:17px;
overflow:hidden;
padding-left:3px;
padding-top:2px;
background-color:#CCCCCC;
border:1px solid #999999;
cursor:hand;
}
SPAN.clsMouseOut
{
position:relative;
height:17px;
overflow:hidden;
padding-left:3px;
padding-top:2px;
background-color:#CCCCCC;
border:1px solid #FFFFFF;
}
SPAN.clsMouseDown
{
position:relative;
height:17px;
overflow:hidden;
padding-left:3px;
padding-top:2px;
background-color:#999999;
border:1px solid #999999;
}
SPAN.clsCurrentHasFocus
{
position:relative;
height:17px;
overflow:hidden;
padding-left:3px;
padding-top:2px;
background-color:#FFFFFF;
border:1px solid #999999;
cursor:hand;
}
SPAN.clsNotReady
{
position:relative;
height:17px;
overflow:hidden;
padding-left:3px;
padding-top:2px;
background-color:#CCCCCC;
border:1px solid #FFFFFF;
}
DIV.focusColor {backgroud-color:red}
DIV.normalColor {backgroud-color:green}
</style>
<script language="JavaScript">
<!--
function SelectStart()
{
window.event.cancelBubble = true;
window.event.returnValue = false;
return false;
}
function onMouseOver()
{
if(window.event.srcElement.tagName=="SPAN" && window.event.srcElement.className=="clsLabel")
{
window.event.srcElement.className="clsMouseOver"
window.event.srcElement.title=window.event.srcElement.innerText
}
}
function onMouseOut()
{
if(window.event.srcElement.tagName=="SPAN" && window.event.srcElement.className=="clsMouseDown")
{
window.event.srcElement.className="clsCurrentHasFocus"
return
}
if(window.event.srcElement.tagName=="SPAN" && window.event.srcElement.className=="clsMouseOver")
window.event.srcElement.className="clsLabel"
}
function onMouseDown()
{
for(i=0;i<document.getElementsByTagName("SPAN").length;i++)
{
if(document.getElementsByTagName("SPAN")[i].className=="clsCurrentHasFocus") document.getElementsByTagName("SPAN")[i].className="clsLabel";
}
if(window.event.srcElement.tagName=="SPAN" && (window.event.srcElement.className=="clsMouseOver" || window.event.srcElement.className=="clsLabel"))
{
window.event.srcElement.className="clsMouseDown";
parentid.value=window.event.srcElement.id;
}
}
window.document.onmouseover = onMouseOver;
window.document.onmouseout = onMouseOut;
window.document.onmousedown = onMouseDown;
var oDiv;
var xmlhttp;
var np;
function window.onload() {
readXML(0);
}
function readXML(id) {
oDiv = document.createElement("DIV");
nP = parseInt(document.all["pid"+id].style.paddingLeft);
oDiv.innerHTML = "<div style='display:;padding-left:"+nP+"px'><span class='container'><span class='clsLeaf'>.</span></span> " + "<span class='clsNotReady' style='color:#000000;'>正在装载栏目数据,请稍侯.......</span></div>"
oDiv.style.paddingLeft= nP + "px";
document.all["pid"+id].appendChild(oDiv);
var str="parentid="+id;
xmlhttp=new ActiveXObject("MSXML2.XMLHTTP");//提取树型结构数据的核心就在这里
xmlhttp.onreadystatechange=getReady;
xmlhttp.open("get","tree2.jsp?"+str,true);//提交数据到tree2.jsp
xmlhttp.send();
}
function getReady() {
if(xmlhttp.ReadyState==4) {
if(xmlhttp.status==200) {
var xmldom = new ActiveXObject("Microsoft.XMLDOM")
xmldom.loadXML(xmlhttp.responseText);//当tree2.jsp操作完成,用xmldom获取这些xml数据
if(xmldom==null||xmldom.documentElement==null) {
oDiv.innerHTML="<div style='display:;padding-left:"+nP+"px'><span class='container'><span class='clsLeaf'>.</span></span> " + "<span class='clsNotReady'>抱歉,装载数据失败。原因:返回的数据不是一个XML结构的文档。</span>< /div>";
return;
}
var nodes = xmldom.documentElement.selectNodes("/root/xiruo");
if(nodes == null ){
oDiv.innerHTML = "<div style='display:;padding-left:"+nP+"px'><span class='container'><span class='clsLeaf'>.</span></span> " + "<span class='clsNotReady'>抱歉,装载数据失败。原因:没有返回正确的XML结构格式。</span>< /div>";
return;
}
var str = "";
for(var i=0;i<nodes.length;i++) {
parentid.options.add(new Option(nodes[i].selectSingleNode("id").text,nodes[i].selectSingleNode("id").text));
if(nodes[i].selectSingleNode("childcount").text!='0') {
str += "<div style='display:;padding-left:"+nP+"px' id='pid" + nodes[i].selectSingleNode("id").text + "'><nobr><span class='container'><span class='clsCollapse' status='' onclick='hideshow(this,\"pid" + nodes[i].selectSingleNode("id").text +"\")'>+</span></span> ";
str += "<span class='clsLabel' id='"+nodes[i].selectSingleNode("id").text+"'>"+ nodes[i].selectSingleNode("message").text + " id="+nodes[i].selectSingleNode("id").text+"</span></nobr></div>";
} else {
str += "<div style='display:;padding-left:"+nP+"px' id='pid" + nodes[i].selectSingleNode("id").text + "'><nobr><span class='container'><span class='clsLeaf' onclick='hideshow(this,\"pid"+ nodes[i].selectSingleNode("id").text +"\")'>.</span></span> "
str += "<span class='clsLabel' id='"+nodes[i].selectSingleNode("id").text+"'>"+ nodes[i].selectSingleNode("message").text + " id="+nodes[i].selectSingleNode("id").text+"</span></nobr></div>";
}
}
str+="";
oDiv.innerHTML = str;
} else {
oDiv.innerHTML = "<div style='display:;padding-left:"+nP+"px'><span class='container'><span class='clsLeaf'>.</span></span> " + "<span class='clsNotReady'>抱歉,装载数据失败。</span></div>";
}
}
}
function hideshow(o,oId)
{
var subjectid = oId.substr(3,oId.length)
if (o.status=="")
{
readXML(subjectid)
o.innerText="-";
o.status="old";
return;
}
var oChild = null
for(var i=0;i<document.all[oId].childNodes.length;i++)
{
if(document.all[oId].childNodes(i).tagName=="DIV") oChild = document.all[oId].childNodes(i)
}
if(oChild==null) return
if(oChild.style.display=="")
{
o.innerText="+";
oChild.style.display="none";
}
else
{
o.innerText="-";
oChild.style.display="";
}
event.returnValue=false;
return false;
}
function addTreeNode() {//添加节点
var str="parentid="+parentid.value+"&message="+message.value+"&action=add";
pid0.innerText="";
parentid.innerText="";
var xmlhttp=new ActiveXObject("MSXML2.XMLHTTP");
xmlhttp.onreadystatechange=function addReady(){
if(xmlhttp.ReadyState==4&&xmlhttp.status==200) {
if(xmlhttp.responseText.indexOf("error")!=-1) {
var oDiv = document.createElement("DIV");
pid0.appendChild(oDiv);
oDiv.innerHTML="<div style='display:;padding-left:"+nP+"px'><span class='container'><span class='clsLeaf'>.</span></span> " + "<span class='clsNotReady'>抱歉,节点添加失败。</span></div>";
} else {
parentid.options.add(new Option("0","0"));
readXML(0);
}
}
};
xmlhttp.open("get","do_tree2.jsp?"+str,true);
xmlhttp.send();
}
function deleteTreeNode() {//删除节点
if(parentid.value=="0") {
alert("不存在ID为0的节点!");
return;
}
var str="parentid="+parentid.value+"&action=delete";
pid0.innerText="";
parentid.innerText="";
var xmlhttp=new ActiveXObject("MSXML2.XMLHTTP");
xmlhttp.onreadystatechange=function addReady(){
if(xmlhttp.ReadyState==4&&xmlhttp.status==200) {
if(xmlhttp.responseText.indexOf("error")!=-1) {
var oDiv = document.createElement("DIV");
pid0.appendChild(oDiv);
oDiv.innerHTML="<div style='display:;padding-left:"+nP+"px'><span class='container'><span class='clsLeaf'>.</span></span> " + "<span class='clsNotReady'>抱歉,节点删除失败。</span></div>";
} else {
parentid.options.add(new Option("0","0"));
readXML(0);
}
}
};
xmlhttp.open("get","do_tree2.jsp?"+str,true);
xmlhttp.send();
}
//-->
</script>
<table>
<tr><td height="300" valign="top">
<div id="pid0" style="padding-left:6px"></div>
</td></tr>
<tr><td valign="top">
parentid:
<select name="parentid" id="parentid">
<option value="0">0</option>
</select>
0-表示根节点,
message:<input type="text" name="message">
<input type="button" value="add" onClick="addTreeNode();"> <input type="button" value="delete" onClick="deleteTreeNode();">
</td></tr></table>
********************
* tree2.jsp *//这个是遍历并返回xml数据的文件
********************
<%@ page contentType="text/xml; charset=gb2312" language="java" import="java.sql.*" errorPage="" %>
<%!//方便起见就不写成javabean了
class myHandler extends org.xml.sax.helpers.DefaultHandler {//继承org.xml.sax.helpers.DefaultHandler,然后通过SAX遍历XML
String s="",parentid="",tarValue="";
boolean isxiruo=false;
public myHandler(String parentid) {
this.parentid=parentid;
}
public void startDocument() {
s+="<?xml version=\"1.0\" encoding=\"gb2312\"?>\r\n\r\n<root>\r\n";
}
public void startElement(String namespaceURI,String localName,String qName,org.xml.sax.Attributes attr) {
if(qName.equals("xiruo")) {
if(attr.getValue("parentid").equals(parentid)) {
isxiruo=true;
s+="\t<xiruo parentid=\""+parentid+"\">\r\n";
} else
isxiruo=false;
}
}
public void endElement(String namespaceURI,String localName,String qName) {
if(isxiruo) {
if(qName.equals("xiruo"))
s+="\t</xiruo>\r\n";
else
s+="\t\t<"+qName+">"+tarValue+"</"+qName+">\r\n";
}
}
public void endDocument() {
s+="</root>";
}
public void characters(char[] ch,int start,int length) {
tarValue=new String(ch,start,length);
}
public String getXML() {
return this.s;
}
}
class buildXML {
public buildXML(javax.servlet.http.HttpServletRequest request,javax.servlet.jsp.JspWriter out) throws Exception {
if(request.getParameter("parentid")==null)return;
javax.xml.parsers.SAXParserFactory spf=javax.xml.parsers.SAXParserFactory.newInstance();
javax.xml.parsers.SAXParser sp=spf.newSAXParser();
org.xml.sax.XMLReader xmlreader=sp.getXMLReader();
myHandler h=new myHandler(request.getParameter("parentid"));//通过构造器输入过滤条件,实现SAX的的XML遍历
xmlreader.setContentHandler(h);
xmlreader.parse(request.getRealPath("tree\\tree2.xml"));//我的tree2.xml文件放在tree目录下,如果放在别的地方,需要改这个路径
out.println(h.getXML());//返回SAX遍历的结果,这里返回的是一个XML的数据结构
}
}
%>
<!--以上可以写成javabean-->
<%
response.setHeader("Cache-Control","no-cache"); //HTTP 1.1//JSP让页面立即过期的实现
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
new buildXML(request,out);//通过buildXML的构造器打印xml数据
%>
********************
* do_tree2.jsp *
********************//这个是添加和删除节点操作的dom实现,实现过程有些复杂,因为是测试用跟树型结构的显示无关,因此可以不必深究
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" errorPage="" %>
<%!//方便起见,就不写成javabean了
class buildXML {
String parentid="0";
String message="";
String action="";
boolean getField(javax.servlet.http.HttpServletRequest request) throws Exception {
if(request.getParameter("parentid")==null||request.getParameter("parentid").equals("")||request.getParameter("action")==null)
return false;
this.parentid=request.getParameter("parentid");
this.action=request.getParameter("action");
if(request.getParameter("message")!=null)
this.message=new String(request.getParameter("message").getBytes("ISO8859_1"),"gb2312");
return true;
}
void init(javax.servlet.http.HttpServletRequest request,javax.servlet.jsp.JspWriter out) throws Exception {
if(!getField(request)) {
out.println("error");
return;
}
if(action.equals("add"))addTreeNode(request);
else if(action.equals("delete"))deleteTreeNode(request);
}
//增加节点
private void addTreeNode(javax.servlet.http.HttpServletRequest request) throws Exception {
int maxid=0;
javax.xml.parsers.DocumentBuilderFactory dbf=javax.xml.parsers.DocumentBuilderFactory.newInstance();
javax.xml.parsers.DocumentBuilder db=dbf.newDocumentBuilder();
org.w3c.dom.Document document=db.parse(request.getRealPath("tree\\tree2.xml"));
org.w3c.dom.Element element=document.getDocumentElement();
org.w3c.dom.NodeList nl=element.getElementsByTagName("xiruo");
for(int i=0;i<nl.getLength();i++) {//这个循环为了得到最大的ID号,让增加的节点ID在这个最大ID号基础上加1
int curid=0;
org.w3c.dom.Element ele = (org.w3c.dom.Element) nl.item(i);
org.w3c.dom.NodeList nl1=ele.getElementsByTagName("id");
if(nl1.getLength()==1) {
org.w3c.dom.Element e=(org.w3c.dom.Element)nl1.item(0);
org.w3c.dom.Text text=(org.w3c.dom.Text)e.getFirstChild();
curid=Integer.parseInt(text.getNodeValue());
maxid=maxid>curid?maxid:curid;
}
nl1=ele.getElementsByTagName("childcount");
if(nl1.getLength()==1&&curid==Integer.parseInt(parentid)) {//所要增加节点的根节点childcount加1
org.w3c.dom.Element e=(org.w3c.dom.Element)nl1.item(0);
org.w3c.dom.Text text=(org.w3c.dom.Text)e.getFirstChild();
text.setNodeValue(String.valueOf(Integer.parseInt(text.getNodeValue())+1));
}
}
org.w3c.dom.Element newElement=document.createElement("xiruo");
element.appendChild(newElement);
newElement.setAttribute("parentid",parentid);
org.w3c.dom.Element e=document.createElement("id");
e.appendChild(document.createTextNode(String.valueOf(maxid+1)));
newElement.appendChild(e);
e=document.createElement("message");
e.appendChild(document.createTextNode(message));
newElement.appendChild(e);
e=document.createElement("childcount");
e.appendChild(document.createTextNode("0"));
newElement.appendChild(e);
javax.xml.transform.dom.DOMSource domsource=new javax.xml.transform.dom.DOMSource(document);
javax.xml.transform.TransformerFactory tf=javax.xml.transform.TransformerFactory.newInstance();
javax.xml.transform.Transformer t=tf.newTransformer();
javax.xml.transform.stream.StreamResult streamresult=new javax.xml.transform.stream.StreamResult(request.getRealPath("tree\\tree2.xml"));
Properties properties=t.getOutputProperties();
properties.setProperty(javax.xml.transform.OutputKeys.ENCODING,"gb2312");
properties.setProperty(javax.xml.transform.OutputKeys.METHOD,"xml");
properties.setProperty(javax.xml.transform.OutputKeys.VERSION,"1.0");
properties.setProperty(javax.xml.transform.OutputKeys.INDENT,"no");
t.setOutputProperties(properties);
t.transform(domsource,streamresult);
}
//删除节点用递归
private void deleteTreeNode(javax.servlet.http.HttpServletRequest request) throws Exception {
javax.xml.parsers.DocumentBuilderFactory dbf=javax.xml.parsers.DocumentBuilderFactory.newInstance();
javax.xml.parsers.DocumentBuilder db=dbf.newDocumentBuilder();
org.w3c.dom.Document document=db.parse(request.getRealPath("tree\\tree2.xml"));
org.w3c.dom.Element element=document.getDocumentElement();
org.w3c.dom.NodeList nl=element.getElementsByTagName("xiruo");
java.util.ArrayList al=new java.util.ArrayList();
getDeleteTreeNodeI(parentid,al,nl);
java.util.Collections.sort(al);
java.util.Collections.reverse(al);
for(int i=0;i<al.size();i++)
element.removeChild(nl.item(((Integer)al.get(i)).intValue()));
javax.xml.transform.dom.DOMSource domsource=new javax.xml.transform.dom.DOMSource(document);
javax.xml.transform.TransformerFactory tf=javax.xml.transform.TransformerFactory.newInstance();
javax.xml.transform.Transformer t=tf.newTransformer();
javax.xml.transform.stream.StreamResult streamresult=new javax.xml.transform.stream.StreamResult(request.getRealPath("tree\\tree2.xml"));
Properties properties=t.getOutputProperties();
properties.setProperty(javax.xml.transform.OutputKeys.ENCODING,"gb2312");
properties.setProperty(javax.xml.transform.OutputKeys.METHOD,"xml");
properties.setProperty(javax.xml.transform.OutputKeys.VERSION,"1.0");
properties.setProperty(javax.xml.transform.OutputKeys.INDENT,"no");
t.setOutputProperties(properties);
t.transform(domsource,streamresult);
}