四级地址插件升级改造(京东商城地址选择插件)city-picker
最近公司做的项目要和京东的数据做对接,所以要做个类似京东商品的详情页。页面的数据,是可以从京东接口获取到的,但是地址插件选择的效果需要自己实现。前端的同事在之前的项目中,已经选择了一款地址插件(city-picker.js),但是这款插件最多只支持三级地址,而且最主要的是这插件的地址数据来源,是写死在一个json文件中的,意思就是说,在使用这个插件的时候页面要一次性的把所有的地址数据都加载出来,这在pc端一般倒还可以承受,但是到了,移动端,随便一个手机就会卡死,浏览器直接崩溃。 经过在网上的各种查找,和研究,发现一个博客,http://www.cnblogs.com/huangchanghuan/p/6681510.html 对city-picker这个插件进行了扩展,扩展成了支持四级地址的插件了。这正是我想要的,因为京东给过来的地址数据就是4级的。正好可以使用。然后就拿过来直接用了。 很强大,完美的满足了,我的需求。但是这个大神的博客只是将三级地址改造成了四级地址,没有解决,动态加载数据的问题,就是说用这个四级地址插件的时候,还是要把京东的地址库数据转成json文件一次性加载到页面。这样的话在移动端浏览时还是会把浏览器搞崩。 好了,说了这么多铺垫的废话,就是为了引出,我对这个四级地址插件的改造。 直接代码 1 /*! 2 * CityPicker v@VERSION 3 * https://github.com/tshi0912/citypicker 4 * 5 * Copyright (c) 2015-@YEAR Tao Shi 6 * Released under the MIT license 7 8 * Date: @DATE 9 */ 10 11 12 ChineseDistricts={ 13 "86": { 14 "中国": [ 15 { 16 "address": "北京",17 "code": "1" 18 },19 { 20 "address": "上海",21 "code": "2" 22 },23 { 24 "address": "天津",25 "code": "3" 26 },27 { 28 "address": "重庆",29 "code": "4" 30 },31 { 32 "address": "河北",33 "code": "5" 34 },35 { 36 "address": "山西",37 "code": "6" 38 },39 { 40 "address": "河南",41 "code": "7" 42 },43 { 44 "address": "辽宁",45 "code": "8" 46 },47 { 48 "address": "吉林",49 "code": "9" 50 },51 { 52 "address": "黑龙江",53 "code": "10" 54 },55 { 56 "address": "内蒙古",57 "code": "11" 58 },59 { 60 "address": "江苏",61 "code": "12" 62 },63 { 64 "address": "山东",65 "code": "13" 66 },67 { 68 "address": "安徽",69 "code": "14" 70 },71 { 72 "address": "浙江",73 "code": "15" 74 },75 { 76 "address": "福建",77 "code": "16" 78 },79 { 80 "address": "湖北",81 "code": "17" 82 },83 { 84 "address": "湖南",85 "code": "18" 86 },87 { 88 "address": "广东",89 "code": "19" 90 },91 { 92 "address": "广西",93 "code": "20" 94 },95 { 96 "address": "江西",97 "code": "21" 98 },99 { 100 "address": "四川",101 "code": "22" 102 },103 { 104 "address": "海南",105 "code": "23" 106 },107 { 108 "address": "贵州",109 "code": "24" 110 },111 { 112 "address": "云南",113 "code": "25" 114 },115 { 116 "address": "西藏",117 "code": "26" 118 },119 { 120 "address": "陕西",121 "code": "27" 122 },123 { 124 "address": "甘肃",125 "code": "28" 126 },127 { 128 "address": "青海",129 "code": "29" 130 },131 { 132 "address": "宁夏",133 "code": "30" 134 },135 { 136 "address": "新疆",137 "code": "31" 138 },139 { 140 "address": "台湾",141 "code": "32" 142 },143 { 144 "address": "钓鱼岛",145 "code": "84" 146 },147 { 148 "address": "港澳",149 "code": "52993" 150 } 151 ] 152 } 153 }; 154 155 (function (factory) { 156 if (typeof define === 'function' && define.amd) { 157 // AMD. Register as anonymous module. 158 define(['jquery','ChineseDistricts'],factory); 159 } else typeof exports === 'object') { 160 Node / CommonJS 161 factory(require('jquery'),require('ChineseDistricts')); 162 } else { 163 Browser globals. 164 factory(jQuery,ChineseDistricts); 165 } 166 })( ($,ChineseDistricts) { 167 168 'use strict'; 169 typeof ChineseDistricts === 'undefined'170 throw new Error('The file "city-picker.data.js" must be included first!'); 171 172 var NAMESPACE = 'citypicker'173 var EVENT_CHANGE = 'change.' + NAMESPACE; 174 var PROVINCE = 'province'175 var CITY = 'city'176 var DISTRICT = 'district'177 var COUNTY = 'county'178 179 CityPicker(element,options) { 180 this.$element = $(element); 181 this.$dropdown = null182 this.options = $.extend({},CityPicker.DEFAULTS,$.isPlainObject(options) && options); 183 this.active = false184 this.dems = []; 185 this.needBlur = 186 this.init(); 187 188 189 CityPicker.prototype =190 constructor: CityPicker,191 192 init: () { 193 194 .defineDems(); 195 196 .render(); 197 198 .bind(); 199 200 true201 },1)">202 界面显示处理 203 render: 204 var p = .getPosition(),1)">205 placeholder = this.$element.attr('placeholder') || .options.placeholder,1)">206 textspan = '<span class="city-picker-span" style="' + 207 this.getWidthStyle(p.width) + 'height:' + 208 p.height + 'px;line-height:' + (p.height - 1) + 'px;">' + 209 (placeholder ? '<span class="placeholder">' + placeholder + '</span>' : '') + 210 '<span class="title"></span><div class="arrow"></div>' + '</span>',1)">211 212 dropdown = '<div class="city-picker-dropdown" style="left:0px;top:100%;' + 213 this.getWidthStyle(p.width,true) + '">' + 214 '<div class="city-select-wrap">' + 215 '<div class="city-select-tab">' + 216 '<a class="active" data-count="province">省份</a>' + 217 (this.includeDem('city') ? '<a data-count="city">城市</a>' : '') + 218 (this.includeDem('district') ? '<a data-count="district">区县</a>' : '') + 219 (this.includeDem('county') ? '<a data-count="county">乡镇</a>' : '') + 220 '</div>' + 221 '<div class="city-select-content">' + 222 '<div class="city-select province" data-count="province"></div>' + 223 (this.includeDem('city') ? '<div class="city-select city" data-count="city"></div>' : '') + 224 (this.includeDem('district') ? '<div class="city-select district" data-count="district"></div>' : '') + 225 (this.includeDem('county') ? '<div class="city-select county" data-count="county"></div>' : '') + 226 '</div></div>'227 228 this.$element.addClass('city-picker-input'229 this.$textspan = $(textspan).insertAfter(.$element); 230 this.$dropdown = $(dropdown).insertAfter(.$textspan); 231 var $select = this.$dropdown.find('.city-select'232 233 setup this.$province,this.$city and/or this.$district object 234 $.each(this.dems,$.proxy( (i,type) { 235 this['$' + type] = $select.filter('.' + type + ''236 },1)">237 238 .refresh(); 239 240 241 refresh: (force) { 242 clean the data-item for each $select 243 244 $select.data('item',1)">245 parse value from value of the target $element 246 var val = this.$element.val() || ''247 val = val.split('/'248 $.each(function (i,type) {遍历dems 249 if (val[i] && i < val.length) { 250 this.options[type] = val[i];把当前显示值赋值给options 251 } if252 this.options[type] = ''253 } 254 this.output(type);输出下拉框显示数据 255 },1)">256 .tab(PROVINCE); 257 this.feedText();界面显示选择的内容 258 this.feedVal();input标签value赋值 259 260 dems赋值 261 defineDems: 262 var stop = 263 $.each([PROVINCE,CITY,DISTRICT,COUNTY],1)">264 if (!stop) { 265 .dems.push(type); 266 267 if (type === .options.level) { 268 stop = 269 270 },1)">271 272 273 includeDem: (type) { 274 return $.inArray(type,1)">this.dems) !== -1275 276 277 getPosition: 278 var p,h,w,s,pw; 279 p = .$element.position(); 280 s = this.getSize(281 h = s.height; 282 w = s.width; 283 .options.responsive) { 284 pw = .$element.offsetParent().width(); 285 (pw) { 286 w = w / pw; 287 if (w > 0.99288 w = 1289 } 290 w = w * 100 + '%'291 292 } 293 294 return295 top: p.top || 0296 left: p.left || 0297 height: h,1)">298 width: w 299 }; 300 301 302 getSize: ($dom) { 303 $wrap,$clone,sizes; 304 if (!$dom.is(':visible')) { 305 $wrap = $("<div />").appendTo($("body"306 $wrap.css({ 307 "position": "absolute !important"308 "visibility": "hidden !important"309 "display": "block !important" 310 }); 311 312 $clone = $dom.clone().appendTo($wrap); 313 314 sizes =315 width: $clone.outerWidth(),1)">316 height: $clone.outerHeight() 317 }; 318 319 $wrap.remove(); 320 } 321 sizes =322 width: $dom.outerWidth(),1)">323 height: $dom.outerHeight() 324 325 326 327 sizes; 328 329 330 getWidthStyle: (w,dropdown) { 331 this.options.responsive && !$.isNumeric(w)) { 332 return 'width:' + w + ';'333 } 334 return 'width:' + (dropdown ? Math.max(320,w) : w) + 'px;'335 336 337 绑定事件 338 bind: 339 var $this = 340 $(document).on('click',(this._mouteclick = (e) { 341 var $target = $(e.target); 342 $dropdown,$span,$input; 343 if ($target.is('.city-picker-span'344 $span = $target; 345 } if ($target.is('.city-picker-span *'346 $span = $target.parents('.city-picker-span'347 348 if ($target.is('.city-picker-input'349 $input =350 351 if ($target.is('.city-picker-dropdown'352 $dropdown =353 } if ($target.is('.city-picker-dropdown *'354 $dropdown = $target.parents('.city-picker-dropdown'355 356 if ((!$input && !$span && !$dropdown) || 357 ($span && $span.get(0) !== $this.$textspan.get(0)) || 358 ($input && $input.get(0) !== $this.$element.get(0)) || 359 ($dropdown && $dropdown.get(0) !== $this.$dropdown.get(0))) { 360 $this.close(361 362 })); 363 this.$element.on('change',1)">this._changeElement = $.proxy(364 365 this.refresh(366 },1)">this))).on('focus',1)">this._focusElement = $.proxy(367 368 .open(); 369 },1)">this))).on('blur',1)">this._blurElement = $.proxy(370 .needBlur) { 371 372 373 374 },1)">))); 375 this.$textspan.on('click',1)">376 $(e.target),type; 377 $378 if ($target.is('.select-item'379 type = $target.data('count'380 $.open(type); 381 } 382 if ($this.$dropdown.is(':visible'383 $.close(); 384 } 385 $386 387 388 }).on('mousedown',1)">389 $390 }); 391 this.$dropdown.on('click','.city-select a',1)">392 var $select = $(this).parents('.city-select'393 var $active = $select.find('a.active'394 var last = $select.next().length === 0395 $active.removeClass('active'396 $(this).addClass('active'397 if ($active.data('code') !== $(this).data('code'398 $select.data('item'399 address: $(this).attr('title'),code: $() 400 }); 401 $().trigger(EVENT_CHANGE); 402 $.feedText(); 403 $this.feedVal(404 (last) { 405 $406 407 408 }).on('click','.city-select-tab a',1)">409 if (!$(this).hasClass('active'410 var type = $(this).data('count'411 $.tab(type); 412 413 }).on('mousedown',1)">414 $415 416 .$province) { 417 this.$province.on(EVENT_CHANGE,1)">this._changeProvince = $.proxy(418 if(this.output(CITY)){判断下一个tab是否有数据,没有则关闭下拉 419 $420 421 }; 422 this.output(CITY); 423 this.output(DISTRICT); 424 this.output(COUNTY); 425 .tab(CITY); 426 },1)">427 428 .$city) { 429 this.$city.on(EVENT_CHANGE,1)">this._changeCity = $.proxy(430 .output(DISTRICT)){ 431 $432 433 }; 434 .output(COUNTY); 435 .tab(DISTRICT); 436 },1)">437 438 439 .$district) { 440 this.$district.on(EVENT_CHANGE,1)">this._changeDistrict = $.proxy(441 .output(COUNTY)){ 442 $443 444 445 .tab(COUNTY); 446 },1)">447 448 449 显示下拉 450 open: 451 type = type || PROVINCE; 452 .$dropdown.show(); 453 this.$textspan.addClass('open').addClass('focus'454 455 456 关闭下拉 457 close: (blur) { 458 .$dropdown.hide(); 459 this.$textspan.removeClass('open'460 461 this.$textspan.removeClass('focus'462 463 464 解绑事件 465 unbind: 466 467 $(document).off('click',1)">._mouteclick); 468 469 this.$element.off('change',1)">._changeElement); 470 this.$element.off('focus',1)">._focusElement); 471 this.$element.off('blur',1)">._blurElement); 472 473 this.$textspan.off('click'474 this.$textspan.off('mousedown'475 476 this.$dropdown.off('click'477 this.$dropdown.off('mousedown'478 479 480 this.$province.off(EVENT_CHANGE,1)">._changeProvince); 481 482 483 484 this.$city.off(EVENT_CHANGE,1)">._changeCity); 485 486 487 488 this.$district.off(EVENT_CHANGE,1)">._changeDistrict); 489 490 491 获取选择项信息 492 getText: 493 var text = ''494 495 .each(496 var item = $(this).data('item'),1)">497 type = $(498 (item) { 499 text += ($(this).hasClass('province') ? '' : '/') + '<span class="select-item" data-count="' + 500 type + '" data-code="' + item.code + '">' + item.address + '</span>'501 502 503 text; 504 505 getPlaceHolder: 506 return .options.placeholder; 507 508 显示placeholder或者选择的区域 509 feedText: 510 var text = .getText(); 511 (text) { 512 this.$textspan.find('>.placeholder').hide(); 513 this.$textspan.find('>.title').html(.getText()).show(); 514 } 515 this.$textspan.find('>.placeholder').text(.getPlaceHolder()).show(); 516 this.$textspan.find('>.title').html(''517 518 519 getCode: (count) { 520 var obj = {},arr =521 this.$textspan.find('.select-item'522 .each(523 var code = $(524 var count = $(525 obj[count] = code; 526 arr.push(code); 527 528 return count ? obj[count] : arr.join('/'529 530 getVal: 531 532 var code=''533 534 .each(535 536 537 text += ($(this).hasClass('province') ? '' : '/') + item.address; 538 code += ($(this).hasClass('province') ? '' : '_') + item.code; 539 540 541 $("#addrValue").val(code); 542 543 544 input的value赋值 545 feedVal: (trigger) { 546 this.$element.val(.getVal()); 547 (trigger) { 548 this.$element.trigger('cp:updated'549 550 551 输出数据 552 output: 553 554 var options = .options; 555 var placeholders = this.placeholders; 556 this['$' + type]; 557 var data = type === PROVINCE ? {} : []; 558 item; 559 districts; 560 561 var matched = 562 value; 563 if (!$select || !$select.length) { 564 565 566 item = $select.data('item'567 value = (item ? item.address : null) || options[type]; 568 code = ( 569 type === PROVINCE ? 86 : 570 type === CITY ? this.$province && this.$province.find('.active').data('code') : 571 type === DISTRICT ? this.$city && this.$city.find('.active').data('code'572 type === COUNTY ? this.$district && this.$district.find('.active').data('code') : code 573 ); 574 districts = $.isNumeric(code) ? ChineseDistricts[code] : null; 575 //判断是否应该去远程加载数据 576 districts = $.isNumeric(code) ? this.remoteLoadData(type,code) : null; 577 ($.isPlainObject(districts)) { 578 $.each(districts,1)"> (code,address) { 579 provs; 580 if (type === PROVINCE) { 581 provs =582 for (var i = 0; i < address.length; i++583 if (address[i].address === value) { 584 matched =585 code: address[i].code,1)">586 address: address[i].address 587 }; 588 } 589 provs.push({ 590 code: address[i].code,1)">591 address: address[i].address,1)">592 selected: address[i].address === value 593 }); 594 } 595 data[code] =596 } 597 if (address ===598 matched =599 code: code,1)">600 address: address 601 }; 602 603 data.push({ 604 code: code,1)">605 address: address,1)">606 selected: address ===607 }); 608 609 610 611 612 $select.html(type === PROVINCE ? .getProvinceList(data) : 613 .getList(data,type)); 614 $select.data('item',matched);当前tab添加item(包含选择对象的内容) 615 if(! (type === PROVINCE)){标识:下一个选项没有数据则关闭 616 if(data.length==0){ 617 618 619 620 621 遍历省份 622 getProvinceList: (data) { 623 var list = [],1)">624 $625 simple = .options.simple; 626 627 $.each(data,n) { 628 list.push('<dl class="clearfix">'629 list.push('<dt>' + i + '</dt><dd>'630 $.each(n,1)"> (j,m) { 631 list.push( 632 '<a' + 633 ' title="' + (m.address || '') + '"' + 634 ' data-code="' + (m.code || '') + '"' + 635 ' class="' + 636 (m.selected ? ' active' : '') + 637 '">' + 638 ( simple ? $this.simplize(m.address,PROVINCE) : m.address) + 639 '</a>'640 641 list.push('</dd></dl>'642 643 644 return list.join(''645 646 遍历市或区或县 647 getList: (data,1)">648 649 $650 simple = 651 list.push('<dl class="clearfix"><dd>'652 653 $.each(data,1)">654 list.push( 655 '<a' + 656 ' title="' + (n.address || '') + '"' + 657 ' data-code="' + (n.code || '') + '"' + 658 ' class="' + 659 (n.selected ? ' active' : '') + 660 '">' + 661 ( simple ? $this.simplize(n.address,type) : n.address) + 662 '</a>'663 664 list.push('</dd></dl>'665 666 667 668 简化名字 669 simplize: (address,1)">670 address = address || ''671 672 return address.replace(/[省,市,自治区,壮族,回族,维吾尔]/g,''673 } CITY) { 674 return address.replace(/[市,地区,蒙古,苗族,白族,傣族,景颇族,藏族,彝族,傈僳族,布依族,侗族]/g,1)">675 .replace('哈萨克','').replace('自治州','').replace(/自治县/,1)">676 } DISTRICT) { 677 return address.length > 2 ? address.replace(/[市,区,县,旗]/g,1)">) : address; 678 679 680 处理tab显示 681 tab: 682 var $selects = 683 var $tabs = this.$dropdown.find('.city-select-tab > a'684 685 var $tab = this.$dropdown.find('.city-select-tab > a[data-count="' + type + '"]'686 ($select) { 687 $selects.hide(); 688 $select.show(); 689 $tabs.removeClass('active'690 $tab.addClass('active'691 692 693 694 reset: 695 null).trigger('change'696 697 698 destroy: 699 .unbind(); 700 this.$element.removeData(NAMESPACE).removeClass('city-picker-input'701 .$textspan.remove(); 702 .$dropdown.remove(); 703 704 远程加载数据 705 remoteLoadData: function (cityType,cityId) { 706 var resultData = {}; 707 if(PROVINCE===cityType) 708 { 709 return ChineseDistricts[cityId]; 710 } 711 $.ajax({ 712 url: "/directoryProcurement/jd/cityData/"+cityType+"/"+cityId,713 type: "GET",714 dataType: "json",715 async: false,716 contentType: "application/json",717 success: function (result) { 718 if(result.code=="0") 719 { 720 resultData = result.data; 721 }else 722 { 723 console.log(result.desc); 724 } 725 } 726 }); 727 return resultData; 728 } 729 }; 730 731 CityPicker.DEFAULTS =732 simple: 733 responsive: 734 placeholder: '请选择省/市/区/镇'735 level: 'county'736 province: ''737 city: ''738 district: ''739 county:'' 740 741 742 CityPicker.setDefaults = (options) { 743 $.extend(CityPicker.DEFAULTS,options); 744 745 746 Save the other citypicker 747 CityPicker.other = $.fn.citypicker; 748 749 Register as jQuery plugin 750 $.fn.citypicker = (option) { 751 var args = [].slice.call(arguments,1752 753 this.each(754 this = $(755 var data = $.data(NAMESPACE); 756 options; 757 fn; 758 759 data) { 760 if (/destroy/.test(option)) { 761 762 763 764 options = $.extend({},$this.data(),$.isPlainObject(option) && option); 765 $this.data(NAMESPACE,(data = new CityPicker(766 767 768 typeof option === 'string' && $.isFunction(fn = data[option])) { 769 fn.apply(data,args); 770 771 }); 772 773 774 $.fn.citypicker.Constructor = CityPicker; 775 $.fn.citypicker.setDefaults = CityPicker.setDefaults; 776 777 No conflict 778 $.fn.citypicker.noConflict = 779 $.fn.citypicker = CityPicker.other; 780 781 782 783 $(784 $('[data-toggle="city-picker"]').citypicker(); 785 }); 786 }); 上面的代码的第一个红色字体处,是为将省和直辖市这种一级地址默认加载过来,其他的三级地址都是靠远程加载过来, 第二处红色字体是为了解决,上面是为了解决一个bug,就当选择了,一级二级三级和四级地址后,如果再重新选择一级地址,后面的三级地址不会清空重选,这样会造成地址归属地不对的问题,这个bug在city-picker原生的代码中是没有的,应该是上面那个博客里,改成四级地址后才出现的bug。 第三处红色字体是为了判断是否有应该去进行远程加载数据,如果只选择了一级地址,就不去远程加载数据,反之则调用远程加载数据方法。 第四处红色字体,是关键了,实现远程加载数据的方法,这个方法是我自己后来加的,写成了同步请求。因为有些地址就没有三级地址,例如北京、丰台、三环到四环之间、这就是一个完整的选择地址了,所以会把一些没有四级地址的信息打印出来,如果有需要的可以修改这个远程加载数据的方法,来实现自己的需求。 最后忘记说了,我是将city-picker.js这个插件的city-picker.data.js和city-picker.js这两个文件合并成了一个文件,因为我只需要默认加载一级地址,一级地址的内容也不多,就写在了一起了,就是第一处红色字体处的js全局对象。 粗略的改造,接受批评。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |