最近公司做的項目要和京東的數據做對接,所以要做個類似京東商品的詳情頁。頁面的數據,是可以從京東介面獲取到的,但是地址插件選擇的效果需要自己實現。前端的同事在之前的項目中,已經選擇了一款地址插件(city-picker.js),但是這款插件最多只支持三級地址,而且最主要的是這插件的地址數據來源,是寫死 ...
最近公司做的項目要和京東的數據做對接,所以要做個類似京東商品的詳情頁。頁面的數據,是可以從京東介面獲取到的,但是地址插件選擇的效果需要自己實現。前端的同事在之前的項目中,已經選擇了一款地址插件(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 if (typeof exports === 'object') { 160 // Node / CommonJS 161 factory(require('jquery'), require('ChineseDistricts')); 162 } else { 163 // Browser globals. 164 factory(jQuery, ChineseDistricts); 165 } 166 })(function ($, ChineseDistricts) { 167 168 'use strict'; 169 if (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 function CityPicker(element, options) { 180 this.$element = $(element); 181 this.$dropdown = null; 182 this.options = $.extend({}, CityPicker.DEFAULTS, $.isPlainObject(options) && options); 183 this.active = false; 184 this.dems = []; 185 this.needBlur = false; 186 this.init(); 187 } 188 189 CityPicker.prototype = { 190 constructor: CityPicker, 191 192 init: function () { 193 194 this.defineDems(); 195 196 this.render(); 197 198 this.bind(); 199 200 this.active = true; 201 }, 202 //界面顯示處理 203 render: function () { 204 var p = this.getPosition(), 205 placeholder = this.$element.attr('placeholder') || this.options.placeholder, 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>', 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(this.$element); 230 this.$dropdown = $(dropdown).insertAfter(this.$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(function (i, type) { 235 this['$' + type] = $select.filter('.' + type + ''); 236 }, this)); 237 238 this.refresh(); 239 }, 240 241 refresh: function (force) { 242 // clean the data-item for each $select 243 var $select = this.$dropdown.find('.city-select'); 244 $select.data('item', null); 245 // parse value from value of the target $element 246 var val = this.$element.val() || ''; 247 val = val.split('/'); 248 $.each(this.dems, $.proxy(function (i, type) {//遍歷dems 249 if (val[i] && i < val.length) { 250 this.options[type] = val[i];//把當前顯示值賦值給options 251 } else if (force) { 252 this.options[type] = ''; 253 } 254 this.output(type);//輸出下拉框顯示數據 255 }, this)); 256 this.tab(PROVINCE); 257 this.feedText();//界面顯示選擇的內容 258 this.feedVal();//input標簽value賦值 259 }, 260 //dems賦值 261 defineDems: function () { 262 var stop = false; 263 $.each([PROVINCE, CITY, DISTRICT,COUNTY], $.proxy(function (i, type) { 264 if (!stop) { 265 this.dems.push(type); 266 } 267 if (type === this.options.level) { 268 stop = true; 269 } 270 }, this)); 271 }, 272 273 includeDem: function (type) { 274 return $.inArray(type, this.dems) !== -1; 275 }, 276 277 getPosition: function () { 278 var p, h, w, s, pw; 279 p = this.$element.position(); 280 s = this.getSize(this.$element); 281 h = s.height; 282 w = s.width; 283 if (this.options.responsive) { 284 pw = this.$element.offsetParent().width(); 285 if (pw) { 286 w = w / pw; 287 if (w > 0.99) { 288 w = 1; 289 } 290 w = w * 100 + '%'; 291 } 292 } 293 294 return { 295 top: p.top || 0, 296 left: p.left || 0, 297 height: h, 298 width: w 299 }; 300 }, 301 302 getSize: function ($dom) { 303 var $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(), 316 height: $clone.outerHeight() 317 }; 318 319 $wrap.remove(); 320 } else { 321 sizes = { 322 width: $dom.outerWidth(), 323 height: $dom.outerHeight() 324 }; 325 } 326 327 return sizes; 328 }, 329 330 getWidthStyle: function (w, dropdown) { 331 if (this.options.responsive && !$.isNumeric(w)) { 332 return 'width:' + w + ';'; 333 } else { 334 return 'width:' + (dropdown ? Math.max(320, w) : w) + 'px;'; 335 } 336 }, 337 //綁定事件 338 bind: function () { 339 var $this = this; 340 $(document).on('click', (this._mouteclick = function (e) { 341 var $target = $(e.target); 342 var $dropdown, $span, $input; 343 if ($target.is('.city-picker-span')) { 344 $span = $target; 345 } else if ($target.is('.city-picker-span *')) { 346 $span = $target.parents('.city-picker-span'); 347 } 348 if ($target.is('.city-picker-input')) { 349 $input = $target; 350 } 351 if ($target.is('.city-picker-dropdown')) { 352 $dropdown = $target; 353 } else 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(true); 361 } 362 })); 363 this.$element.on('change', (this._changeElement = $.proxy(function () { 364 this.close(true); 365 this.refresh(true); 366 }, this))).on('focus', (this._focusElement = $.proxy(function () { 367 this.needBlur = true; 368 this.open(); 369 }, this))).on('blur', (this._blurElement = $.proxy(function () { 370 if (this.needBlur) { 371 this.needBlur = false; 372 this.close(true); 373 } 374 }, this))); 375 this.$textspan.on('click', function (e) { 376 var $target = $(e.target), type; 377 $this.needBlur = false; 378 if ($target.is('.select-item')) { 379 type = $target.data('count'); 380 $this.open(type); 381 } else { 382 if ($this.$dropdown.is(':visible')) { 383 $this.close(); 384 } else { 385 $this.open(); 386 } 387 } 388 }).on('mousedown', function () { 389 $this.needBlur = false; 390 }); 391 this.$dropdown.on('click', '.city-select a', function () { 392 var $select = $(this).parents('.city-select'); 393 var $active = $select.find('a.active'); 394 var last = $select.next().length === 0; 395 $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: $(this).data('code') 400 }); 401 $(this).trigger(EVENT_CHANGE); 402 $this.feedText(); 403 $this.feedVal(true); 404 if (last) { 405 $this.close(); 406 } 407 } 408 }).on('click', '.city-select-tab a', function () { 409 if (!$(this).hasClass('active')) { 410 var type = $(this).data('count'); 411 $this.tab(type); 412 } 413 }).on('mousedown', function () { 414 $this.needBlur = false; 415 }); 416 if (this.$province) { 417 this.$province.on(EVENT_CHANGE, (this._changeProvince = $.proxy(function () { 418 if(this.output(CITY)){//判斷下一個tab是否有數據,沒有則關閉下拉 419 $this.close(); 420 return; 421 }; 422 this.output(CITY); 423 this.output(DISTRICT); 424 this.output(COUNTY); 425 this.tab(CITY); 426 }, this))); 427 } 428 if (this.$city) { 429 this.$city.on(EVENT_CHANGE, (this._changeCity = $.proxy(function () { 430 if(this.output(DISTRICT)){ 431 $this.close(); 432 return; 433 }; 434 this.output(COUNTY); 435 this.tab(DISTRICT); 436 }, this))); 437 } 438 439 if (this.$district) { 440 this.$district.on(EVENT_CHANGE, (this._changeDistrict = $.proxy(function () { 441 if(this.output(COUNTY)){ 442 $this.close(); 443 return; 444 }; 445 this.tab(COUNTY); 446 }, this))); 447 } 448 }, 449 //顯示下拉 450 open: function (type) { 451 type = type || PROVINCE; 452 this.$dropdown.show(); 453 this.$textspan.addClass('open').addClass('focus'); 454 this.tab(type); 455 }, 456 //關閉下拉 457 close: function (blur) { 458 this.$dropdown.hide(); 459 this.$textspan.removeClass('open'); 460 if (blur) { 461 this.$textspan.removeClass('focus'); 462 } 463 }, 464 //解綁事件 465 unbind: function () { 466 467 $(document).off('click', this._mouteclick); 468 469 this.$element.off('change', this._changeElement); 470 this.$element.off('focus', this._focusElement); 471 this.$element.off('blur', this._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 if (this.$province) { 480 this.$province.off(EVENT_CHANGE, this._changeProvince); 481 } 482 483 if (this.$city) { 484 this.$city.off(EVENT_CHANGE, this._changeCity); 485 } 486 487 if (this.$district) { 488 this.$district.off(EVENT_CHANGE, this._changeDistrict); 489 } 490 }, 491 //獲取選擇項信息 492 getText: function () { 493 var text = ''; 494 this.$dropdown.find('.city-select') 495 .each(function () { 496 var item = $(this).data('item'), 497 type = $(this).data('count'); 498 if (item) { 499 text += ($(this).hasClass('province') ? '' : '/') + '<span class="select-item" data-count="' + 500 type + '" data-code="' + item.code + '">' + item.address + '</span>'; 501 } 502 }); 503 return text; 504 }, 505 getPlaceHolder: function () { 506 return this.$element.attr('placeholder') || this.options.placeholder; 507 }, 508 //顯示placeholder或者選擇的區域 509 feedText: function () { 510 var text = this.getText(); 511 if (text) { 512 this.$textspan.find('>.placeholder').hide(); 513 this.$textspan.find('>.title').html(this.getText()).show(); 514 } else { 515 this.$textspan.find('>.placeholder').text(this.getPlaceHolder()).show(); 516 this.$textspan.find('>.title').html('').hide(); 517 } 518 }, 519 getCode: function (count) { 520 var obj = {}, arr = []; 521 this.$textspan.find('.select-item') 522 .each(function () { 523 var code = $(this).data('code'); 524 var count = $(this).data('count'); 525 obj[count] = code; 526 arr.push(code); 527 }); 528 return count ? obj[count] : arr.join('/'); 529 }, 530 getVal: function () { 531 var text = ''; 532 var code=''; 533 this.$dropdown.find('.city-select') 534 .each(function () { 535 var item = $(this).data('item'); 536 if (item) { 537 text += ($(this).hasClass('province') ? '' : '/') + item.address; 538 code += ($(this).hasClass('province') ? '' : '_') + item.code; 539 } 540 }); 541 $("#addrValue").val(code); 542 return text; 543 }, 544 //input的value賦值 545 feedVal: function (trigger) { 546 this.$element.val(this.getVal()); 547 if(trigger) { 548 this.$element.trigger('cp:updated'); 549 } 550 }, 551 //輸出數據 552 output: function (type) { 553 var $this = this; 554 var options = this.options; 555 //var placeholders = this.placeholders; 556 var $select = this['$' + type]; 557 var data = type === PROVINCE ? {} : []; 558 var item; 559 var districts; 560 var code; 561 var matched = null; 562 var value; 563 if (!$select || !$select.length) { 564 return; 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('c