最近在ie8碰到一个js问题,需要实现(ie8)使用拼音或者拼音首字母来检索select中的内容,原来的combobox只能支持汉字输入检索,现在需要进行改进,现在我将一步一步的实现方法记录下来,功能简单,也可能有bug和不足,供学习参考。(本文只是提供思路学习和备份,实际情况需要在ie8或者ie兼容模式上使用,所以没有考虑到别的浏览器)
目录结构:
test
|--js
|--index.html
在index页面中添加
index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script type="text/javascript" src="https://www.jb51.net/js/autoComplete.js" ></script> <script type="text/javascript"> </script> </head> <body> <input type="text" /> <select> <option value="1">北京</option> <option value="2">上海</option> <option value="3">广州</option> <option value="4">深圳</option> <option value="5">重庆</option> <option value="6">天津</option> <option value="7">沈阳</option> <option value="8">南京</option> <option value="9">武汉</option> <option value="10">长春</option> <option value="11">成都</option> <option value="12">大连</option> <option value="13">杭州</option> <option value="14">青岛</option> <option value="15">a济南</option> <option value="16">厦门</option> <option value="17">福州</option> <option value="18">西安</option> <option value="19">长沙</option> <option value="20">哈尔滨</option> </select> </body> </html>
效果:开始将select 的下拉列表框隐藏,当点击input文本框的时候显示到input框的下面,选择完成后再将select隐藏。
js实现:
如果一个页面有多个地方需要实现这样的功能,这个时候就要使用面向对象的思维,尽可能代码重用,我们需要自定义一个ap这样的集合。
autoComplete.js
function Map() { /** 存放键的数组(遍历用到) */ this.keys = new Array(); /** 存放数据 */ this.data = new Object(); /** * 放入一个键值对 * @param {String} key * @param {Object} value */ this.put = function(key, value) { if(this.data[key] == null){ this.keys.push(key); } this.data[key] = value; }; /** * 获取某键对应的值 * @param {String} key * @return {Object} value */ this.get = function(key) { return this.data[key]; }; /** * 删除一个键值对 * @param {String} key */ this.remove = function(key) { this.keys.remove(key); this.data[key] = null; }; /** * 遍历Map,执行处理函数 * * @param {Function} 回调函数 function(key,value,index){..} */ this.each = function(fn){ if(typeof fn != 'function'){ return; } var len = this.keys.length; for(var i=0;i<len;i++){ var k = this.keys[i]; fn(k,this.data[k],i); } }; /** * 获取键值数组(类似Java的entrySet()) * @return 键值对象{key,value}的数组 */ this.entrys = function() { var len = this.keys.length; var entrys = new Array(len); for (var i = 0; i < len; i++) { entrys[i] = { key : this.keys[i], value : this.data[i] }; } return entrys; }; /** * 判断Map是否为空 */ this.isEmpty = function() { return this.keys.length == 0; }; /** * 获取键值对数量 */ this.size = function(){ return this.keys.length; }; /** * 重写toString */ this.toString = function(){ var s = "{"; for(var i=0;i<this.keys.length;i++,s+=','){ var k = this.keys[i]; s += k+"="+this.data[k]; } s+="}"; return s; }; } Array.prototype.remove = function(s) { for (var i = 0; i < this.length; i++) { if (s == this[i]) this.splice(i, 1); } }
现在我们要写一个程序加载入口文件,用来将input和select的对象传入,然后进行事件绑定等等一系列的操作。
var autoCompleteMap = new Map(); //组件容器,便于组件事件驱动时调用,同时支持多组件管理 var splitFleg = "_"; //分隔符 /** * 文本框,下拉框组合成自动补全组件 * @param {Object} txtObj 文本框对象 * @param {Object} selectObj 下拉框对象 * @param {int} selectSize 显示下拉框的数量 * @param {int} selectLength 下拉框的长度 */ function AutoComplete(txtObj, selectObj, selectSize, selectLength) { this.cacheContainer = new Array(); //缓存容器,用来在页面刚刚加载的时候将option中的内容缓存到cacheContainer中 this.init = function() { this.initCache(); //缓存数据,将option的数据缓存到cacheContainer this.initCSS(); //初始化css 将select隐藏 this.registerEvent(); //注册事件 this.setSelectIdPosition(); //设置select的位置 // 缓存当前组件,便于组件事件驱动时调用,同时支持多组件管理 autoCompleteMap.put(txtObj.id + selectObj.id, this); // 界面刷新后,将直属机构下拉框text值,写入文本框 var selectIndex = selectObj.selectedIndex; if (selectIndex > 0) //第一个内容一般是 【请选择】,如果没有则将>0改为>=0 txtObj.value = selectObj.options[selectIndex].text; } //缓存数据,将option的数据缓存到cacheContainer this.initCache = function() { var select_options = selectObj.options; if (select_options == null || select_options.length == 0) { return; } this.cacheContainer = []; for (var i = 0; i < select_options.length; i++) { this.cacheContainer[i] = select_options[i].text + splitFleg + select_options[i].value; } } this.initCSS = function() { selectObj.style.display = "none"; selectObj.style.position = "absolute"; selectObj.style.zIndex = 2; selectObj.style.width = selectLength + "px"; selectObj.multiple = "multiple"; txtObj.style.width = selectLength - 5 + "px"; } this.registerEvent = function() { // 下拉框事件 selectObj.ondblclick = this.doubleClickEvent; selectObj.onkeyup = this.keyupEvent; selectObj.onblur = this.OnblurEvent; selectObj.onfocus = this.OnfocusEvent; // 文本框事件 txtObj.onfocus = this.OnfocusEvent; txtObj.onblur = this.OnblurEvent; txtObj.onkeyup = this.txtObjKeyupEvent; } this.setSelectIdPosition = function() { var position = this.findPosition(txtObj); selectObj.style.left = position[0] + "px"; selectObj.style.top = position[3] + 3 + "px"; } this.findPosition = function(oElement) { var x2 = 0; var y2 = 0; var width = oElement.offsetWidth; var height = oElement.offsetHeight; if (typeof(oElement.offsetParent) != 'undefined') { for (var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent) { posX += oElement.offsetLeft; posY += oElement.offsetTop; } x2 = posX + width; y2 = posY + height; return [posX, posY, x2, y2]; } else { x2 = oElement.x + width; y2 = oElement.y + height; return [oElement.x, oElement.y, x2, y2]; } } //-----------------绑定的事件------------------------ /** * select下拉列表框双击事件 */ this.doubleClickEvent = function() { selectObj.style.display = "none"; var selectIndex = selectObj.selectedIndex; txtObj.value = selectObj.options[selectIndex].text; } /** * 鼠标点击松开事件 */ this.keyupEvent = function() { var autocomplete = autoCompleteMap.get(txtObj.id + selectObj.id); if (event.keyCode == 13) { event.returnValue = false; var srcElem = document.activeElement; //获取当前聚焦的对象 var testval = srcElem.id; if (testval == selectObj.id) { autocomplete.doubleClickEvent(); } } } /** * 聚焦事件 */ this.OnblurEvent = function() { var srcElem = document.activeElement; var testval = srcElem.id; if (testval != selectObj.id && testval != txtObj.id) { //如果没有聚焦到当前input框或者select列表 selectObj.style.display = "none"; //将select列表隐藏 } } /** * 聚焦事件 */ this.OnfocusEvent = function() { var autocomplete = autoCompleteMap.get(txtObj.id + selectObj.id); autocomplete.setSelectIdPosition(); var srcElem = document.activeElement; var testval = srcElem.id; if (testval == selectObj.id || testval == txtObj.id) { //聚焦在当前对象 if (txtObj.value.length != 0) { //当input框中存在字符,则不进行任何操作 return; } var selectIdLength = selectObj.options.length; if (selectIdLength > selectSize) { selectObj.size = selectSize; } else { selectObj.size = selectIdLength; } selectObj.style.display = "block"; } } var myTimeout = null; /** * 文本框鼠标聚焦松开事件 ,设置一个定时器,每个特定的时间执行函数,查询和input框中匹配到的select列表数据并显示 */ this.txtObjKeyupEvent = function() { var autocomplete = autoCompleteMap.get(txtObj.id + selectObj.id); if (event.keyCode == 40) { //input框中点击键盘方向键下,这个时候不需要进行检索,只有在输入的时候触发检索事件 var srcElem = document.activeElement; var testval = srcElem.id; if (testval == txtObj.id) { selectObj.focus(); if (selectObj.options.length >= 1) selectObj.options[0].selected = true; } return; } if (autocomplete.myTimeout != null) { //清空设置的定时执行事件 clearTimeout(autocomplete.myTimeout); } autocomplete.myTimeout = setTimeout(autocomplete.doAJAX, 200); } //----------------------------检索显示匹配数据----------------------------- /** * 做主要的查询匹配操作 */ this.doAJAX = function() { var autocomplete = autoCompleteMap.get(txtObj.id + selectObj.id); //清空原来的OPTIONS autocomplete.clearAllOptions(); autocomplete.setSelectIdPosition(); var inputStr = txtObj.value; var arrays = autocomplete.compareInput(inputStr); //匹配符合查询条件的数据 if (arrays == null || arrays.length == 0) { selectObj.style.display = "none"; return; } selectObj.style.display = "block"; for (var i = 0; i < arrays.length; i++) { var optionParams = arrays[i].split(splitFleg); var opt = new Option(); opt.text = optionParams[0]; opt.value = optionParams[1]; selectObj.add(opt); } if (arrays.length > selectSize) { selectObj.size = selectSize; } else { selectObj.size = arrays.length; } } /** * 清空原来的OPTIONS */ this.clearAllOptions = function() { //清空原来的OPTIONS var nL = selectObj.options.length; while (nL > 0) { selectObj.remove(selectObj.options.length - 1); nL = selectObj.options.length; } } //--------------------------数据检索规则--------------------- /** * 数据检索规则 * @param {String} inputStr input框中需要进行匹配的条件 */ this.compareInput = function(inputStr) { if (this.cacheContainer.length == 0) { return; } inputStr = inputStr.replace(/(^[\s]*)/g, ""); //去前边空白字符串 inputStr = this.deleteSpecialSpace(inputStr); //去除特殊空白字符串 if (inputStr == null || inputStr.length == 0) { return this.cacheContainer; } inputStr = disableSpecialCharacter(inputStr); //特殊字符处理 var resultArray = new Array(); var k = 0; var selectText = ""; for (var i = 0; i < this.cacheContainer.length; i++) { selectText = (this.cacheContainer[i].split(splitFleg)[0]).replace(/(^[\s]*)/g, ""); selectText = this.deleteSpecialSpace(selectText); if (compareRules(inputStr, selectText)) { //匹配规则 resultArray[k] = this.cacheContainer[i]; k++; } } return uniqueArray(resultArray); } /** * 去除特殊空白字符串 */ this.deleteSpecialSpace = function(srcStr) { var temp = ""; for (var i = 0; i < srcStr.length; i++) { var charStr = srcStr.charAt(i); // 界面特殊空格Unicode=160,此空格既不是全角,也非半角 if (charStr.charCodeAt(0) == 160) { continue; } temp += charStr; } return temp; } } /** * @param {String} inputStr 需要进行过滤的字符 * 特殊字符处理 */ function disableSpecialCharacter(inputStr) { inputStr = inputStr.replace(new RegExp("\\\\", 'g'), "\\\\"); inputStr = inputStr.replace(new RegExp("\\.", 'g'), "\\."); inputStr = inputStr.replace(new RegExp("\\^", 'g'), "\\^"); inputStr = inputStr.replace(new RegExp("\\{", 'g'), "\\{"); inputStr = inputStr.replace(new RegExp("\\[", 'g'), "\\["); inputStr = inputStr.replace(new RegExp("\\(", 'g'), "\\("); inputStr = inputStr.replace(new RegExp("\\|", 'g'), "\\|"); inputStr = inputStr.replace(new RegExp("\\]", 'g'), "\\]"); inputStr = inputStr.replace(new RegExp("\\)", 'g'), "\\)"); inputStr = inputStr.replace(new RegExp("\\*", 'g'), "\\*"); inputStr = inputStr.replace(new RegExp("\\+", 'g'), "\\+"); inputStr = inputStr.replace(new RegExp("\\?", 'g'), "\\?"); return inputStr; } /** * 匹配规则 * @param {String} inputStr input框字符,匹配条件 * @param {String} selectText 被匹配文字 */ function compareRules(inputStr, selectText) { //匹配汉字 return selectText.indexOf(inputStr) != -1 ; } /** * 过滤重复数据 * @param {Object} arr 结果数组 */ function uniqueArray(arr) { if(arr == null || arr.length == 0){ return arr; } return arr.reverse().join(",").match( /([^,]+)(?!.*\1)/ig).reverse(); } /** * 在原来onload的基础上加上自定义要执行的函数 * @param {Object} func 加载函数 */ function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = func; } else { window.onload = function() { oldonload(); func(); } } }
引入将汉字转换成拼音的工具js
pinYinHanZi.js