- 浏览: 468085 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
a1439226817:
能不能发下源码1439226817@qq.com
ExtJsCRUD组件实现 -
w923690968:
[list][*]引用[u][/u][/list]
[JS]Drag And Drop -
zhumingyuan:
您好!用的的是3.2.3版本,按照您的方法配置了一下,但是还是 ...
spring vmc3.1.1 下,通过AnnotationMethodHandlerAdapter配置webBindingInitializer失效解决方案 -
sumo084:
我把xDarkness-MultClrBubble-1.0.j ...
JAVA实现类泡泡屏保效果 -
sumo084:
求源码,楼主好人,630483738@qq.com,谢谢
JAVA实现类泡泡屏保效果
注:源自Ajax实战
实现自动提示功能:
后台测试页面:
重构:
继承测试,以适应业务需要:
页面测试:
后台方法重构:
重构后的方法:
样式:
jquery版本:
实现自动提示功能:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Type Ahead</title> <%@ include file="/views/js/importjs.jsp" %> <script type="text/javascript"> window.onload = function(){ var elemSpan = document.createElement("span"); elemSpan.id = "spanOutput"; elemSpan.className = "spanTextDropdown"; document.body.appendChild(elemSpan); /* 用来给文本框设置属性,用定制的对象给这个属性赋值,这样就能够在整个脚本中引用它来获取值,而不是使用全局变量来获取值 */ document.getElementById('txtUserInput').obj = SetProperties(document.getElementById('txtUserInput'), $('txtUserValue'), 'TypeAheadAction-getContent.action', true, true, true, true, "No matching Data", false, null); }; /** * 将属性分配给对象 * @param {Object} xElem 要分配输入前提示功能的文本框 * @param {Object} xHidden 用来保存值的hidden元素 * @param {Object} xserverCode 服务器端URL * @param {Object} xignoreCase 搜索过程中忽略大小写? * @param {Object} xmatchAnywhere 字符串中匹配任意位置的文本? * @param {Object} xmatchTextBoxWidth 匹配文本框宽度? * @param {Object} xshowNoMatchMessage 显示无匹配消息? * @param {Object} xnoMatchingDataMessage 用于显示的消息 * @param {Object} xuseTimeout 选项显示一段时间后是否隐藏? * @param {Object} xtheVisibleTime span保持打开的时间 */ function SetProperties(xElem, xHidden, xserverCode, xignoreCase, xmatchAnywhere, xmatchTextBoxWidth, xshowNoMatchMessage, xnoMatchingDataMessage, xuseTimeout, xtheVisibleTime){ /** * 对于xignoreCase,xmatchAnywhere的处理,没有在属性中保持布尔值,而是保存了等价的正则表达式 * 在正则表达式中使用i来忽略大小写,使用^来匹配字符串的开始位置 * 在这里设置正则表达式参数,而不是在每次调用函数时使用if语句,对我们来说更加容易些 */ var props = { elem: xElem, hidden: xHidden, serverCode: xserverCode, regExFlags: ((xignoreCase) ? "i" : ""), regExAny: ((xmatchAnywhere) ? "" : "^"), matchAnywhere: xmatchAnywhere, matchTextBoxWidth: xmatchTextBoxWidth, theVisibleTime: xtheVisibleTime, showNoMatchMessage: xshowNoMatchMessage, noMatchingDataMessage: xnoMatchingDataMessage, useTimeout: xuseTimeout }; /* 将事件处理函数分配给文本框 */ AddHandler(xElem); return props; } /** * 附加事件处理函数,监听用户的输入、是否离开了文本框 */ function AddHandler(objText){ /* 键盘按键释放 */ objText.onkeyup = GiveOptions; objText.onblur = function(){ if (this.obj.useTimeout) StartTimeout(); } /* Opera浏览器出发onkeyup事件处理函数的方式与其他浏览器不同。 * 当触发onkeyup事件时,Opera不会再包括当前按键的文本框中显示值 * 我们为Opera添加onkeypress事件处理函数纠正了这个问题 */ if (Browser.isOpera) { objText.onkeypress = GiveOptions; } } var arrOptions = new Array();/* 从服务器查询中获取的所有可用选项 */ var strLastValue = "";/* 文本框中包含的最后的字符串 */ var bMadeRequest;/* 请求是否已经发送到服务器,而不必持续发送附加的请求,该标志正对快速打字员,这样我们就不必操心使用像Google那样的超时设置了 */ var theTextBox;/* 保存对拥有焦点的文本框的引用 */ var objLastActive;/* 最后激活的文本框 ,如果用户切换了文本框,这个变量将用来确定数据集是否需要重新刷新。在一个拥有多个文本框的窗口中实现这个解决方案,就需要知道哪一个文本框拥有焦点 */ var currentValueSelected = -1;/* 作用于选择列表的selectedIndex类似,-1表示没有选项被选中 */ var bNoResults = false;/* 是否有结果?这样我们就不必费心去试图找到任何结果了 */ var isTiming = false;/* 允许确定页面上是否运行了一个定时器,如果在一段时间内没有操作选项列表,那么运行的这个定时器会将选项列表从用户的视线中隐藏起来 */ /** * 检测用户按键 * @param {Object} e */ function GiveOptions(e){ /* 按下键的键编码 */ var intKey = -1; /* 检测用户按键 */ if (window.event) { intKey = event.keyCode; theTextBox = event.srcElement; } else { intKey = e.which; theTextBox = e.target; } /* 重置定时器 */ if (theTextBox.obj.useTimeout) { if (isTiming) { EraseTimeout();/* 取消定时器 */ } /* 重启定时器 */ StartTimeout(); } /* 确定是否存在文本 ,如果文本框不包含文本,隐藏下拉列表*/ if (theTextBox.value.length == 0 && !Browser.isOpera) { arrOptions = new Array(); HideTheBox(); strLastValue = ""; return false; } /* 在检测回车键、箭头键之前,需要验证当前激活的文本框是否是最后激活的文本框 */ if (objLastActive == theTextBox) { if (intKey == 13) {/* 回车键 */ GrabHighlighted(); theTextBox.blur(); return false; } else if (intKey == 38) { MoveHighlight(-1); return false; } else if (intKey == 40) { MoveHighlight(1); return false; } } /* 处理按键操作 * 使用脚本缓存的机制,来限制回送喝减少服务器的副段 * 我们执行一些检查来查看是否得到了新的结果 */ /* 访问服务器获取值 ,如果这些检查中的任何一个通过了,就需要检查服务器以获取数据*/ if (objLastActive != theTextBox/* 确定最后激活的文本框是否是当前拥有焦点的文本框 */ || theTextBox.value.indexOf(strLastValue) != 0 /* 检查文本框中输入的文本与上一次相同,只是在末尾处附加了一些内容 */ || ((arrOptions.length == 0 || arrOptions.length == 15) && !bNoResults)/* 如果没有结果,或者结果集包含了15个或者更少的元素 */ || (theTextBox.value.length <= strLastValue.length)/* 确保当前的长度大于最后的长度 */) { objLastActive = theTextBox; bMadeRequest = true; TypeAhead(theTextBox.value); } else if (!bMadeRequest) {/* 使用已经从服务器获取的列表 */ BuildList(theTextBox.value); } /* 保存用户输入 到“最近使用” */ strLastValue = theTextBox.value; } /* 发送请求道服务器 */ function TypeAhead(xStrText){ var strParams = "q=" + xStrText + "&where=" + theTextBox.obj.matchAnywhere; new Sky.Ajax({ url: theTextBox.obj.serverCode, params: strParams, onload: BuildChoices, method: "POST" }); } /* 将resopnseText属性转换为数组 */ function BuildChoices(data){ eval(data); BuildList(strLastValue); bMadeRequest = false;/* 通知脚本的其他部分发送到服务器的请求已经完成 */ } /* 将结果格式化为可显示的格式 */ function BuildList(theText){ SetElementPosition(theTextBox);/* 设置元素的位置,需要将span元素动态定位为直接位于实现输入前提示功能的文本框的底部 */ var theMatches = MakeMatches(theText);/* 格式化匹配的文本 */ theMatches = theMatches.join(""); if (theMatches.length > 0) {/* 显示结果 */ document.getElementById("spanOutput").innerHTML = theMatches; document.getElementById("OptionsList_0").className = "spanHighElement"; currentValueSelected = 0; bNoResults = false; } else {/* 显示没有匹配 */ currentValueSelected = -1; bNoResults = true; if (theTextBox.obj.showNoMatchMessage) document.getElementById("spanOutput").innerHTML = "<span class='noMatchData'>" + theTextBox.obj.noMatchingDataMessage + "</span>"; else HideTheBox(); } } /* 动态查找未定位元素的位置 */ function SetElementPosition(theTextBoxInt){ var selectedPosX = 0; var selectedPosY = 0; var theElement = theTextBoxInt; if (!theElement) return; var theElemHeight = theElement.offsetHeight; var theElemWidth = theElement.offsetWidth; /* 遍历文档树,获取元素想对于其父节点在X,Y方向上的位置。通过遍历每一个已定位的父节点,增加对应于父节点位置的偏移量,就能够得到元素的准确位置 */ while (theElement != null) { selectedPosX += theElement.offsetLeft; selectedPosY += theElement.offsetTop; theElement = theElement.offsetParent; } xPosElement = document.getElementById("spanOutput"); xPosElement.style.left = selectedPosX; if (theTextBoxInt.obj.matchTextBoxWidth) {/* 匹配文本框的宽度 */ xPosElement.style.width = theElemWidth; } xPosElement.style.top = selectedPosY + theElemHeight; xPosElement.style.display = "block";/* 显示下拉列表 */ if (theTextBoxInt.obj.useTimeout) { xPosElement.onmouseout = StartTimeout; xPosElement.onmouseover = EraseTimeout; } else { xPosElement.onmouseout = null; xPosElement.onmouseover = null; } } /* 使用正则来限制结果数量 */ var countForId = 0; function MakeMatches(xCompareStr){ countForId = 0; var matchArray = new Array(); var regExp = new RegExp(theTextBox.obj.regExAny + xCompareStr, theTextBox.obj.regExFlags); for (i = 0; i < arrOptions.length; i++) { var theMatch = arrOptions[i][0].match(regExp); if (theMatch) { matchArray[matchArray.length] = CreateUnderline(arrOptions[i][0], xCompareStr, i); } } return matchArray; } /* 操作字符串 */ var undeStart = "<span class='spanMatchText'>";/* span的起始标签 */ var undeEnd = "</span>";/* 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 + "'>"; var regExp = new RegExp(theTextBox.obj.regExAny + xTextMatch, theTextBox.obj.regExFlags); var aStart = xStr.search(regExp); var matchedText = xStr.substring(aStart, aStart + xTextMatch.length); countForId++; return selectSpanStart + selectSpanMid + xStr.replace(regExp, undeStart + matchedText + undeEnd) + selectSpanEnd; } /* 高亮 */ function MoveHighlight(xDir){ if (currentValueSelected >= 0) { newValue = parseInt(currentValueSelected) + parseInt(xDir); if (newValue > -1 && newValue < countForId) { currentValueSelected = newValue; SetHighColor(null); } } } function SetHighColor(theTextBox){ if (theTextBox) { currentValueSelected = theTextBox.id.slice(theTextBox.id.indexOf("_") + 1, theTextBox.id.length); } for (i = 0; i < countForId; i++) { document.getElementById('OptionsList_' + i).className = 'spanNormalElement'; } document.getElementById('OptionsList_' + currentValueSelected).className = 'spanHighElement'; } /* 处理箭头、鼠标点击事件 */ function SetText(xVal){ theTextBox.value = arrOptions[xVal][0]; //set text value theTextBox.obj.hidden.value = arrOptions[xVal][1]; document.getElementById("spanOutput").style.display = "none"; currentValueSelected = -1; //remove the selected index } /* 获取选中条目的文本和值 */ function GrabHighlighted(){ if (currentValueSelected >= 0) { xVal = document.getElementById("OptionsList_" + currentValueSelected).getAttribute("theArrayNumber"); SetText(xVal); HideTheBox(); } } function HideTheBox(){ document.getElementById("spanOutput").style.display = "none"; currentValueSelected = -1; EraseTimeout(); } function EraseTimeout(){ clearTimeout(isTiming); isTiming = false; } function StartTimeout(){ isTiming = setTimeout("HideTheBox()", theTextBox.obj.theVisibleTime); } </script> <style type="text/css"> span.spanTextDropdown { position: absolute; top: 0px; left: 0px; width: 150px; z-index: 101; background-color: #C0C0C0; border: 1px solid #000000; padding-left: 2px; overflow: visible; display: none; } span.spanMatchText { text-decoration: underline; font-weight: bold; } span.spanNormalElement { background: #C0C0C0; } span.spanHighElement { background: #000040; color: white; cursor: pointer; } span.noMatchData { font-weight: bold; color: #0000FF; } </style> </head> <body> AutoComplete Text Box:<input type="text" id="txtUserInput"/><input type="hidden" id="txtUserValue" ID="hidden1" /> </body> </html>
后台测试页面:
public class TypeAheadPage extends Page { /** * 从客户端接收数值,并且将查询得到的数据处理成字符串形式的JavaScript数组, * 这个心创建的数组返回给客户端,并在客户端执行 */ public void getData() { // 需要搜索的字符串 String strQuery = Request.getString("q"); // 在一个单词的中间位置查找结果? boolean isAny = "true".equalsIgnoreCase(Request.getString("where")); // 查询出的搜索结果集 DataTable dtQuestions = queryDataTable(isAny, strQuery); // 创建JavaScript数组 StringBuffer strJSArr = new StringBuffer("arrOptions = new Array("); boolean isFirstRecord = true; // 循环结果 for (DataRow row : dtQuestions.getDataRows()) { if (!isFirstRecord) { strJSArr.append(","); isFirstRecord = false; } // 创建包含产品名称,Id的二维数组 strJSArr.append("['").append(row.getString("ProductName")).append( "','").append(row.getString("Productid")).append("']"); } strJSArr.append(");"); // 将字符串写入响应流 Response.write(strJSArr.toString()); } /** * 查询出的搜索结果集 * * @param isAny 在一个单词的中间位置查找结果? * @param strQuery 需要搜索的字符串 * @return 包含搜索字符串的结果集 */ private DataTable queryDataTable(boolean isAny, String strQuery) { String strAny = isAny ? "%" : ""; // 创建查询SQL语句,限制只允许搜索返回15条记录 String strSql = "SELECT top 15 ProductName, ProductId FROM Products " + "WHERE ProductName LIKE '" + strAny + strQuery + "%' ORDER BY ProductName"; return new QueryBuilder(strSql).executeDataTable(); } }
重构:
var logger = { msgs: '', append: function(msg){ logger.msgs += msg; }, show: function() { alert(logger.msgs); } }; /** * @class TextSuggest 自动提示 */ TextSuggest = Base.extend({ /** * @method constructor 构造器,根据配置初始化自动提示,并对需要自动提示的文本框注入键盘监听事件 * @param {String} anId 自动提示对应的文本框id * @param {String} url 服务器端url * @param {Object} options 配置参数对象 */ constructor: function(anId, url, options){ /** * @property {String} id 自动提示对应的文本框id */ this.id = anId; /** * @property {Element} id 自动提示对应的文本框 */ this.textInput = $(anId); /** * @property {Array} suggestions {text: strText, value: strValue}格式数据 */ this.suggestions = []; /** * @property {String} url 服务器端url */ this.url = url; this.setOptions(options); this.injectSuggestBehavior(); }, /** * @method setOptions 初始化配置参数 * @param {Object} options 参数 */ setOptions: function(options){ /** * @property options 配置参数 */ this.options = Sky.apply({ suggestDivClassName: 'suggestDiv', suggestionClassName: 'suggestion', matchClassName: 'spanMatchText', matchTextWidth: true, selectionColor: '#b1c09c', matchAnywhere: false, ignoreCase: false, count: 10 }, options || {}); }, /** * @method injectSuggestBehavior 注入Suggest行为: * 为自动提示文本框添加对应的隐藏字段(其id命名为文本框id+"_hidden"),创建自动提示DIV,并对文本框添加键盘监听 */ injectSuggestBehavior: function(){ logger.append("injectSuggest"); if (Browser.isIE) this.textInput.autocomplete = "off"; /* 添加键盘监听 */ new TextSuggestKeyHandler(this); /* 添加隐藏字段 */ var hiddenInput = document.createElement("input"); hiddenInput.name = this.id + '_hidden'; hiddenInput.id = this.id + '_hidden'; hiddenInput.type = "hidden"; this.textInput.parentNode.appendChild(hiddenInput); /** * @property {Element} inputHidden 隐藏字段 */ this.hiddenInput = hiddenInput; this.createSuggestionsDiv(); logger.append("end injectSuggest"); }, /** * @method sendRequestForSuggestions 请求数据:如果正在请求,加入等待队列 */ sendRequestForSuggestions: function(){ logger.append("request"); /** * @property {Boolean} handlingRequest 正在处理请求? */ if (this.handlingRequest) { /** * @property {Boolean} pendingRequest 有等待的请求? */ this.pendingRequest = true; return; } this.handlingRequest = true; this.callAjaxEngine(); logger.append("end request"); }, /** * @method callAjaxEngine 发送请求 */ callAjaxEngine: function(){ logger.append("callAjaxEngine"); var callParms = []; callParms.push(this.id + '_request'); callParms.push('id=' + this.id); callParms.push('count=' + this.options.count); callParms.push('query=' + this.lastRequestString); callParms.push('match_anywhere=' + this.options.matchAnywhere); callParms.push('ignore_case=' + this.options.ignoreCase); var additionalParms = this.options.requestParameters || []; for (var i = 0; i < additionalParms.length; i++) callParms.push(additionalParms[i]); var instance = this; /* new Sky.Ajax({ url: this.url, params: callParms.join("&"), onload: instance.ajaxUpdate.bind(instance), method: "POST" });*/ DWRActionUtil.executeaction(this.url,{query:this.lastRequestString},instance.ajaxUpdate.bind(instance)); logger.append("end callAjaxEngine"); }, /** * @method ajaxUpdate 处理请求响应:没有匹配项,隐藏下拉列表,清空隐藏值。 * 否则,更新并显示下拉列表,设置第一个下拉项为选中状态。如果有等待的请求,发送请求(注:只处理最后输入框中的请求) * @param {String} responseText 请求响应的文本 */ ajaxUpdate: function(responseText){ this.createSuggestions(responseText.responseText); if (this.suggestions.length == 0) { this.suggestionsDiv.hide(); this.hiddenInput.value = ""; } else { this.updateSuggestionsDiv(); this.showSuggestions(); this.updateSelection(0); } this.handlingRequest = false; if (this.pendingRequest) { this.pendingRequest = false; this.lastRequestString = this.textInput.value; this.sendRequestForSuggestions(); } }, /** * @method createSuggestions 解析响应文本,并设置匹配项,匹配项为{text: strText, value: strValue}格式数据 * @param {String} responseText 响应文本 */ createSuggestions: function(responseText){ this.suggestions = []; eval(responseText); if(!arrOptions) return; for (var i = 0; i < arrOptions.length; i++) { var strText = arrOptions[i][0]; var strValue = arrOptions[i][1]; this.suggestions.push({ text: strText, value: strValue }); } }, /** * @method getElementContent // TODO 这个方法需移除 * @param {Object} element */ getElementContent: function(element){ return element.firstChild.data; }, /** * @method updateSuggestionsDiv 更新下拉列表 */ updateSuggestionsDiv: function(){ this.suggestionsDiv.innerHTML = ""; var suggestLines = this.createSuggestionSpans(); for (var i = 0; i < suggestLines.length; i++) { this.suggestionsDiv.appendChild(suggestLines[i]); logger.append("adding div " + suggestLines[i]); } }, /** * @method createSuggestionSpans 创建下拉列表选项 */ createSuggestionSpans: function(){ var regExpFlags = this.options.ignoreCase ? "i" : ""; var startRegExp = this.options.matchAnywhere ? "" : "^"; var regExp = new RegExp(startRegExp + this.lastRequestString, regExpFlags); var suggestionSpans = []; for (var i = 0; i < this.suggestions.length; i++) { suggestionSpans.push(this.createSuggestionSpan(i, regExp)) logger.append("suggestion: " + this.suggestions[i].text); } return suggestionSpans; }, addBeforeSpan: function() { return null; }, /** * @method createSuggestionSpan 创建单个下拉选项 * @param {Number} index 下拉选项索引 * @param {RegExp} regExp */ createSuggestionSpan: function(n, regExp){ var suggestion = this.suggestions[n]; var suggestionSpan = document.createElement("span"); suggestionSpan.className = this.options.suggestionClassName; suggestionSpan.style.width = '100%'; suggestionSpan.style.display = 'block'; suggestionSpan.id = this.id + "_" + n; suggestionSpan.onmouseover = this.mouseoverHandler.bindAsEventListener(this); suggestionSpan.onclick = this.itemClickHandler.bindAsEventListener(this); var textValues = this.splitTextValues(suggestion.text, this.lastRequestString.length, regExp); var textMatchSpan = document.createElement("span"); textMatchSpan.id = this.id + "_match_" + n; textMatchSpan.className = this.options.matchClassName; textMatchSpan.onmouseover = this.mouseoverHandler.bindAsEventListener(this); textMatchSpan.onclick = this.itemClickHandler.bindAsEventListener(this); textMatchSpan.appendChild(document.createTextNode(textValues.mid)); var beforeSpan = this.addBeforeSpan(); if(beforeSpan){ suggestionSpan.appendChild(document.createTextNode(beforeSpan)); } suggestionSpan.appendChild(document.createTextNode(textValues.start)); suggestionSpan.appendChild(textMatchSpan); suggestionSpan.appendChild(document.createTextNode(textValues.end)); return suggestionSpan; }, /** * @method updateSelection 更新第n个选项为选中,同时改变背景色 * @param {Object} n */ updateSelection: function(n){ this.selectedIndex = n; for (var i = 0; i < this.suggestions.length; i++) { var span = $(this.id + "_" + i); if (i != this.selectedIndex) span.style.backgroundColor = ""; else span.style.backgroundColor = this.options.selectionColor; } }, /** * @method handleTextInput 文本框输入改变处理器 */ handleTextInput: function(){ var previousRequest = this.lastRequestString; this.setLastRequestString(); logger.append("text input: " + previousRequest + " -> " + this.lastRequestString); if (this.lastRequestString == "") { this.suggestionsDiv.hide(); } else if (this.lastRequestString != previousRequest) { this.sendRequestForSuggestions(); } }, setLastRequestString: function() { /** * @property {String} lastRequestString 最后输入的字符串 */ this.lastRequestString = this.textInput.value; }, /** * @method moveSelectionUp 设置当前选项的上一个选项为选中状态 */ moveSelectionUp: function(){ if (this.selectedIndex == 0) { this.selectedIndex = this.suggestions.length; } this.updateSelection(this.selectedIndex - 1); }, /** * @method moveSelectionDown 设置当前选项的下一个选项为选中状态 */ moveSelectionDown: function(){ if (this.selectedIndex == (this.suggestions.length - 1)) { this.selectedIndex = -1; } this.updateSelection(this.selectedIndex + 1); }, /** * @method createSuggestionsDiv 创建自动提示div */ createSuggestionsDiv: function(){ /** * @property {Element} suggestionsDiv 自动提示div元素 */ this.suggestionsDiv = document.createElement("div"); this.suggestionsDiv.className = this.options.suggestDivClassName; var divStyle = this.suggestionsDiv.style; divStyle.position = 'absolute'; divStyle.zIndex = 101; divStyle.display = "none"; this.textInput.parentNode.appendChild(this.suggestionsDiv); }, /** * @method setInputFromSelection 设置表单文本及隐藏域的值 */ setInputFromSelection: function(){ var suggestion = this.suggestions[this.selectedIndex]; this.textInput.value = $(this.id + "_" + this.selectedIndex).innerText;//suggestion.text; this.hiddenInput.value = suggestion.value; this.suggestionsDiv.hide(); }, /** * @method showSuggestions 显示下拉列表 */ showSuggestions: function(){ var divStyle = this.suggestionsDiv.style; if (divStyle.display == '') return; this.positionSuggestionsDiv(); divStyle.display = ''; }, /** * @method positionSuggestionsDiv 设置下拉列表的位置、大小 */ positionSuggestionsDiv: function(){ var textPos = this.textInput.getPositionEx(); var divStyle = this.suggestionsDiv.style; divStyle.top = (textPos.y + this.textInput.offsetHeight) + "px"; divStyle.left = textPos.x + "px"; logger.append("position suggest div: " + divStyle.left + "," + divStyle.top); if (this.options.matchTextWidth) divStyle.width = (this.textInput.offsetWidth) + "px"; }, /** * @method mouseoverHandler 鼠标移过事件处理器 * @param {Event} e */ mouseoverHandler: function(e){ var src = e.srcElement ? e.srcElement : e.target; var index = parseInt(src.id.substring(src.id.lastIndexOf('_') + 1)); this.updateSelection(index); }, /** * @method itemClickHandler 下拉选项点击事件处理器 * @param {Object} e */ itemClickHandler: function(e){ this.mouseoverHandler(e); this.suggestionsDiv.hide(); this.textInput.focus(); }, /** * @method splitTextValues 将为本拆开为开始、中间、结尾三部分,其中中间部分为匹配文本 * @param {String} text 需要匹配的文本 * @param {Number} len 匹配文本的长度 * @param {RegExp} regExp 匹配文本正则 * @return {Object} {start: startText, mid: matchText, end: endText}格式的对象 */ splitTextValues: function(text, len, regExp){ var startPos = text.search(regExp); var matchText = text.substring(startPos, startPos + len); var startText = startPos == 0 ? "" : text.substring(0, startPos); var endText = text.substring(startPos + len); return { start: startText, mid: matchText, end: endText }; } }); /** * @class TextSuggestKeyHandler 自动提示文本监听事件 */ TextSuggestKeyHandler = Base.extend({ /** * @method constructor 构造器 * @param {TextSuggest} textSuggest 自动提示组件 */ constructor: function(textSuggest){ /** * @property {TextSuggest} textSuggest 自动提示组件 */ this.textSuggest = textSuggest; /** * @property {Element} input 自动提示文本框 */ this.input = this.textSuggest.textInput; this.addKeyHandling(); }, /** * @method addKeyHandling 添加事件处理 */ addKeyHandling: function(){ addEvent(this.input, "keyup", this.keyupHandler.bindAsEventListener(this)); addEvent(this.input, "keydown", this.keydownHandler.bindAsEventListener(this)); addEvent(this.input, "blur", this.onblurHandler.bindAsEventListener(this)); if (Sky.isOpera) addEvent(this.input, "keypress", this.keyupHandler.bindAsEventListener(this)); }, /** * @method keydownHandler 键盘按下处理器 * @param {Event} e 事件,为自动注入 */ keydownHandler: function(e){ var upArrow = 38; var downArrow = 40; if (e.keyCode == upArrow) { this.textSuggest.moveSelectionUp(); setTimeout(this.moveCaretToEnd.bind(this), 1); } else if (e.keyCode == downArrow) this.textSuggest.moveSelectionDown(); }, /** * @method keyupHandler 键盘弹起处理器 * @param {Event} e 事件,为自动注入 */ keyupHandler: function(e){ if (this.input.length == 0 && !Sky.isOpera) this.textSuggest.hideSuggestions(); if (!this.handledSpecialKeys(e)) this.textSuggest.handleTextInput(); }, /** * @method handledSpecialKeys 处理特殊按键 * @param {Event} e 事件,为自动注入 */ handledSpecialKeys: function(e){ var enterKey = 13; var upArrow = 38; var downArrow = 40; if (e.keyCode == upArrow || e.keyCode == downArrow) { return true; } else if (e.keyCode == enterKey) { this.textSuggest.setInputFromSelection(); return true; } return false; }, moveCaretToEnd: function(){ var pos = this.input.value.length; if (this.input.setSelectionRange) { this.input.setSelectionRange(pos, pos); } else if (this.input.createTextRange) { var m = this.input.createTextRange(); m.moveStart('character', pos); m.collapse(); m.select(); } }, onblurHandler: function(e){ if (this.textSuggest.suggestionsDiv.style.display == '') this.textSuggest.setInputFromSelection(); this.textSuggest.suggestionsDiv.hide(); } });
继承测试,以适应业务需要:
Array.prototype.containsRoute = function(route) { for(var i=0;i<this.length;i++) { if(this[i].text == route.text && this[i].value == route.value){ return true; } } return false; } RouteTextSuggest = TextSuggest.extend({ allRoutes: [], setLastRequestString: function() { var routes = this.textInput.value.split("-"); if(routes.length >= 2) { this.lastRequestString = routes[routes.length-2] + "-" + routes[routes.length-1]; } else { this.lastRequestString = this.textInput.value; } }, addBeforeSpan: function(){ //return this.textInput.value.replace(this.lastRequestString,""); return this.textInput.value.substring(0,this.textInput.value.lastIndexOf(this.lastRequestString)); }, getRoutes: function(){ var routeIds = []; var routes = this.textInput.value.split("-"); for(var i=0;i<routes.length-1;i++){ var route = routes[i] + "-" + routes[i+1]; for(var j=0;j<this.allRoutes.length;j++){ if(this.allRoutes[j].text==route){ routeIds.push(this.allRoutes[j].value); } } } return routeIds; }, createSuggestions: function(responseText){ this.base(responseText); for (var i = 0; i < this.suggestions.length; i++) { if(!this.allRoutes.containsRoute(this.suggestions[i])){ this.allRoutes.push(this.suggestions[i]); } } } });
页面测试:
new RouteTextSuggest(id, "RouteAction-getRoutes")
后台方法重构:
/** * 自动提示组件生成数据获取对应key,value值的接口 * @author Darkness * Create on Aug 19, 2010 4:56:12 PM * @version 1.0 */ public interface ITextSuggest { /** * 获取id */ String getId(Object entry); /** * 获取name * @param entry * @return */ String getName(Object entry); }
/** * 初始化TextSuggest数据 * @param list * @param textSuggest */ protected void initTextSuggest(List list, ITextSuggest textSuggest) { // 创建JavaScript数组 StringBuffer strJSArr = new StringBuffer("arrOptions = new Array("); boolean isFirstRecord = true; for (int i = 0; i < list.size(); i++) { Object entry = list.get(i); if (!isFirstRecord) { strJSArr.append(","); } // 创建包含产品名称,Id的二维数组 strJSArr.append("['").append(textSuggest.getName(entry)).append("','") .append(textSuggest.getId(entry)).append("']"); isFirstRecord = false; } strJSArr.append(");"); // 将字符串写入响应流 responseText = strJSArr.toString(); }
重构后的方法:
/** * 获取自动提示数组 */ public void getRoutes() { // 查询出的搜索结果集 List<RouteEntry> list = routeEntryService.getRouteEntryList(query); initTextSuggest(list, new ITextSuggest() { public String getId(Object entry) { return ((RouteEntry) entry).getId().toString(); } public String getName(Object entry) { return ((RouteEntry) entry).getName(); } }); }
样式:
.suggestDiv { position: absolute; top: 0px; left: 0px; width: 150px; z-index: 101; background-color: #C0C0C0; border: 1px solid #000000; padding-left: 2px; overflow: visible; } span.spanMatchText { text-decoration: underline; font-weight: bold; } .suggestion { background: #C0C0C0; } span.spanHighElement { background: #000040; color: white; cursor: pointer; } span.noMatchData { font-weight: bold; color: #0000FF; }
jquery版本:
var logger = { msgs: '', append: function(msg){ logger.msgs += msg; }, show: function() { alert(logger.msgs); } }; /** * @class TextSuggest 自动提示 */ TextSuggest = Base.extend({ /** * @method constructor 构造器,根据配置初始化自动提示,并对需要自动提示的文本框注入键盘监听事件 * @param {String} anId 自动提示对应的文本框id * @param {String} url 服务器端url * @param {Object} options 配置参数对象 */ constructor: function(anId, url, options){ /** * @property {String} id 自动提示对应的文本框id */ this.id = anId; /** * @property {Element} id 自动提示对应的文本框 */ this.textInput = findElement(anId); /** * @property {Array} suggestions {text: strText, value: strValue}格式数据 */ this.suggestions = []; /** * @property {String} url 服务器端url */ this.url = url; this.setOptions(options); this.injectSuggestBehavior(); }, /** * @method setOptions 初始化配置参数 * @param {Object} options 参数 */ setOptions: function(options){ /** * @property options 配置参数 */ this.options = $.extend({ suggestDivClassName: 'suggestDiv', suggestionClassName: 'suggestion', matchClassName: 'spanMatchText', matchTextWidth: true, selectionColor: '#b1c09c', matchAnywhere: false, ignoreCase: false, count: 10 }, options || {}); }, /** * @method injectSuggestBehavior 注入Suggest行为: * 为自动提示文本框添加对应的隐藏字段(其id命名为文本框id+"_hidden"),创建自动提示DIV,并对文本框添加键盘监听 */ injectSuggestBehavior: function(){ logger.append("injectSuggest"); if ($.browser.isie) this.textInput.autocomplete = "off"; /* 添加键盘监听 */ new TextSuggestKeyHandler(this); /* 添加隐藏字段 */ var hiddenInput = document.createElement("input"); hiddenInput.name = this.id + '_hidden'; hiddenInput.id = this.id + '_hidden'; hiddenInput.type = "hidden"; this.textInput.parentNode.appendChild(hiddenInput); /** * @property {Element} inputHidden 隐藏字段 */ this.hiddenInput = hiddenInput; this.createSuggestionsDiv(); logger.append("end injectSuggest"); }, /** * @method sendRequestForSuggestions 请求数据:如果正在请求,加入等待队列 */ sendRequestForSuggestions: function(){ logger.append("request"); /** * @property {Boolean} handlingRequest 正在处理请求? */ if (this.handlingRequest) { /** * @property {Boolean} pendingRequest 有等待的请求? */ this.pendingRequest = true; return; } this.handlingRequest = true; this.callAjaxEngine(); logger.append("end request"); }, /** * @method callAjaxEngine 发送请求 */ callAjaxEngine: function(){ logger.append("callAjaxEngine"); var callParms = []; callParms.push(this.id + '_request'); callParms.push('id=' + this.id); callParms.push('count=' + this.options.count); callParms.push('query=' + this.lastRequestString); callParms.push('match_anywhere=' + this.options.matchAnywhere); callParms.push('ignore_case=' + this.options.ignoreCase); var additionalParms = this.options.requestParameters || []; for (var i = 0; i < additionalParms.length; i++) callParms.push(additionalParms[i]); var instance = this; /* new Sky.Ajax({ url: this.url, params: callParms.join("&"), onload: instance.ajaxUpdate.bind(instance), method: "POST" });*/ $.post(this.url,{query:this.lastRequestString},instance.ajaxUpdate.bind(instance)); logger.append("end callAjaxEngine"); }, /** * @method ajaxUpdate 处理请求响应:没有匹配项,隐藏下拉列表,清空隐藏值。 * 否则,更新并显示下拉列表,设置第一个下拉项为选中状态。如果有等待的请求,发送请求(注:只处理最后输入框中的请求) * @param {String} responseText 请求响应的文本 */ ajaxUpdate: function(responseText){ console.log('xxxxxxxxxxxxxxxxxxxx') this.createSuggestions(responseText); if (this.suggestions.length == 0) { this.suggestionsDiv.hide(); this.hiddenInput.value = ""; } else { this.updateSuggestionsDiv(); this.showSuggestions(); this.updateSelection(0); } this.handlingRequest = false; if (this.pendingRequest) { this.pendingRequest = false; this.lastRequestString = this.textInput.value; this.sendRequestForSuggestions(); } }, /** * @method createSuggestions 解析响应文本,并设置匹配项,匹配项为{text: strText, value: strValue}格式数据 * @param {String} responseText 响应文本 */ createSuggestions: function(arrOptions){ this.suggestions = []; //eval(responseText); //if(!arrOptions) return; for (var i = 0; i < arrOptions.length; i++) { var strText = arrOptions[i].name; var strValue = arrOptions[i].id; this.suggestions.push({ text: strText, value: strValue, raw: arrOptions[i] }); } }, /** * @method getElementContent // TODO 这个方法需移除 * @param {Object} element */ getElementContent: function(element){ return element.firstChild.data; }, /** * @method updateSuggestionsDiv 更新下拉列表 */ updateSuggestionsDiv: function(){ this.suggestionsDiv.innerHTML = ""; var suggestLines = this.createSuggestionSpans(); for (var i = 0; i < suggestLines.length; i++) { this.suggestionsDiv.appendChild(suggestLines[i]); logger.append("adding div " + suggestLines[i]); } }, /** * @method createSuggestionSpans 创建下拉列表选项 */ createSuggestionSpans: function(){ var regExpFlags = this.options.ignoreCase ? "i" : ""; var startRegExp = this.options.matchAnywhere ? "" : "^"; var regExp = new RegExp(startRegExp + this.lastRequestString, regExpFlags); var suggestionSpans = []; for (var i = 0; i < this.suggestions.length; i++) { suggestionSpans.push(this.createSuggestionSpan(i, regExp)) logger.append("suggestion: " + this.suggestions[i].text); } return suggestionSpans; }, addBeforeSpan: function(n) { var suggestion = this.suggestions[n]; var raw = suggestion.raw; var before = ''; var level = raw.level; for(var i=1; i<level; i++) { before += '--'; } return before; }, /** * @method createSuggestionSpan 创建单个下拉选项 * @param {Number} index 下拉选项索引 * @param {RegExp} regExp */ createSuggestionSpan: function(n, regExp){ var suggestion = this.suggestions[n]; var suggestionSpan = document.createElement("span"); suggestionSpan.className = this.options.suggestionClassName; suggestionSpan.style.width = '100%'; suggestionSpan.style.display = 'block'; suggestionSpan.id = this.id + "_" + n; suggestionSpan.onmouseover = this.mouseoverHandler.bindAsEventListener(this); suggestionSpan.onclick = this.itemClickHandler.bindAsEventListener(this); var textValues = this.splitTextValues(suggestion.text, this.lastRequestString.length, regExp); var textMatchSpan = document.createElement("span"); textMatchSpan.id = this.id + "_match_" + n; textMatchSpan.className = this.options.matchClassName; textMatchSpan.onmouseover = this.mouseoverHandler.bindAsEventListener(this); textMatchSpan.onclick = this.itemClickHandler.bindAsEventListener(this); textMatchSpan.appendChild(document.createTextNode(textValues.mid)); var beforeSpan = this.addBeforeSpan(n); if(beforeSpan){ suggestionSpan.appendChild(document.createTextNode(beforeSpan)); } suggestionSpan.appendChild(document.createTextNode(textValues.start)); suggestionSpan.appendChild(textMatchSpan); suggestionSpan.appendChild(document.createTextNode(textValues.end)); return suggestionSpan; }, /** * @method updateSelection 更新第n个选项为选中,同时改变背景色 * @param {Object} n */ updateSelection: function(n){ this.selectedIndex = n; for (var i = 0; i < this.suggestions.length; i++) { var span = findElement( this.id + "_" + i); if (i != this.selectedIndex) span.style.backgroundColor = ""; else span.style.backgroundColor = this.options.selectionColor; } }, /** * @method handleTextInput 文本框输入改变处理器 */ handleTextInput: function(){ var previousRequest = this.lastRequestString; this.setLastRequestString(); logger.append("text input: " + previousRequest + " -> " + this.lastRequestString); if (this.lastRequestString == "") { $(this.suggestionsDiv).hide(); } else if (this.lastRequestString != previousRequest) { this.sendRequestForSuggestions(); } }, setLastRequestString: function() { /** * @property {String} lastRequestString 最后输入的字符串 */ this.lastRequestString = this.textInput.value; }, /** * @method moveSelectionUp 设置当前选项的上一个选项为选中状态 */ moveSelectionUp: function(){ if (this.selectedIndex == 0) { this.selectedIndex = this.suggestions.length; } this.updateSelection(this.selectedIndex - 1); }, /** * @method moveSelectionDown 设置当前选项的下一个选项为选中状态 */ moveSelectionDown: function(){ if (this.selectedIndex == (this.suggestions.length - 1)) { this.selectedIndex = -1; } this.updateSelection(this.selectedIndex + 1); }, /** * @method createSuggestionsDiv 创建自动提示div */ createSuggestionsDiv: function(){ /** * @property {Element} suggestionsDiv 自动提示div元素 */ this.suggestionsDiv = document.createElement("div"); this.suggestionsDiv.className = this.options.suggestDivClassName; var divStyle = this.suggestionsDiv.style; divStyle.position = 'absolute'; divStyle.zIndex = 101; divStyle.display = "none"; this.textInput.parentNode.appendChild(this.suggestionsDiv); }, /** * @method setInputFromSelection 设置表单文本及隐藏域的值 */ setInputFromSelection: function(){ var suggestion = this.suggestions[this.selectedIndex]; this.textInput.value = suggestion.text;//findElement(this.id + "_" + this.selectedIndex).innerText;//suggestion.text; this.hiddenInput.value = suggestion.value; $(this.suggestionsDiv).hide(); }, /** * @method showSuggestions 显示下拉列表 */ showSuggestions: function(){ var divStyle = this.suggestionsDiv.style; if (divStyle.display == '') return; this.positionSuggestionsDiv(); divStyle.display = ''; }, /** * @method positionSuggestionsDiv 设置下拉列表的位置、大小 */ positionSuggestionsDiv: function(){ var textPos = $E.getPositionEx(this.textInput); var divStyle = this.suggestionsDiv.style; divStyle.top = (textPos.y + this.textInput.offsetHeight) + "px"; divStyle.left = textPos.x + "px"; logger.append("position suggest div: " + divStyle.left + "," + divStyle.top); if (this.options.matchTextWidth) divStyle.width = (this.textInput.offsetWidth) + "px"; }, /** * @method mouseoverHandler 鼠标移过事件处理器 * @param {Event} e */ mouseoverHandler: function(e){ var src = e.srcElement ? e.srcElement : e.target; var index = parseInt(src.id.substring(src.id.lastIndexOf('_') + 1)); this.updateSelection(index); }, /** * @method itemClickHandler 下拉选项点击事件处理器 * @param {Object} e */ itemClickHandler: function(e){ this.mouseoverHandler(e); this.suggestionsDiv.hide(); this.textInput.focus(); }, /** * @method splitTextValues 将为本拆开为开始、中间、结尾三部分,其中中间部分为匹配文本 * @param {String} text 需要匹配的文本 * @param {Number} len 匹配文本的长度 * @param {RegExp} regExp 匹配文本正则 * @return {Object} {start: startText, mid: matchText, end: endText}格式的对象 */ splitTextValues: function(text, len, regExp){ var startPos = text.search(regExp); var matchText = text.substring(startPos, startPos + len); var startText = startPos == 0 ? "" : text.substring(0, startPos); var endText = text.substring(startPos + len); return { start: startText, mid: matchText, end: endText }; } }); /** * @class TextSuggestKeyHandler 自动提示文本监听事件 */ TextSuggestKeyHandler = Base.extend({ /** * @method constructor 构造器 * @param {TextSuggest} textSuggest 自动提示组件 */ constructor: function(textSuggest){ /** * @property {TextSuggest} textSuggest 自动提示组件 */ this.textSuggest = textSuggest; /** * @property {Element} input 自动提示文本框 */ this.input = this.textSuggest.textInput; this.addKeyHandling(); }, /** * @method addKeyHandling 添加事件处理 */ addKeyHandling: function(){ $(this.input).on("keyup", this.keyupHandler.bindAsEventListener(this)); $(this.input).on("keydown", this.keydownHandler.bindAsEventListener(this)); $(this.input).on("blur", this.onblurHandler.bindAsEventListener(this)); }, /** * @method keydownHandler 键盘按下处理器 * @param {Event} e 事件,为自动注入 */ keydownHandler: function(e){ var upArrow = 38; var downArrow = 40; if (e.keyCode == upArrow) { this.textSuggest.moveSelectionUp(); //setTimeout(this.moveCaretToEnd.bind(this), 1); } else if (e.keyCode == downArrow) this.textSuggest.moveSelectionDown(); }, /** * @method keyupHandler 键盘弹起处理器 * @param {Event} e 事件,为自动注入 */ keyupHandler: function(e){ if (this.input.length == 0 && !Sky.isOpera) this.textSuggest.hideSuggestions(); if (!this.handledSpecialKeys(e)) this.textSuggest.handleTextInput(); }, /** * @method handledSpecialKeys 处理特殊按键 * @param {Event} e 事件,为自动注入 */ handledSpecialKeys: function(e){ var enterKey = 13; var upArrow = 38; var downArrow = 40; if (e.keyCode == upArrow || e.keyCode == downArrow) { return true; } else if (e.keyCode == enterKey) { this.textSuggest.setInputFromSelection(); return true; } return false; }, moveCaretToEnd: function(){ var pos = this.input.value.length; if (this.input.setSelectionRange) { this.input.setSelectionRange(pos, pos); } else if (this.input.createTextRange) { var m = this.input.createTextRange(); m.moveStart('character', pos); m.collapse(); m.select(); } }, onblurHandler: function(e){ if (this.textSuggest.suggestionsDiv.style.display == '') this.textSuggest.setInputFromSelection(); $(this.textSuggest.suggestionsDiv).hide(); } });
/* Base.js, version 1.1a Copyright 2006-2010, Dean Edwards License: http://www.opensource.org/licenses/mit-license.php */ var Base = function() { // dummy }; Base.extend = function(_instance, _static) { // subclass var extend = Base.prototype.extend; // build the prototype Base._prototyping = true; var proto = new this; extend.call(proto, _instance); proto.base = function() { // call this method from any other method to invoke that method's ancestor }; delete Base._prototyping; // create the wrapper for the constructor function //var constructor = proto.constructor.valueOf(); //-dean var constructor = proto.constructor; var klass = proto.constructor = function() { if (!Base._prototyping) { if (this._constructing || this.constructor == klass) { // instantiation this._constructing = true; constructor.apply(this, arguments); delete this._constructing; } else if (arguments[0] != null) { // casting return (arguments[0].extend || extend).call(arguments[0], proto); } } }; // build the class interface klass.ancestor = this; klass.extend = this.extend; klass.forEach = this.forEach; klass.implement = this.implement; klass.prototype = proto; klass.toString = this.toString; klass.valueOf = function(type) { //return (type == "object") ? klass : constructor; //-dean return (type == "object") ? klass : constructor.valueOf(); }; extend.call(klass, _static); // class initialisation if (typeof klass.init == "function") klass.init(); return klass; }; Base.prototype = { extend: function(source, value) { if (arguments.length > 1) { // extending with a name/value pair var ancestor = this[source]; if (ancestor && (typeof value == "function") && // overriding a method? // the valueOf() comparison is to avoid circular references (!ancestor.valueOf || ancestor.valueOf() != value.valueOf()) && /\bbase\b/.test(value)) { // get the underlying method var method = value.valueOf(); // override value = function() { var previous = this.base || Base.prototype.base; this.base = ancestor; var returnValue = method.apply(this, arguments); this.base = previous; return returnValue; }; // point to the underlying method value.valueOf = function(type) { return (type == "object") ? value : method; }; value.toString = Base.toString; } this[source] = value; } else if (source) { // extending with an object literal var extend = Base.prototype.extend; // if this object has a customised extend method then use it if (!Base._prototyping && typeof this != "function") { extend = this.extend || extend; } var proto = {toSource: null}; // do the "toString" and other methods manually var hidden = ["constructor", "toString", "valueOf"]; // if we are prototyping then include the constructor var i = Base._prototyping ? 0 : 1; while (key = hidden[i++]) { if (source[key] != proto[key]) { extend.call(this, key, source[key]); } } // copy each of the source object's properties to this object for (var key in source) { if (!proto[key]) extend.call(this, key, source[key]); } } return this; } }; // initialise Base = Base.extend({ constructor: function() { this.extend(arguments[0]); } }, { ancestor: Object, version: "1.1", forEach: function(object, block, context) { for (var key in object) { if (this.prototype[key] === undefined) { block.call(context, object[key], key, object); } } }, implement: function() { for (var i = 0; i < arguments.length; i++) { if (typeof arguments[i] == "function") { // if it's a function, call it arguments[i](this.prototype); } else { // add the interface using the extend method this.prototype.extend(arguments[i]); } } return this; }, toString: function() { return String(this.valueOf()); } }); function $A(iterable) { if (!iterable) return []; if (iterable.toArray) return iterable.toArray(); var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; } Function.prototype.bind = function() { var __method = this, args = $A(arguments), object = args.shift(); return function() { return __method.apply(object, args.concat($A(arguments))); }; }; Function.prototype.bindAsEventListener = function() { var __method = this, args = $A(arguments), object = args.shift(); return function(event) { return __method.apply(object, [event || window.event].concat(args)); }; }; var isIE=navigator.userAgent.toLowerCase().indexOf("msie")!=-1; var isIE6=navigator.userAgent.toLowerCase().indexOf("msie 6.0")!=-1; var isIE7=navigator.userAgent.toLowerCase().indexOf("msie 7.0")!=-1&&!window.XDomainRequest; var isIE8=!!window.XDomainRequest; var isGecko=navigator.userAgent.toLowerCase().indexOf("gecko")!=-1; var isQuirks=document.compatMode=="BackCompat"; function findElement(a){ if(typeof(a)=="string"){ a=document.getElementById(a); if(!a){ return null } } return a; } function getEvent(a){ return window.event||a } function stopEvent(a){ a=getEvent(a); if(!a){ return; } if(isGecko){ a.preventDefault(); a.stopPropagation() } a.cancelBubble=true; a.returnValue=false } function getEventPosition(evt){ evt = window.event || evt; var f={x:evt.clientX, y:evt.clientY}; var d, srcEle = (evt.srcElement ? evt.srcElement : evt.target); if(isGecko){ d = srcEle.ownerDocument.defaultView }else{ d = srcEle.ownerDocument.parentWindow } var a,c; while(d != d.parent){ if(d.frameElement){ pos2 = $E.getPosition(d.frameElement); f.x += pos2.x; f.y += pos2.y } a = Math.max(d.document.body.scrollLeft, d.document.documentElement.scrollLeft); c = Math.max(d.document.body.scrollTop, d.document.documentElement.scrollTop); f.x -= a; f.y -= c; d = d.parent } return f } var $E = {}; $E.getPosition=function(m){ m=m||this; m=findElement(m); var k=m.ownerDocument; if(m.parentNode===null||m.style.display=="none"){ return false } var l=null; var j=[]; var g; if(m.getBoundingClientRect){ g=m.getBoundingClientRect(); var c=Math.max(k.documentElement.scrollTop,k.body.scrollTop); var d=Math.max(k.documentElement.scrollLeft,k.body.scrollLeft); var b=g.left+d-k.documentElement.clientLeft; var a=g.top+c-k.documentElement.clientTop; if(isIE){ b--; a-- } return {x:b,y:a } }else{ if(k.getBoxObjectFor){ g=k.getBoxObjectFor(m); var h=(m.style.borderLeftWidth)?parseInt(m.style.borderLeftWidth):0; var f=(m.style.borderTopWidth)?parseInt(m.style.borderTopWidth):0; j=[g.x-h,g.y-f] } } if(m.parentNode){ l=m.parentNode }else{ l=null } while(l&&l.tagName!="BODY"&&l.tagName!="HTML"){ j[0]-=l.scrollLeft; j[1]-=l.scrollTop; if(l.parentNode){ l=l.parentNode }else{ l=null } } return{x:j[0],y:j[1]} }; $E.getPositionEx=function(c){ c=c||this; c=findElement(c); var f=$E.getPosition(c); var d=window; var a,b; while(d!=d.parent){ if(d.frameElement){ pos2=$E.getPosition(d.frameElement); f.x+=pos2.x; f.y+=pos2.y } a=Math.max(d.document.body.scrollLeft,d.document.documentElement.scrollLeft); b=Math.max(d.document.body.scrollTop,d.document.documentElement.scrollTop); f.x-=a; f.y-=b; d=d.parent } return f }; $E.getParent=function(a,b){ b=b||this; b=findElement(b); while(b){ if(b.tagName.toLowerCase()==a.toLowerCase()){ return findElement(b) } b=b.parentElement } return null }; $E.getParentByAttr= function(a,c,b){ b=b||this; b=findElement(b); while(b){ if(b.getAttribute(a)==c){ return $(b) } b=b.parentElement } return null }; $E.getTopLevelWindow= function(){ var a=window; while(a!=a.parent){ a=a.parent } return a };
<html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <style type="text/css"> .suggestDiv { position: absolute; top: 0px; left: 0px; width: 150px; z-index: 101; background-color: #C0C0C0; border: 1px solid #000000; padding-left: 2px; overflow: visible; } span.spanMatchText { text-decoration: underline; font-weight: bold; } .suggestion { background: #C0C0C0; } span.spanHighElement { background: #000040; color: white; cursor: pointer; } span.noMatchData { font-weight: bold; color: #0000FF; } </style> <script type="text/javascript" src="jquery-1.7.2.min.js"></script> <script type="text/javascript" src="Base.js"></script> <script type="text/javascript" src="TextSuggest.js"></script> <script type="text/javascript" > window.onload = function(){ new TextSuggest('txtUserInput', '/textsuggest.do'); } </script> </head> <body> 自动提示: <input type="text" id="txtUserInput"/> </body> </html>
package com.conch.ark.platform.codegenerator; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * * @author Darkness * @date 2014-8-25 上午10:28:12 * @version V1.0 */ @Controller public class TestController { @ResponseBody @RequestMapping("/textsuggest.do") public List<Map<String, Object>> textsuggest(String query) { List<Map<String, Object>> result = new ArrayList<Map<String,Object>>(); Map<String, Object> topLevelDept = new HashMap<String, Object>(); topLevelDept.put("name", "总部"); topLevelDept.put("level", 1); topLevelDept.put("id", "topLevelDept"); result.add(topLevelDept); Map<String, Object> secondLevelDept = new HashMap<String, Object>(); secondLevelDept.put("name", "北京分部"); secondLevelDept.put("level", 2); secondLevelDept.put("id", "secondLevelDept"); result.add(secondLevelDept); Map<String, Object> secondLevelDept2 = new HashMap<String, Object>(); secondLevelDept2.put("name", "上海分部"); secondLevelDept2.put("level", 2); secondLevelDept2.put("id", "secondLevelDept2"); result.add(secondLevelDept2); return result; } }
发表评论
-
google浏览器书签账号登陆不上解决方案
2011-10-10 09:22 760问题: 最近google浏览器书签同步账号登陆不上 ... -
Dom多事件注册
2010-12-15 15:28 1182今天看到这样一段代码,如下: Page.clickFunc ... -
excanvas饼图实现
2010-10-21 15:04 2852代码比较简单,需要注意的一点就是: 如果想让饼图的边框显示出来 ... -
DOM中cloneNode的使用之旅
2010-10-21 14:52 2548struts2中可以自动封装表单提交过来的参数 List< ... -
Tab键控制页面中元素获取焦点顺序
2010-08-19 17:35 4099修改默认Tab键按键触发,界面元素获取焦点的顺序 此方法 ... -
js实现在图片上画矩形框
2010-08-18 16:10 10838JS组件: @author Darkness @versio ... -
JS控制输入字符长度
2009-11-09 10:50 5546<script language="Jav ... -
internet explorer 无法打开 Internet站点 已中止操作
2009-09-26 18:20 2155JavaScript使IE的经典异常 代码 http://ww ... -
fusioncharts相关问题
2009-09-26 15:59 1787中文问题 使用UTF-8 or GBK,X 轴正常, ... -
js日期时间函数
2009-09-20 17:28 1183Date.prototype.isLeapYear 判断闰 ... -
IE6 png 透明 (三种解决方法)
2009-09-14 14:49 2054FF和IE7已经直接支持透明的png图了,下面这个主要是解决I ... -
类似MSN的消息提示
2009-08-18 11:00 975<html> <head> ... -
IE6的“错误:53 存储空间不足,无法完成此操作”解决方法
2009-08-07 21:10 7994一法: 打开注册表, ... -
表格边框的隐藏
2009-08-07 09:19 2637代码如下: <html> <he ... -
JS操作VML
2009-08-03 14:52 2799可以用鼠标拖动这条线,效果如图: 说明:还有一些bug, ... -
javascript技巧大全 (3)
2009-03-06 12:36 1121... -
javascript技巧大全(2)
2009-03-06 12:33 1339进入页面<meta http-equiv="P ... -
javascript技巧大全(1)
2009-03-06 12:32 1194事件源对象 event.srcElement.tagName ... -
javascript性能优化
2009-02-27 10:56 4359很久就想总结一下关于javascript性能优化方面的一些东西 ... -
JavaScript DOM笔记:修改DOM
2009-02-27 10:51 2263常用函数: creat ...
相关推荐
"cocos2d-js弹窗组件"是这个框架的一个重要组成部分,它提供了方便易用的弹窗功能,极大地简化了在游戏或应用中创建对话框、提示信息等交互元素的过程。 在Cocos2d-js中,弹窗组件通常用于显示临时的通知、用户确认...
微信小程序-ToolTip信息提示组件导入将ToolTip文件夹复制到pages文件夹内使用在需要使用ToolTip的页面对应的.wxml文件中添加: src="../ToolTip/toolTip.wxml"/> <!-- 引入toolTip模板 --> is=...
Vue Autosuggest 是一款基于 Vue.js 的高效且可定制的自动提示组件,专为构建智能输入框而设计。这款组件能够帮助开发者轻松实现搜索建议、自动完成等交互功能,提升用户体验。在JavaScript开发中,尤其是在使用Vue....
7. **使用方法**:在uni-app项目中,首先需要通过npm或 yarn 安装插件,然后在需要使用该组件的页面中导入,并在模板中添加相应的标签,同时在对应的js文件中进行事件绑定和状态管理。 通过深入理解和正确使用"uni-...
本篇文章将详细探讨MyEclipse中JS(JavaScript)的自动提示功能,帮助开发者提升编码速度和准确性。 **1. JS自动提示的基本概念** JS自动提示,也称为代码补全或智能感知,是IDE为提高编程效率提供的一项功能。当...
HTML Overlay 是一种常见的前端开发技术,用于在网页上创建覆盖层,实现如提示框、弹窗、模态对话框等功能。在"html-overlay-master"组件中,它提供了一种高效且灵活的方式来实现这些效果。这个组件的核心是通过HTML...
总的来说,"js-ui-autocomplete"是一个集成在jQuery UI框架中的自动补全组件,通过结合使用这些JavaScript和CSS资源,开发者能够轻松地在网页中实现智能提示和自动完成功能,提升用户的交互体验。这个包不仅提供了...
- `index.js`: 这个JavaScript文件通常包含了组件的主要逻辑,包括组件的定义、属性绑定、事件处理等。在这里,你会看到`AutoCompleteField`组件的实现代码。 - `index.json`: JSON配置文件,定义了组件的对外属性...
本文将重点介绍一种强大的自动补全提示组件——Kissy Suggest,它是一个基于JavaScript的开源工具,适用于各种前端项目。 Kissy Suggest是Kissy框架的一部分,Kissy是一个轻量级的前端JavaScript库,旨在简化Web...
在这些组件中,"自动提示"功能是实现类似百度搜索框下拉框提示的关键功能,极大地提升了用户体验。 ### 1. 自动提示的基本概念 自动提示,也被称为自动完成或自动填充,是一种用户输入辅助技术。当用户在输入框中...
Element Plus 是一个基于 Vue.js 的开源 UI 组件库,提供了丰富的界面组件,如表格、按钮、下拉菜单等,用于快速构建现代化的前端应用。"element-plus打包" 指的是将 Element Plus 项目进行构建和打包的过程,以便在...
在本文中,我们将深入探讨这个Angular消息提示组件的特性、使用方法以及其在JavaScript开发中的重要性。 首先,这个组件的核心是基于Angular框架构建的,这意味着它可以无缝集成到任何Angular应用中,利用Angular的...
Vue2Autocomplete是针对Vue 2框架设计的一个高效、可定制化的文本输入自动完成组件。这个组件主要用于提高用户在输入时的效率,通过提供实时的建议列表,帮助用户快速找到并选择所需的内容。Vue.js是一个轻量级但...
通过结合jQuery,CUI组件库简化了JavaScript代码编写,使得开发者可以更快速地实现复杂的交互效果。 1. **组件介绍**: - **按钮(Buttons)**:提供基础的按钮样式,支持不同状态(如禁用、加载中)和类型(如...
该资源主要涵盖了 CSS 和 JavaScript 代码,旨在实现一个可以自动消失的消息提示框。 1. CSS 代码分析 在 CSS 代码中,我们可以看到一些重要的样式定义。例如,`.msg-box-mc` 类定义了字体大小为 14px 的样式,`....
1. **显示当前月份**:组件应能自动显示系统当前的月份和年份,允许用户向前或向后翻页选择其他月份。 2. **日期选择**:用户可以通过点击日期单元格选择特定日期,选中的日期应该有明显的视觉反馈,如改变背景色。...
"js组件操作手册"聚焦于如何利用JavaScript来创建和操作组件,以提高代码复用性和开发效率。本手册可能包含了如何设计、实现以及在JSP(JavaServer Pages)环境中集成这些组件的方法。 JSP组件是JavaWeb开发中的一...
Vue.js是一种流行的前端JavaScript框架,由尤雨溪开发,它以简洁的API和组件化设计赢得了开发者们的喜爱。在Web应用开发中,消息提示队列组件是不可或缺的一部分,它能够帮助用户更好地理解程序运行状态,提供操作...
总的来说,这个项目提供了一个基础的JavaScript自动完成解决方案,对于学习和实践JavaScript DOM操作、事件处理以及简单前端组件的构建是非常有价值的。开发者可以通过阅读和修改源代码,了解其工作原理,并将其定制...
Vue.js 是一款流行的前端JavaScript框架,它以组件化开发、数据双向绑定和简易上手而闻名。Vue.js-devtools 是一个强大的开发者工具,专为Vue应用程序的调试设计,它可以集成到Chrome浏览器中,帮助开发者更好地理解...