JavaScript打造城市选择控件

开发 前端
一、先用一定的格式罗列出控件所需要的城市以及拼音等,我这里是按照如下格式罗列成一个数组, 如果需要增加城市,直接增加在数组里面即可......

淘宝旅行上看到的城市选择效果,感觉还不错,就自己的理解重新实现一遍,先看效果(有人说IE9下面有BUG,LZ用的是落后的XP,居然装不上IE9,去公司在搞搞好了),然后再细说实现原理,支持鼠标上下键选择城市,支持直接输入城市名称,拼音首字母,全拼,支持IE6遮盖SELECT,压缩后12K, 

我实现的步骤:

一、先用一定的格式罗列出控件所需要的城市以及拼音等,我这里是按照如下格式罗列成一个数组, 如果需要增加城市,直接增加在数组里面即可:

城市我是一个一个手打的。。。

  1. ['北京|beijing|bj','上海|shanghai|sh', '重庆|chongqing|cq'] 

二、因为控件的城市分组按好几类划分,比如:按首字母HOT 、ABCDEFH  、  IJKLMNOP  、 QRSTUVWXYZ 四组划分,

而划分了四组后又按照了首字母划分,所以我用正则表达式和循环把数组重新格式化为一个分组对象,热门城市取前16条。

对象格式如下:

  1. {HOT:{hot:[]},ABCDEFGH:{a:[1,2,3],b:[1,2,3]},IJKLMNOP:{i:[1.2.3],j:[1,2,3]},QRSTUVWXYZ:{}} 

所用代码如下:

  1. /* *  
  2.  * 格式化城市数组为对象oCity,按照a-h,i-p,q-z,hot热门城市分组:  
  3.  * {HOT:{hot:[]},ABCDEFGH:{a:[1,2,3],b:[1,2,3]},IJKLMNOP:{i:[1.2.3],j:[1,2,3]},QRSTUVWXYZ:{}}  
  4.  * */ 
  5.  
  6. (function () {  
  7.     var citys = Vcity.allCity, match, letter,  
  8.         regEx = Vcity.regEx,  
  9.         reg2 = /^[a-h]$/i, reg3 = /^[i-p]$/i, reg4 = /^[q-z]$/i;  
  10.     if (!Vcity.oCity) {  
  11.         Vcity.oCity = {hot:{},ABCDEFGH:{}, IJKLMNOP:{}, QRSTUVWXYZ:{}};  
  12.         //console.log(citys.length);  
  13.         for (var i = 0, n = citys.length; i < n; i++) {  
  14.             match = regEx.exec(citys[i]);  
  15.             letter = match[3].toUpperCase();  
  16.             if (reg2.test(letter)) {  
  17.                 if (!Vcity.oCity.ABCDEFGH[letter]) Vcity.oCity.ABCDEFGH[letter] = [];  
  18.                 Vcity.oCity.ABCDEFGH[letter].push(match[1]);  
  19.             } else if (reg3.test(letter)) {  
  20.                 if (!Vcity.oCity.IJKLMNOP[letter]) Vcity.oCity.IJKLMNOP[letter] = [];  
  21.                 Vcity.oCity.IJKLMNOP[letter].push(match[1]);  
  22.             } else if (reg4.test(letter)) {  
  23.                 if (!Vcity.oCity.QRSTUVWXYZ[letter]) Vcity.oCity.QRSTUVWXYZ[letter] = [];  
  24.                 Vcity.oCity.QRSTUVWXYZ[letter].push(match[1]);  
  25.             }  
  26.             /* 热门城市 前16条 */ 
  27.             if(i<16){  
  28.                 if(!Vcity.oCity.hot['hot']) Vcity.oCity.hot['hot'] = [];  
  29.                 Vcity.oCity.hot['hot'].push(match[1]);  
  30.             }  
  31.         }  
  32.     }  
  33. })(); 

三、然后先照着淘宝旅行里面的样子弄出HTML与CSS;这里略过。

四、然后开始建立CitySelector构造函数,根据城市对象,构建生成DOM对象,在按照相应的事件触发。在生成相应的按照A\B\C\D……分组的时候遇到一个

关于排序的问题,我的对象格式是这样的ABCDEFGH:{a:[1,2,3],b:[1,2,3],c:[1,2,3]},里面的小数组要按照字母的顺序排序,但是我用for……in循环生成

出来是乱的,咨询了群里的高人后,处理方法如下:这里单独把KEY拿出来组成一个数组,然后排序后,在根据数组的值作为KEY值,来读取对象!

  1. sortKey=[];  
  2.             for(ckey in oCity[key]){  
  3.                 sortKey.push(ckey);  
  4.                 // ckey按照ABCDEDG顺序排序  
  5.                 sortKey.sort();  
  6.             }  
  7.             for(var j=0,k = sortKey.length;j<k;j++){  
  8.                 odl = document.createElement('dl');  
  9.                 odt = document.createElement('dt');  
  10.                 odd = document.createElement('dd');  
  11.                 odt.innerHTML = sortKey[j] == 'hot'?'&nbsp;':sortKey[j];  
  12.                 odda = [];  
  13.                 for(var i=0,n=oCity[key][sortKey[j]].length;i<n;i++){  
  14.                     str = '<a href="#">' + oCity[key][sortKey[j]][i] + '</a>';  
  15.                     odda.push(str);  
  16.                 } 

五、鼠标上下键移动选择城市的处理方法:在城市弹出后记录一个this.count = 0;然后再获取上下键的按键事件中分别对count值加一或者减一,

 当然count的***值不能大于筛选出来的城市数组的长度,超过长度后归0,小于0后赋值***值,然后把this.count的值,来作为数组的标获取相应的城市

项:

  1. switch(keycode){  
  2.             case 40: //向下箭头↓  
  3.                 this.count++;  
  4.                 if(this.count > len-1) this.count = 0;  
  5.                 for(var i=0;i<len;i++){  
  6.                     Vcity._m.removeClass('on',lis[i]);  
  7.                 }  
  8.                 Vcity._m.addClass('on',lis[this.count]);  
  9.                 break;  
  10.             case 38: //向上箭头↑  
  11.                 this.count--;  
  12.                 if(this.count<0) this.count = len-1;  
  13.                 for(i=0;i<len;i++){  
  14.                     Vcity._m.removeClass('on',lis[i]);  
  15.                 }  
  16.                 Vcity._m.addClass('on',lis[this.count]);  
  17.                 break;  
  18.             case 13: // enter键  
  19.                 this.input.value = Vcity.regExChiese.exec(lis[this.count].innerHTML)[0];  
  20.                 Vcity._m.addClass('hide',this.ul);  
  21.                 Vcity._m.addClass('hide',this.ul);  
  22.                 /* IE6 */ 
  23.                 Vcity._m.addClass('hide',this.myIframe);  
  24.                 break;  
  25.             default:  
  26.                 break;  
  27.         } 

六、IE中对SELECT的遮挡也是一个增加代码的地方,因为城市弹出框的大小是变化的,然后下拉的城市列也是根据筛选出来的值而变化,所以得每操作一个变化的

地方的时候就重新给iframe设置长度和宽度,苦逼的处理方法啊,所以就多了这样一个方法,然后在改变尺寸的时候,应用一下就可以了。

  1. /* IE6的改变遮罩SELECT 的 IFRAME尺寸大小 */ 
  2.     changeIframe:function(){  
  3.         if(!this.isIE6)return;  
  4.         this.myIframe.style.width = this.rootDiv.offsetWidth + 'px';  
  5.         this.myIframe.style.height = this.rootDiv.offsetHeight + 'px';  
  6.     }, 

7、弹出框的取消问题,这个问题最开始我是设置document的click事件关闭层,然后再弹出的层上阻止click事件的冒泡,但是这样两个层有同时出现的可能,

如这个反例:http://www.cnblogs.com/NNUF/archive/2012/06/24/2560557.html  ; 这里要感谢一下JS丛林群里面的‘搞搞破鞋’同学的方法,取消INPUT对

click的冒泡,然后关键是如下红色字体:

  1. // 设置点击文档隐藏弹出的城市选择框  
  2.         Vcity._m.on(document, 'click'function (event) {  
  3.             event = Vcity._m.getEvent(event);  
  4.             var target = Vcity._m.getTarget(event);  
  5.             if(target == that.input) return false;  
  6.             //console.log(target.className);  
  7.             if (that.cityBox)Vcity._m.addClass('hide', that.cityBox);  
  8.             if (that.ul)Vcity._m.addClass('hide', that.ul);  
  9.             if(that.myIframe)Vcity._m.addClass('hide',that.myIframe);  
  10.         }); 

8、输入框输入拼音或者文字或者拼音首字母筛选城市,这个就是直接用正则表达式在最开始的数组里面筛选数据即可:

  1. var reg = new RegExp("^" + value + "|\\|" + value, 'gi');              
  2.             var searchResult = [];  
  3.             for (var i = 0, n = Vcity.allCity.length; i < n; i++) {  
  4.                 if (reg.test(Vcity.allCity[i])) {  
  5.                     var match = Vcity.regEx.exec(Vcity.allCity[i]);  
  6.                     if (searchResult.length !== 0) {  
  7.                         str = '<li><b class="cityname">' + match[1] + '</b><b class="cityspell">' + match[2] + '</b></li>';  
  8.                     } else {  
  9.                         str = '<li class="on"><b class="cityname">' + match[1] + '</b><b class="cityspell">' + match[2] + '</b></li>';  
  10.                     }  
  11.                     searchResult.push(str);  
  12.                 }  
  13.             } 

#p#

然后总的JS代码如下:

  1. View Code   
  2.  
  3. /* *  
  4.  * ---------------------------------------- *  
  5.  * 城市选择组件 v1.0  
  6.  * Author: VVG  
  7.  * QQ: 83816819  
  8.  * Mail: mysheller@163.com  
  9.  * http://www.cnblogs.com/NNUF/  
  10.  * ---------------------------------------- *  
  11.  * Date: 2012-07-10  
  12.  * ---------------------------------------- *  
  13.  * */ 
  14.  
  15. /* *  
  16.  * 全局空间 Vcity  
  17.  * */ 
  18. var Vcity = {};  
  19. /* *  
  20.  * 静态方法集  
  21.  * @name _m  
  22.  * */ 
  23. Vcity._m = {  
  24.     /* 选择元素 */ 
  25.     $:function (arg, context) {  
  26.         var tagAll, n, eles = [], i, sub = arg.substring(1);  
  27.         context = context || document;  
  28.         if (typeof arg == 'string') {  
  29.             switch (arg.charAt(0)) {  
  30.                 case '#':  
  31.                     return document.getElementById(sub);  
  32.                     break;  
  33.                 case '.':  
  34.                     if (context.getElementsByClassName) return context.getElementsByClassName(sub);  
  35.                     tagAll = Vcity._m.$('*', context);  
  36.                     n = tagAll.length;  
  37.                     for (i = 0; i < n; i++) {  
  38.                         if (tagAll[i].className.indexOf(sub) > -1) eles.push(tagAll[i]);  
  39.                     }  
  40.                     return eles;  
  41.                     break;  
  42.                 default:  
  43.                     return context.getElementsByTagName(arg);  
  44.                     break;  
  45.             }  
  46.         }  
  47.     },  
  48.  
  49.     /* 绑定事件 */ 
  50.     on:function (node, type, handler) {  
  51.         node.addEventListener ? node.addEventListener(type, handler, false) : node.attachEvent('on' + type, handler);  
  52.     },  
  53.  
  54.     /* 获取事件 */ 
  55.     getEvent:function(event){  
  56.         return event || window.event;  
  57.     },  
  58.  
  59.     /* 获取事件目标 */ 
  60.     getTarget:function(event){  
  61.         return event.target || event.srcElement;  
  62.     },  
  63.  
  64.     /* 获取元素位置 */ 
  65.     getPos:function (node) {  
  66.         var scrollx = document.documentElement.scrollLeft || document.body.scrollLeft,  
  67.             scrollt = document.documentElement.scrollTop || document.body.scrollTop;  
  68.         var pos = node.getBoundingClientRect();  
  69.         return {top:pos.top + scrollt, right:pos.right + scrollx, bottom:pos.bottom + scrollt, left:pos.left + scrollx }  
  70.     },  
  71.  
  72.     /* 添加样式名 */ 
  73.     addClass:function (c, node) {  
  74.         if(!node)return;  
  75.         node.className = Vcity._m.hasClass(c,node) ? node.className : node.className + ' ' + c ;  
  76.     },  
  77.  
  78.     /* 移除样式名 */ 
  79.     removeClass:function (c, node) {  
  80.         var reg = new RegExp("(^|\\s+)" + c + "(\\s+|$)""g");  
  81.         if(!Vcity._m.hasClass(c,node))return;  
  82.         node.className = reg.test(node.className) ? node.className.replace(reg, '') : node.className;  
  83.     },  
  84.  
  85.     /* 是否含有CLASS */ 
  86.     hasClass:function (c, node) {  
  87.         if(!node || !node.className)return false;  
  88.         return node.className.indexOf(c)>-1;  
  89.     },  
  90.  
  91.     /* 阻止冒泡 */ 
  92.     stopPropagation:function (event) {  
  93.         event = event || window.event;  
  94.         event.stopPropagation ? event.stopPropagation() : event.cancelBubble = true;  
  95.     },  
  96.     /* 去除两端空格 */ 
  97.     trim:function (str) {  
  98.         return str.replace(/^\s+|\s+$/g,'');  
  99.     }  
  100. };  
  101.  
  102. /* 所有城市数据,可以按照格式自行添加(北京|beijing|bj),前16条为热门城市 */ 
  103.  
  104. Vcity.allCity = ['北京|beijing|bj','上海|shanghai|sh''重庆|chongqing|cq',  '深圳|shenzhen|sz''广州|guangzhou|gz''杭州|hangzhou|hz',  
  105.     '南京|nanjing|nj''苏州|shuzhou|sz''天津|tianjin|tj''成都|chengdu|cd''南昌|nanchang|nc''三亚|sanya|sy','青岛|qingdao|qd',  
  106.     '厦门|xiamen|xm''西安|xian|xa','长沙|changsha|cs','合肥|hefei|hf','西藏|xizang|xz''内蒙古|neimenggu|nmg''安庆|anqing|aq''阿泰勒|ataile|atl''安康|ankang|ak',  
  107.     '阿克苏|akesu|aks''包头|baotou|bt''北海|beihai|bh''百色|baise|bs','保山|baoshan|bs''长治|changzhi|cz''长春|changchun|cc''常州|changzhou|cz''昌都|changdu|cd',  
  108.     '朝阳|chaoyang|cy''常德|changde|cd''长白山|changbaishan|cbs''赤峰|chifeng|cf''大同|datong|dt''大连|dalian|dl''达县|daxian|dx''东营|dongying|dy''大庆|daqing|dq''丹东|dandong|dd',  
  109.     '大理|dali|dl''敦煌|dunhuang|dh''鄂尔多斯|eerduosi|eeds''恩施|enshi|es''福州|fuzhou|fz''阜阳|fuyang|fy''贵阳|guiyang|gy',  
  110.     '桂林|guilin|gl''广元|guangyuan|gy''格尔木|geermu|gem''呼和浩特|huhehaote|hhht''哈密|hami|hm',  
  111.     '黑河|heihe|hh''海拉尔|hailaer|hle''哈尔滨|haerbin|heb''海口|haikou|hk''黄山|huangshan|hs''邯郸|handan|hd',  
  112.     '汉中|hanzhong|hz''和田|hetian|ht''晋江|jinjiang|jj''锦州|jinzhou|jz''景德镇|jingdezhen|jdz',  
  113.     '嘉峪关|jiayuguan|jyg''井冈山|jinggangshan|jgs''济宁|jining|jn''九江|jiujiang|jj''佳木斯|jiamusi|jms''济南|jinan|jn',  
  114.     '喀什|kashi|ks''昆明|kunming|km''康定|kangding|kd''克拉玛依|kelamayi|klmy''库尔勒|kuerle|kel''库车|kuche|kc''兰州|lanzhou|lz',  
  115.     '洛阳|luoyang|ly''丽江|lijiang|lj''林芝|linzhi|lz''柳州|liuzhou|lz''泸州|luzhou|lz''连云港|lianyungang|lyg''黎平|liping|lp',  
  116.     '连成|liancheng|lc''拉萨|lasa|ls''临沧|lincang|lc''临沂|linyi|ly''芒市|mangshi|ms''牡丹江|mudanjiang|mdj''满洲里|manzhouli|mzl''绵阳|mianyang|my',  
  117.     '梅县|meixian|mx''漠河|mohe|mh''南充|nanchong|nc''南宁|nanning|nn''南阳|nanyang|ny''南通|nantong|nt''那拉提|nalati|nlt',  
  118.     '宁波|ningbo|nb''攀枝花|panzhihua|pzh''衢州|quzhou|qz''秦皇岛|qinhuangdao|qhd''庆阳|qingyang|qy''齐齐哈尔|qiqihaer|qqhe',  
  119.     '石家庄|shijiazhuang|sjz',  '沈阳|shenyang|sy''思茅|simao|sm''铜仁|tongren|tr''塔城|tacheng|tc''腾冲|tengchong|tc''台州|taizhou|tz',  
  120.     '通辽|tongliao|tl''太原|taiyuan|ty''威海|weihai|wh''梧州|wuzhou|wz''文山|wenshan|ws''无锡|wuxi|wx''潍坊|weifang|wf''武夷山|wuyishan|wys''乌兰浩特|wulanhaote|wlht',  
  121.     '温州|wenzhou|wz''乌鲁木齐|wulumuqi|wlmq''万州|wanzhou|wz''乌海|wuhai|wh''兴义|xingyi|xy''西昌|xichang|xc',  '襄樊|xiangfan|xf',  
  122.     '西宁|xining|xn''锡林浩特|xilinhaote|xlht''西双版纳|xishuangbanna|xsbn''徐州|xuzhou|xz''义乌|yiwu|yw''永州|yongzhou|yz''榆林|yulin|yl''延安|yanan|ya''运城|yuncheng|yc',  
  123.     '烟台|yantai|yt''银川|yinchuan|yc''宜昌|yichang|yc''宜宾|yibin|yb''盐城|yancheng|yc''延吉|yanji|yj''玉树|yushu|ys''伊宁|yining|yn''珠海|zhuhai|zh''昭通|zhaotong|zt',  
  124.     '张家界|zhangjiajie|zjj''舟山|zhoushan|zs''郑州|zhengzhou|zz''中卫|zhongwei|zw''芷江|zhijiang|zj''湛江|zhanjiang|zj'];  
  125.  
  126. /* 正则表达式 筛选中文城市名、拼音、首字母 */ 
  127.  
  128. Vcity.regEx = /^([\u4E00-\u9FA5\uf900-\ufa2d]+)\|(\w+)\|(\w)\w*$/i;  
  129. Vcity.regExChiese = /([\u4E00-\u9FA5\uf900-\ufa2d]+)/;  
  130.  
  131. /* *  
  132.  * 格式化城市数组为对象oCity,按照a-h,i-p,q-z,hot热门城市分组:  
  133.  * {HOT:{hot:[]},ABCDEFGH:{a:[1,2,3],b:[1,2,3]},IJKLMNOP:{i:[1.2.3],j:[1,2,3]},QRSTUVWXYZ:{}}  
  134.  * */ 
  135.  
  136. (function () {  
  137.     var citys = Vcity.allCity, match, letter,  
  138.         regEx = Vcity.regEx,  
  139.         reg2 = /^[a-h]$/i, reg3 = /^[i-p]$/i, reg4 = /^[q-z]$/i;  
  140.     if (!Vcity.oCity) {  
  141.         Vcity.oCity = {hot:{},ABCDEFGH:{}, IJKLMNOP:{}, QRSTUVWXYZ:{}};  
  142.         //console.log(citys.length);  
  143.         for (var i = 0, n = citys.length; i < n; i++) {  
  144.             match = regEx.exec(citys[i]);  
  145.             letter = match[3].toUpperCase();  
  146.             if (reg2.test(letter)) {  
  147.                 if (!Vcity.oCity.ABCDEFGH[letter]) Vcity.oCity.ABCDEFGH[letter] = [];  
  148.                 Vcity.oCity.ABCDEFGH[letter].push(match[1]);  
  149.             } else if (reg3.test(letter)) {  
  150.                 if (!Vcity.oCity.IJKLMNOP[letter]) Vcity.oCity.IJKLMNOP[letter] = [];  
  151.                 Vcity.oCity.IJKLMNOP[letter].push(match[1]);  
  152.             } else if (reg4.test(letter)) {  
  153.                 if (!Vcity.oCity.QRSTUVWXYZ[letter]) Vcity.oCity.QRSTUVWXYZ[letter] = [];  
  154.                 Vcity.oCity.QRSTUVWXYZ[letter].push(match[1]);  
  155.             }  
  156.             /* 热门城市 前16条 */ 
  157.             if(i<16){  
  158.                 if(!Vcity.oCity.hot['hot']) Vcity.oCity.hot['hot'] = [];  
  159.                 Vcity.oCity.hot['hot'].push(match[1]);  
  160.             }  
  161.         }  
  162.     }  
  163. })();  
  164. /* 城市HTML模板 */ 
  165. Vcity._template = [  
  166.     '<p class="tip">热门城市(支持汉字/拼音)</p>',  
  167.     '<ul>',  
  168.     '<li class="on">热门城市</li>',  
  169.     '<li>ABCDEFGH</li>',  
  170.     '<li>IJKLMNOP</li>',  
  171.     '<li>QRSTUVWXYZ</li>',  
  172.     '</ul>' 
  173. ];  
  174.  
  175. /* *  
  176.  * 城市控件构造函数  
  177.  * @CitySelector  
  178.  * */ 
  179.  
  180. Vcity.CitySelector = function () {  
  181.     this.initialize.apply(this, arguments);  
  182. };  
  183.  
  184. Vcity.CitySelector.prototype = {  
  185.  
  186.     constructor:Vcity.CitySelector,  
  187.  
  188.     /* 初始化 */ 
  189.  
  190.     initialize :function (options) {  
  191.         var input = options.input;  
  192.         this.input = Vcity._m.$('#'+ input);  
  193.         this.inputEvent();  
  194.     },  
  195.  
  196.     /* *  
  197.      * @createWarp  
  198.      * 创建城市BOX HTML 框架  
  199.      * */ 
  200.  
  201.     createWarp:function(){  
  202.         var inputPos = Vcity._m.getPos(this.input);  
  203.         var div = this.rootDiv = document.createElement('div');  
  204.         var that = this;  
  205.  
  206.         // 设置DIV阻止冒泡  
  207.         Vcity._m.on(this.rootDiv,'click',function(event){  
  208.             Vcity._m.stopPropagation(event);  
  209.         });  
  210.  
  211.         // 设置点击文档隐藏弹出的城市选择框  
  212.         Vcity._m.on(document, 'click'function (event) {  
  213.             event = Vcity._m.getEvent(event);  
  214.             var target = Vcity._m.getTarget(event);  
  215.             if(target == that.input) return false;  
  216.             //console.log(target.className);  
  217.             if (that.cityBox)Vcity._m.addClass('hide', that.cityBox);  
  218.             if (that.ul)Vcity._m.addClass('hide', that.ul);  
  219.             if(that.myIframe)Vcity._m.addClass('hide',that.myIframe);  
  220.         });  
  221.         div.className = 'citySelector';  
  222.         div.style.position = 'absolute';  
  223.         div.style.left = inputPos.left + 'px';  
  224.         div.style.top = inputPos.bottom + 'px';  
  225.         div.style.zIndex = 999999;  
  226.  
  227.         // 判断是否IE6,如果是IE6需要添加iframe才能遮住SELECT框  
  228.         var isIe = (document.all) ? true : false;  
  229.         var isIE6 = this.isIE6 = isIe && !window.XMLHttpRequest;  
  230.         if(isIE6){  
  231.             var myIframe = this.myIframe =  document.createElement('iframe');  
  232.             myIframe.frameborder = '0';  
  233.             myIframe.src = 'about:blank';  
  234.             myIframe.style.position = 'absolute';  
  235.             myIframe.style.zIndex = '-1';  
  236.             this.rootDiv.appendChild(this.myIframe);  
  237.         }  
  238.  
  239.         var childdiv = this.cityBox = document.createElement('div');  
  240.         childdiv.className = 'cityBox';  
  241.         childdiv.id = 'cityBox';  
  242.         childdiv.innerHTML = Vcity._template.join('');  
  243.         var hotCity = this.hotCity =  document.createElement('div');  
  244.         hotCity.className = 'hotCity';  
  245.         childdiv.appendChild(hotCity);  
  246.         div.appendChild(childdiv);  
  247.         this.createHotCity();  
  248.     },  
  249.  
  250.     /* *  
  251.      * @createHotCity  
  252.      * TAB下面DIV:hot,a-h,i-p,q-z 分类HTML生成,DOM操作  
  253.      * {HOT:{hot:[]},ABCDEFGH:{a:[1,2,3],b:[1,2,3]},IJKLMNOP:{},QRSTUVWXYZ:{}}  
  254.      **/ 
  255.  
  256.     createHotCity:function(){  
  257.         var odiv,odl,odt,odd,odda=[],str,key,ckey,sortKey,regEx = Vcity.regEx,  
  258.             oCity = Vcity.oCity;  
  259.         for(key in oCity){  
  260.             odiv = this[key] = document.createElement('div');  
  261.             // 先设置全部隐藏hide  
  262.             odiv.className = key + ' ' + 'cityTab hide';  
  263.             sortKey=[];  
  264.             for(ckey in oCity[key]){  
  265.                 sortKey.push(ckey);  
  266.                 // ckey按照ABCDEDG顺序排序  
  267.                 sortKey.sort();  
  268.             }  
  269.             for(var j=0,k = sortKey.length;j<k;j++){  
  270.                 odl = document.createElement('dl');  
  271.                 odt = document.createElement('dt');  
  272.                 odd = document.createElement('dd');  
  273.                 odt.innerHTML = sortKey[j] == 'hot'?'&nbsp;':sortKey[j];  
  274.                 odda = [];  
  275.                 for(var i=0,n=oCity[key][sortKey[j]].length;i<n;i++){  
  276.                     str = '<a href="javascript:">' + oCity[key][sortKey[j]][i] + '</a>';  
  277.                     odda.push(str);  
  278.                 }  
  279.                 odd.innerHTML = odda.join('');  
  280.                 odl.appendChild(odt);  
  281.                 odl.appendChild(odd);  
  282.                 odiv.appendChild(odl);  
  283.             }  
  284.  
  285.             // 移除热门城市的隐藏CSS  
  286.             Vcity._m.removeClass('hide',this.hot);  
  287.             this.hotCity.appendChild(odiv);  
  288.         }  
  289.         document.body.appendChild(this.rootDiv);  
  290.         /* IE6 */ 
  291.         this.changeIframe();  
  292.  
  293.         this.tabChange();  
  294.         this.linkEvent();  
  295.     },  
  296.  
  297.     /* *  
  298.      *  tab按字母顺序切换  
  299.      *  @ tabChange  
  300.      * */ 
  301.  
  302.     tabChange:function(){  
  303.         var lis = Vcity._m.$('li',this.cityBox);  
  304.         var divs = Vcity._m.$('div',this.hotCity);  
  305.         var that = this;  
  306.         for(var i=0,n=lis.length;i<n;i++){  
  307.             lis[i].index = i;  
  308.             lis[i].onclick = function(){  
  309.                 for(var j=0;j<n;j++){  
  310.                     Vcity._m.removeClass('on',lis[j]);  
  311.                     Vcity._m.addClass('hide',divs[j]);  
  312.                 }  
  313.                 Vcity._m.addClass('on',this);  
  314.                 Vcity._m.removeClass('hide',divs[this.index]);  
  315.                 /* IE6 改变TAB的时候 改变Iframe 大小*/ 
  316.                 that.changeIframe();  
  317.             };  
  318.         }  
  319.     },  
  320.  
  321.     /* *  
  322.      * 城市LINK事件  
  323.      *  @linkEvent  
  324.      * */ 
  325.  
  326.     linkEvent:function(){  
  327.         var links = Vcity._m.$('a',this.hotCity);  
  328.         var that = this;  
  329.         for(var i=0,n=links.length;i<n;i++){  
  330.             links[i].onclick = function(){  
  331.                 that.input.value = this.innerHTML;  
  332.                 Vcity._m.addClass('hide',that.cityBox);  
  333.                 /* 点击城市名的时候隐藏myIframe */ 
  334.                 Vcity._m.addClass('hide',that.myIframe);  
  335.             }  
  336.         }  
  337.     },  
  338.  
  339.     /* *  
  340.      * INPUT城市输入框事件  
  341.      * @inputEvent  
  342.      * */ 
  343.  
  344.     inputEvent:function(){  
  345.         var that = this;  
  346.         Vcity._m.on(this.input,'click',function(event){  
  347.             event = event || window.event;  
  348.             if(!that.cityBox){  
  349.                 that.createWarp();  
  350.             }else if(!!that.cityBox && Vcity._m.hasClass('hide',that.cityBox)){  
  351.                 // slideul 不存在或者 slideul存在但是是隐藏的时候 两者不能共存  
  352.                 if(!that.ul || (that.ul && Vcity._m.hasClass('hide',that.ul))){  
  353.                     Vcity._m.removeClass('hide',that.cityBox);  
  354.  
  355.                     /* IE6 移除iframe 的hide 样式 */ 
  356.                     //alert('click');  
  357.                     Vcity._m.removeClass('hide',that.myIframe);  
  358.                     that.changeIframe();  
  359.                 }  
  360.             }  
  361.         });  
  362.         Vcity._m.on(this.input,'focus',function(){  
  363.             that.input.select();  
  364.             if(that.input.value == '城市名') that.input.value = '';  
  365.         });  
  366.         Vcity._m.on(this.input,'blur',function(){  
  367.             if(that.input.value == '') that.input.value = '城市名';  
  368.         });  
  369.         Vcity._m.on(this.input,'keyup',function(event){  
  370.             event = event || window.event;  
  371.             var keycode = event.keyCode;  
  372.             Vcity._m.addClass('hide',that.cityBox);  
  373.             that.createUl();  
  374.  
  375.             /* 移除iframe 的hide 样式 */ 
  376.             Vcity._m.removeClass('hide',that.myIframe);  
  377.  
  378.             // 下拉菜单显示的时候捕捉按键事件  
  379.             if(that.ul && !Vcity._m.hasClass('hide',that.ul) && !that.isEmpty){  
  380.                 that.KeyboardEvent(event,keycode);  
  381.             }  
  382.         });  
  383.     },  
  384.  
  385.     /* *  
  386.      * 生成下拉选择列表  
  387.      * @ createUl  
  388.      * */ 
  389.  
  390.     createUl:function () {  
  391.         //console.log('createUL');  
  392.         var str;  
  393.         var value = Vcity._m.trim(this.input.value);  
  394.         // 当value不等于空的时候执行  
  395.         if (value !== '') {  
  396.             var reg = new RegExp("^" + value + "|\\|" + value, 'gi');  
  397.             var searchResult = [];  
  398.             for (var i = 0, n = Vcity.allCity.length; i < n; i++) {  
  399.                 if (reg.test(Vcity.allCity[i])) {  
  400.                     var match = Vcity.regEx.exec(Vcity.allCity[i]);  
  401.                     if (searchResult.length !== 0) {  
  402.                         str = '<li><b class="cityname">' + match[1] + '</b><b class="cityspell">' + match[2] + '</b></li>';  
  403.                     } else {  
  404.                         str = '<li class="on"><b class="cityname">' + match[1] + '</b><b class="cityspell">' + match[2] + '</b></li>';  
  405.                     }  
  406.                     searchResult.push(str);  
  407.                 }  
  408.             }  
  409.             this.isEmpty = false;  
  410.             // 如果搜索数据为空  
  411.             if (searchResult.length == 0) {  
  412.                 this.isEmpty = true;  
  413.                 str = '<li class="empty">对不起,没有找到数据 "<em>' + value + '</em>"</li>';  
  414.                 searchResult.push(str);  
  415.             }  
  416.             // 如果slideul不存在则添加ul  
  417.             if (!this.ul) {  
  418.                 var ul = this.ul = document.createElement('ul');  
  419.                 ul.className = 'cityslide';  
  420.                 this.rootDiv && this.rootDiv.appendChild(ul);  
  421.                 // 记录按键次数,方向键  
  422.                 this.count = 0;  
  423.             } else if (this.ul && Vcity._m.hasClass('hide'this.ul)) {  
  424.                 this.count = 0;  
  425.                 Vcity._m.removeClass('hide'this.ul);  
  426.             }  
  427.             this.ul.innerHTML = searchResult.join('');  
  428.  
  429.             /* IE6 */ 
  430.             this.changeIframe();  
  431.  
  432.             // 绑定Li事件  
  433.             this.liEvent();  
  434.         }else{  
  435.             Vcity._m.addClass('hide',this.ul);  
  436.             Vcity._m.removeClass('hide',this.cityBox);  
  437.  
  438.             Vcity._m.removeClass('hide',this.myIframe);  
  439.  
  440.             this.changeIframe();  
  441.         }  
  442.     },  
  443.  
  444.     /* IE6的改变遮罩SELECT 的 IFRAME尺寸大小 */ 
  445.     changeIframe:function(){  
  446.         if(!this.isIE6)return;  
  447.         this.myIframe.style.width = this.rootDiv.offsetWidth + 'px';  
  448.         this.myIframe.style.height = this.rootDiv.offsetHeight + 'px';  
  449.     },  
  450.  
  451.     /* *  
  452.      * 特定键盘事件,上、下、Enter键  
  453.      * @ KeyboardEvent  
  454.      * */ 
  455.  
  456.     KeyboardEvent:function(event,keycode){  
  457.         var lis = Vcity._m.$('li',this.ul);  
  458.         var len = lis.length;  
  459.         switch(keycode){  
  460.             case 40: //向下箭头↓  
  461.                 this.count++;  
  462.                 if(this.count > len-1) this.count = 0;  
  463.                 for(var i=0;i<len;i++){  
  464.                     Vcity._m.removeClass('on',lis[i]);  
  465.                 }  
  466.                 Vcity._m.addClass('on',lis[this.count]);  
  467.                 break;  
  468.             case 38: //向上箭头↑  
  469.                 this.count--;  
  470.                 if(this.count<0) this.count = len-1;  
  471.                 for(i=0;i<len;i++){  
  472.                     Vcity._m.removeClass('on',lis[i]);  
  473.                 }  
  474.                 Vcity._m.addClass('on',lis[this.count]);  
  475.                 break;  
  476.             case 13: // enter键  
  477.                 this.input.value = Vcity.regExChiese.exec(lis[this.count].innerHTML)[0];  
  478.                 Vcity._m.addClass('hide',this.ul);  
  479.                 Vcity._m.addClass('hide',this.ul);  
  480.                 /* IE6 */ 
  481.                 Vcity._m.addClass('hide',this.myIframe);  
  482.                 break;  
  483.             default:  
  484.                 break;  
  485.         }  
  486.     },  
  487.  
  488.     /* *  
  489.      * 下拉列表的li事件  
  490.      * @ liEvent  
  491.      * */ 
  492.  
  493.     liEvent:function(){  
  494.         var that = this;  
  495.         var lis = Vcity._m.$('li',this.ul);  
  496.         for(var i = 0,n = lis.length;i < n;i++){  
  497.             Vcity._m.on(lis[i],'click',function(event){  
  498.                 event = Vcity._m.getEvent(event);  
  499.                 var target = Vcity._m.getTarget(event);  
  500.                 that.input.value = Vcity.regExChiese.exec(target.innerHTML)[0];  
  501.                 Vcity._m.addClass('hide',that.ul);  
  502.                 /* IE6 下拉菜单点击事件 */ 
  503.                 Vcity._m.addClass('hide',that.myIframe);  
  504.             });  
  505.             Vcity._m.on(lis[i],'mouseover',function(event){  
  506.                 event = Vcity._m.getEvent(event);  
  507.                 var target = Vcity._m.getTarget(event);  
  508.                 Vcity._m.addClass('on',target);  
  509.             });  
  510.             Vcity._m.on(lis[i],'mouseout',function(event){  
  511.                 event = Vcity._m.getEvent(event);  
  512.                 var target = Vcity._m.getTarget(event);  
  513.                 Vcity._m.removeClass('on',target);  
  514.             })  
  515.         }  
  516.     }  
  517. }; 

调用方法:只需传入需要填写城市的text表单的ID即可,如下:

  1. var test=new Vcity.CitySelector({input:'citySelect'});  
  2.  var test2=new Vcity.CitySelector({input:'citySelect1'}); 

如有代码某处有好的改进建议,请留言告知,感激不进! 

源代码下载:城市选择控件.rar

原文链接:http://www.cnblogs.com/NNUF/archive/2012/07/13/2590877.html

【编辑推荐】

  1. JavaScript,只有你想不到
  2. JavaScript面试后的反思
  3. JavaScript制作新浪网易的评论块
  4. 能说明你的JS技术很烂的五个原因
  5. 不用再担心JS文件限制你的速度
责任编辑:张伟 来源: VVG的博客
相关推荐

2013-01-24 11:42:56

IBM

2013-05-15 16:09:50

智慧城市通信网络电信日

2021-03-15 11:15:36

物联网智慧城市IoT

2009-08-04 14:00:56

ASP.NET弹出式日

2021-08-11 15:49:34

大数据智慧城市大数据分析

2020-08-10 10:31:18

物联网5G智慧城市

2016-03-16 16:54:46

视频监控系统华为中国合作伙伴大会

2021-03-31 11:52:47

智能数字城市智慧城市物联网

2014-04-23 14:20:04

2015-10-08 16:54:37

美团城市选择android

2014-12-31 16:20:02

时间选择日历Android

2015-09-16 10:42:06

华三/新IT

2022-03-16 10:20:57

数据智慧城市传感器

2016-12-05 14:36:53

WIFI无线城市

2022-08-31 14:39:47

物联网智慧城市大数据

2014-04-14 15:59:11

浪潮常德智慧城市

2018-08-22 11:01:19

华为智博会深圳

2020-05-21 18:31:42

智慧城市物联网5G

2017-07-05 16:09:25

LED智慧城市
点赞
收藏

51CTO技术栈公众号