一、效果图
下拉框中的内容为查询结果
二、标签定义代码
package com.moonNigh.tagSupport; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; /** * * @author HHB * * 联想查询自定义标签标签处理类 * */ public class AssociateQueryTag extends TagSupport { private static final long serialVersionUID = 1164199147616542853L; // 标签name树形 private String name; //引用JavaScript代码的路径 private String scriptPaht; //引用的CSS的路径 private String cssPath; //项目根路径 private String rootPath; //标签value属性 private String value; private String text; /* * 标签的actionUrl属性 * 联想查询结果数据通过向actionUrl属性指定的url请求得到 */ private String actionUrl; private int startLeng; public int getStartLeng() { return startLeng; } public void setStartLeng(int startLeng) { this.startLeng = startLeng; } private HttpServletRequest request=null; public String getActionUrl() { return actionUrl; } public void setActionUrl(String actionUrl) { this.actionUrl = actionUrl; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getScriptPaht() { return scriptPaht; } public void setScriptPaht(String scriptPaht) { this.scriptPaht = scriptPaht; } public String getCssPath() { return cssPath; } public void setCssPath(String cssPath) { this.cssPath = cssPath; } public String getText() { return text; } public void setText(String text) { this.text = text; } public String getName() { return name; } public void setName(String name) { this.name = name; } public AssociateQueryTag() { } /** * 初始化变量 */ private void initAbttributes() { request=(HttpServletRequest)this.pageContext.getRequest(); rootPath=request.getContextPath(); this.scriptPaht="/js/"; this.cssPath="/css/"; } @Override public int doStartTag() throws JspException { initAbttributes(); JspWriter out=pageContext.getOut(); try { String tName=name; //引入javascript文件 //<script type='text/javascript' src='../../js/jquery/jquery-1.4.2.min.js'></script> out.println("<script type='text/javascript' charset='UTF-8' src='"+rootPath+"/js/"+"jquery-1.3.2.js'></script>"); out.println("<script type='text/javascript' charset='UTF-8' src='"+rootPath+scriptPaht+"associater.js'></script>"); out.println("<script type='text/javascript' charset='UTF-8' src='"+rootPath+scriptPaht+"autoComplete.js'></script>"); //引入css文件 out.println("<link rel='stylesheet' href='"+rootPath+cssPath+"selector.css' type='text/css' />"); //文本输入框 StringBuilder tag=new StringBuilder("<input type='text' "); tag.append("id='").append(id).append("'"); tag.append(" value='").append(text==null?"":text).append("' autocomplete='off'"); tag.append(" onkeyup='return queryInfo(").append(startLeng).append(",\""); //tag.append(id).append("\",\"").append(path).append("\")'>"); tag.append(id).append("\",\"").append(actionUrl).append("\")'>"); //隐藏的用于保存选择结果值的标签 tag.append("<input type='hidden' name='") .append(tName).append("' id='").append(id).append("_value") .append("' value='").append(value==null?"":value).append("'>"); out.println(tag.toString()); } catch (IOException e) { e.printStackTrace(); } return SKIP_BODY; } }
三、JS代码:associater.js
function closeHandler(cal) { cal.hide(); // hide the selector } /* function selected(cal, date) { } */ /* * 联想查询文本框输入时触发事件 */ function queryInfo(starLen,id,path) { var t = ''; clearTimeout(t); t = setTimeout("query("+starLen+",'"+id+"','"+path+"')",500); return null; } function query(starLen,id,path) { var selector=null; var textBox=document.getElementById(id); var text=textBox.value; if(text.length>=starLen) { showAssociater(id,path); var selector=window["asso_"+id]; $.get(path,{"para":text}, function(data,status){ var parentNode=selector.element; while(parentNode.hasChildNodes()) { parentNode.removeChild(parentNode.firstChild); } assoXmlParser(data,parentNode,id); }); } } /* * 显示下拉框 */ function showAssociater(id,path) { var selector=window["asso_"+id]; var el = document.getElementById(id); if (selector) { // we already have some selector created //selector.hide(); // so we hide it first. } else { // first-time call, create the selector. var selec = new Associater(); selec.textId=id; selec.id="asso_"+id; selec.valueId=id+"_value"; selec.path=path; // uncomment the following line to hide the week numbers // cal.weekNumbers = false; selector = selec; // remember it in the global var selec.create(); } selector.sel = el; // inform it what input field we use // the reference element that we pass to showAtElement is the button that // triggers the selector. In this example we align the selector bottom-right // to the button. selector.showAtElement(el, "Bl"); // show the selector return false; } Associater = function () { // member variables this.activeDiv = null; this.timeout = null; this.dragging = false; this.hidden = false; this.isPopup = true; // HTML elements this.element = null; this.containFrame=null; this.textId=null; this.id=null; this.valueId=null; this.path=null; } Associater.is_ie = ( /msie/i.test(navigator.userAgent) &&!/opera/i.test(navigator.userAgent) ); Associater.getAbsolutePos = function(el) { var r = { x: el.offsetLeft, y: el.offsetTop }; if (el.offsetParent) { var tmp = Associater.getAbsolutePos(el.offsetParent); r.x += tmp.x; r.y += tmp.y; } //alert(r.x+":"+r.y); return r; }; Associater.isRelated = function (el, evt) { var related = evt.relatedTarget; if (!related) { var type = evt.type; if (type == "mouseover") { related = evt.fromElement; } else if (type == "mouseout") { related = evt.toElement; } } while (related) { if (related == el) { return true; } related = related.parentNode; } return false; }; Associater.removeClass = function(el, className) { if (!(el && el.className)) { return; } var cls = el.className.split(" "); var ar = new Array(); for (var i = cls.length; i > 0;) { if (cls[--i] != className) { ar[ar.length] = cls[i]; } } el.className = ar.join(" "); }; Associater.addClass = function(el, className) { Associater.removeClass(el, className); el.className += " " + className; }; Associater.createElement = function(type, parent) { var el = null; if (document.createElementNS) { // use the XHTML namespace; IE won't normally get here unless // _they_ "fix" the DOM2 implementation. el = document.createElementNS("http://www.w3.org/1999/xhtml", type); } else { el = document.createElement(type); } if (typeof parent != "undefined") { parent.appendChild(el); } return el; }; /** Calls the second user handler (closeHandler). * 添加事件 * */ Associater.addEvent = function(el, evname, func) { if (el.attachEvent) { // IE el.attachEvent("on" + evname, func); } else if (el.addEventListener) { // Gecko / W3C el.addEventListener(evname, func, true); } else { // Opera (or old browsers) el["on" + evname] = func; } }; Associater.stopEvent = function(ev) { if (Associater.is_ie) { window.event.cancelBubble = true; window.event.returnValue = false; } else { ev.preventDefault(); ev.stopPropagation(); } return false; }; /* * 删除事件 */ Associater.removeEvent = function(el, evname, func) { //alert(el.detachEvent); if (el.detachEvent) { // IE el.detachEvent("on" + evname, func); } else if (el.removeEventListener) { // Gecko / W3C el.removeEventListener(evname, func, true); } else { // Opera (or old browsers) el["on" + evname] = null; } }; Associater.prototype.create = function (_par) { var parent = null; if (! _par) { // default parent is the document body, in which case we create // a popup selector. parent = document.getElementsByTagName("body")[0]; this.isPopup = true; } else { parent = _par; this.isPopup = false; } var div = document.createElement("div"); /* var selectIframe = Associater.createElement("iframe"); selectIframe.style.width="100%"; selectIframe.style.height="100%"; selectIframe.style.display="block"; selectIframe.scrolling="auto"; selectIframe.style.border="0px"; selectIframe.id=this.textId+"_frm"; this.containFrame=selectIframe; //selectIframe.style.border="1px"; selectIframe.src=this.path; var span=document.createElement("span"); span.innerHTML="test"; //div.appendChild(span); this.frame=selectIframe; div.appendChild(selectIframe); */ this.element = div; div.className = "selector"; if (this.isPopup) { div.style.position = "absolute"; div.style.display = "none"; } parent.appendChild(this.element); }; Associater.prototype.destroy = function () { var el = this.element.parentNode; el.removeChild(this.element); Associater._C = null; }; /** * Hides the Associater. Also removes any "hilite" from the class of any TD * element. * 隐藏下拉框 */ Associater.prototype.hide = function () { if (this.isPopup) { var obj=this; //Associater.removeEvent(document, "keydown", Associater._keyEvent); //Associater.removeEvent(document, "keypress", Associater._keyEvent); Associater.removeEvent(document, "click", function(){obj.hide();}); } this.element.style.display = "none"; this.hidden = true; this.hideShowCovered(); }; Associater.prototype.showAt = function (x, y) { var s = this.element.style; s.left = x + "px"; s.top = y + "px"; this.show(); }; /** 下拉框相对指定控件的显示位置. */ Associater.prototype.showAtElement = function (el, opts) { var p = Associater.getAbsolutePos(el); //alert(p.x+'-'+p.y); if (!opts || typeof opts != "string") { this.showAt(p.x, p.y + el.offsetHeight); return true; } this.show(); var w = this.element.offsetWidth; var h = this.element.offsetHeight; this.hide(); var valign = opts.substr(0, 1); var halign = "l"; if (opts.length > 1) { halign = opts.substr(1, 1); } // 垂直对齐方式 switch (valign) { case "T": p.y -= h; break; case "B": p.y += el.offsetHeight; break; case "C": p.y += (el.offsetHeight - h) / 2; break; case "t": p.y += el.offsetHeight - h; break; case "b": break; // already there } // 水平对齐方式 switch (halign) { case "L": p.x -= w; break; case "R": p.x += el.offsetWidth; break; case "C": p.x += (el.offsetWidth - w) / 2; break; case "r": p.x += el.offsetWidth - w; break; case "l": break; // already there } this.showAt(p.x, p.y); }; Associater.prototype.show = function () { this.element.style.display = "block"; this.hidden = false; if (this.isPopup) { window[this.id] = this; var tId=this.id; //Calendar.addEvent(document, "keydown", Calendar._keyEvent); //Calendar.addEvent(document, "keypress", Calendar._keyEvent); Associater.addEvent(document, "click", function(){window[tId].hide();}); } this.hideShowCovered(); }; Associater._Click=function(ev,mid,srcObj) { var sel=window[mid]; if(!sel) { return false; } var obj=null; obj=ev.srcElement?ev.srcElement:srcObj; var el=obj.parentNode; while(el!=null&&el!=sel.element) { el=el.parentNode; } if(el==null) { sel.hide(); Associater.stopEvent(ev); } }; Associater.prototype.hideShowCovered = function () { function getStyleProp(obj, style){ var value = obj.style[style]; if (!value) { if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C value = document.defaultView. getComputedStyle(obj, "").getPropertyValue(style); } else if (obj.currentStyle) { // IE value = obj.currentStyle[style]; } else { value = obj.style[style]; } } return value; }; var tags = new Array("applet", "select"); var el = this.element; var p = Associater.getAbsolutePos(el); var EX1 = p.x; var EX2 = el.offsetWidth + EX1; var EY1 = p.y; var EY2 = el.offsetHeight + EY1; for (var k = tags.length; k > 0; ) { var ar = document.getElementsByTagName(tags[--k]); var cc = null; for (var i = ar.length; i > 0;) { cc = ar[--i]; p = Associater.getAbsolutePos(cc); var CX1 = p.x; var CX2 = cc.offsetWidth + CX1; var CY1 = p.y; var CY2 = cc.offsetHeight + CY1; if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) { if (!cc.__msh_save_visibility) { cc.__msh_save_visibility = getStyleProp(cc, "visibility"); } cc.style.visibility = cc.__msh_save_visibility; } else { if (!cc.__msh_save_visibility) { cc.__msh_save_visibility = getStyleProp(cc, "visibility"); } cc.style.visibility = "hidden"; } } } };
四、CSS代码:selector.css
.selector { position: relative; display: none; border:2px solid #C9D7F1; font-size: 11px; color: #666; cursor: default; background:white; /* font-family:tahoma,verdana,sans-serif;*/ font-family:arial; margin-top:2px; width: 150px; height: 200px; }
五、tld定义
<!-- 联想查询标签 -->
<tag>
<name>associate</name>
<tag-class>com.moonNigh.tagSupport.AssociateQueryTag</tag-class>
<body-content>empty</body-content>
<small-icon></small-icon>
<large-icon></large-icon>
<description></description>
<attribute>
<name>id</name>
<required>true</required>
</attribute>
<attribute>
<name>name</name>
<required>true</required>
</attribute>
<attribute>
<name>text</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>value</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>actionUrl</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>startLeng</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.Integer</type>
</attribute>
<example></example>
</tag>
六、测试标签
<c:associate name="ca" id="ca" actionUrl="getTreeXml" startLeng="2"/>