龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > web编程 > Javascript编程 >

JavaScript打造城市选择控件(1)

时间:2013-03-06 14:58来源:未知 作者:admin 点击:
分享到:
在 淘宝旅行 上看到的城市选择效果,感觉还不错,就自己的理解重新实现一遍,先看效果(有人说IE9下面有BUG,LZ用的是落后的XP,居然装不上IE9,去公司在搞搞好了),然后再细说实

淘宝旅行上看到的城市选择效果,感觉还不错,就自己的理解重新实现一遍,先看效果(有人说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对象,在按照相应的事件触发。在生成相应的按照ABCD……分组的时候遇到一个

关于排序的问题,我的对象格式是这样的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.             } 

精彩图集

赞助商链接