├── Demo.html
├── css
├── bootstrap.css
├── city-picker.css
└── main.css
├── fonts
├── glyphicons-halflings-regular.eot
├── glyphicons-halflings-regular.svg
├── glyphicons-halflings-regular.ttf
├── glyphicons-halflings-regular.woff
└── glyphicons-halflings-regular.woff2
├── images
└── drop-arrow.png
└── js
├── bootstrap.js
├── city-picker.data.js
├── city-picker.js
├── jquery.js
└── main.js
/Demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ' +
71 | '
' +
72 | '
' +
73 | '
省份' +
74 | (this.includeDem('city') ? '
城市' : '') +
75 | (this.includeDem('district') ? '
区县' : '') + '
' +
76 | '
' +
77 | '
' +
78 | (this.includeDem('city') ? '
' : '') +
79 | (this.includeDem('district') ? '
' : '') +
80 | '
';
81 |
82 | this.$element.addClass('city-picker-input');
83 | this.$textspan = $(textspan).insertAfter(this.$element);
84 | this.$dropdown = $(dropdown).insertAfter(this.$textspan);
85 | var $select = this.$dropdown.find('.city-select');
86 |
87 | // setup this.$province, this.$city and/or this.$district object
88 | $.each(this.dems, $.proxy(function (i, type) {
89 | this['$' + type] = $select.filter('.' + type + '');
90 | }, this));
91 |
92 | this.refresh();
93 | },
94 |
95 | refresh: function (force) {
96 | // clean the data-item for each $select
97 | var $select = this.$dropdown.find('.city-select');
98 | $select.data('item', null);
99 | // parse value from value of the target $element
100 | var val = this.$element.val() || '';
101 | val = val.split('/');
102 | $.each(this.dems, $.proxy(function (i, type) {
103 | if (val[i] && i < val.length) {
104 | this.options[type] = val[i];
105 | } else if (force) {
106 | this.options[type] = '';
107 | }
108 | this.output(type);
109 | }, this));
110 | this.tab(PROVINCE);
111 | this.feedText();
112 | this.feedVal();
113 | },
114 |
115 | defineDems: function () {
116 | var stop = false;
117 | $.each([PROVINCE, CITY, DISTRICT], $.proxy(function (i, type) {
118 | if (!stop) {
119 | this.dems.push(type);
120 | }
121 | if (type === this.options.level) {
122 | stop = true;
123 | }
124 | }, this));
125 | },
126 |
127 | includeDem: function (type) {
128 | return $.inArray(type, this.dems) !== -1;
129 | },
130 |
131 | getPosition: function () {
132 | var p, h, w, s, pw;
133 | p = this.$element.position();
134 | s = this.getSize(this.$element);
135 | h = s.height;
136 | w = s.width;
137 | if (this.options.responsive) {
138 | pw = this.$element.offsetParent().width();
139 | if (pw) {
140 | w = w / pw;
141 | if (w > 0.99) {
142 | w = 1;
143 | }
144 | w = w * 100 + '%';
145 | }
146 | }
147 |
148 | return {
149 | top: p.top || 0,
150 | left: p.left || 0,
151 | height: h,
152 | width: w
153 | };
154 | },
155 |
156 | getSize: function ($dom) {
157 | var $wrap, $clone, sizes;
158 | if (!$dom.is(':visible')) {
159 | $wrap = $("
").appendTo($("body"));
160 | $wrap.css({
161 | "position": "absolute !important",
162 | "visibility": "hidden !important",
163 | "display": "block !important"
164 | });
165 |
166 | $clone = $dom.clone().appendTo($wrap);
167 |
168 | sizes = {
169 | width: $clone.outerWidth(),
170 | height: $clone.outerHeight()
171 | };
172 |
173 | $wrap.remove();
174 | } else {
175 | sizes = {
176 | width: $dom.outerWidth(),
177 | height: $dom.outerHeight()
178 | };
179 | }
180 |
181 | return sizes;
182 | },
183 |
184 | getWidthStyle: function (w, dropdown) {
185 | if (this.options.responsive && !$.isNumeric(w)) {
186 | return 'width:' + w + ';';
187 | } else {
188 | return 'width:' + (dropdown ? Math.max(320, w) : w) + 'px;';
189 | }
190 | },
191 |
192 | bind: function () {
193 | var $this = this;
194 |
195 | $(document).on('click', (this._mouteclick = function (e) {
196 | var $target = $(e.target);
197 | var $dropdown, $span, $input;
198 | if ($target.is('.city-picker-span')) {
199 | $span = $target;
200 | } else if ($target.is('.city-picker-span *')) {
201 | $span = $target.parents('.city-picker-span');
202 | }
203 | if ($target.is('.city-picker-input')) {
204 | $input = $target;
205 | }
206 | if ($target.is('.city-picker-dropdown')) {
207 | $dropdown = $target;
208 | } else if ($target.is('.city-picker-dropdown *')) {
209 | $dropdown = $target.parents('.city-picker-dropdown');
210 | }
211 | if ((!$input && !$span && !$dropdown) ||
212 | ($span && $span.get(0) !== $this.$textspan.get(0)) ||
213 | ($input && $input.get(0) !== $this.$element.get(0)) ||
214 | ($dropdown && $dropdown.get(0) !== $this.$dropdown.get(0))) {
215 | $this.close(true);
216 | }
217 |
218 | }));
219 |
220 | this.$element.on('change', (this._changeElement = $.proxy(function () {
221 | this.close(true);
222 | this.refresh(true);
223 | }, this))).on('focus', (this._focusElement = $.proxy(function () {
224 | this.needBlur = true;
225 | this.open();
226 | }, this))).on('blur', (this._blurElement = $.proxy(function () {
227 | if (this.needBlur) {
228 | this.needBlur = false;
229 | this.close(true);
230 | }
231 | }, this)));
232 |
233 | this.$textspan.on('click', function (e) {
234 | var $target = $(e.target), type;
235 | $this.needBlur = false;
236 | if ($target.is('.select-item')) {
237 | type = $target.data('count');
238 | $this.open(type);
239 | } else {
240 | if ($this.$dropdown.is(':visible')) {
241 | $this.close();
242 | } else {
243 | $this.open();
244 | }
245 | }
246 | }).on('mousedown', function () {
247 | $this.needBlur = false;
248 | });
249 |
250 | this.$dropdown.on('click', '.city-select a', function () {
251 | var $select = $(this).parents('.city-select');
252 | var $active = $select.find('a.active');
253 | var last = $select.next().length === 0;
254 | $active.removeClass('active');
255 | $(this).addClass('active');
256 | if ($active.data('code') !== $(this).data('code')) {
257 | $select.data('item', {
258 | address: $(this).attr('title'), code: $(this).data('code')
259 | });
260 | $(this).trigger(EVENT_CHANGE);
261 | $this.feedText();
262 | $this.feedVal();
263 | if (last) {
264 | $this.close();
265 | }
266 | }
267 | }).on('click', '.city-select-tab a', function () {
268 | if (!$(this).hasClass('active')) {
269 | var type = $(this).data('count');
270 | $this.tab(type);
271 | }
272 | }).on('mousedown', function () {
273 | $this.needBlur = false;
274 | });
275 |
276 | if (this.$province) {
277 | this.$province.on(EVENT_CHANGE, (this._changeProvince = $.proxy(function () {
278 | this.output(CITY);
279 | this.output(DISTRICT);
280 | this.tab(CITY);
281 | }, this)));
282 | }
283 |
284 | if (this.$city) {
285 | this.$city.on(EVENT_CHANGE, (this._changeCity = $.proxy(function () {
286 | this.output(DISTRICT);
287 | this.tab(DISTRICT);
288 | }, this)));
289 | }
290 | },
291 |
292 | open: function (type) {
293 | type = type || PROVINCE;
294 | this.$dropdown.show();
295 | this.$textspan.addClass('open').addClass('focus');
296 | this.tab(type);
297 | },
298 |
299 | close: function (blur) {
300 | this.$dropdown.hide();
301 | this.$textspan.removeClass('open');
302 | if (blur) {
303 | this.$textspan.removeClass('focus');
304 | }
305 | },
306 |
307 | unbind: function () {
308 |
309 | $(document).off('click', this._mouteclick);
310 |
311 | this.$element.off('change', this._changeElement);
312 | this.$element.off('focus', this._focusElement);
313 | this.$element.off('blur', this._blurElement);
314 |
315 | this.$textspan.off('click');
316 | this.$textspan.off('mousedown');
317 |
318 | this.$dropdown.off('click');
319 | this.$dropdown.off('mousedown');
320 |
321 | if (this.$province) {
322 | this.$province.off(EVENT_CHANGE, this._changeProvince);
323 | }
324 |
325 | if (this.$city) {
326 | this.$city.off(EVENT_CHANGE, this._changeCity);
327 | }
328 | },
329 |
330 | getText: function () {
331 | var text = '';
332 | this.$dropdown.find('.city-select')
333 | .each(function () {
334 | var item = $(this).data('item'),
335 | type = $(this).data('count');
336 | if (item) {
337 | text += ($(this).hasClass('province') ? '' : '/') + '
' + item.address + '';
339 | }
340 | });
341 | return text;
342 | },
343 |
344 | getPlaceHolder: function () {
345 | return this.$element.attr('placeholder') || this.options.placeholder;
346 | },
347 |
348 | feedText: function () {
349 | var text = this.getText();
350 | if (text) {
351 | this.$textspan.find('>.placeholder').hide();
352 | this.$textspan.find('>.title').html(this.getText()).show();
353 | } else {
354 | this.$textspan.find('>.placeholder').text(this.getPlaceHolder()).show();
355 | this.$textspan.find('>.title').html('').hide();
356 | }
357 | },
358 |
359 | getVal: function () {
360 | var text = '';
361 | this.$dropdown.find('.city-select')
362 | .each(function () {
363 | var item = $(this).data('item');
364 | if (item) {
365 | text += ($(this).hasClass('province') ? '' : '/') + item.address;
366 | }
367 | });
368 | return text;
369 | },
370 |
371 | feedVal: function () {
372 | this.$element.val(this.getVal());
373 | },
374 |
375 | output: function (type) {
376 | var options = this.options;
377 | //var placeholders = this.placeholders;
378 | var $select = this['$' + type];
379 | var data = type === PROVINCE ? {} : [];
380 | var item;
381 | var districts;
382 | var code;
383 | var matched = null;
384 | var value;
385 |
386 | if (!$select || !$select.length) {
387 | return;
388 | }
389 |
390 | item = $select.data('item');
391 |
392 | value = (item ? item.address : null) || options[type];
393 |
394 | code = (
395 | type === PROVINCE ? 86 :
396 | type === CITY ? this.$province && this.$province.find('.active').data('code') :
397 | type === DISTRICT ? this.$city && this.$city.find('.active').data('code') : code
398 | );
399 |
400 | districts = $.isNumeric(code) ? ChineseDistricts[code] : null;
401 |
402 | if ($.isPlainObject(districts)) {
403 | $.each(districts, function (code, address) {
404 | var provs;
405 | if (type === PROVINCE) {
406 | provs = [];
407 | for (var i = 0; i < address.length; i++) {
408 | if (address[i].address === value) {
409 | matched = {
410 | code: address[i].code,
411 | address: address[i].address
412 | };
413 | }
414 | provs.push({
415 | code: address[i].code,
416 | address: address[i].address,
417 | selected: address[i].address === value
418 | });
419 | }
420 | data[code] = provs;
421 | } else {
422 | if (address === value) {
423 | matched = {
424 | code: code,
425 | address: address
426 | };
427 | }
428 | data.push({
429 | code: code,
430 | address: address,
431 | selected: address === value
432 | });
433 | }
434 | });
435 | }
436 |
437 | $select.html(type === PROVINCE ? this.getProvinceList(data) :
438 | this.getList(data, type));
439 | $select.data('item', matched);
440 | },
441 |
442 | getProvinceList: function (data) {
443 | var list = [],
444 | $this = this,
445 | simple = this.options.simple;
446 |
447 | $.each(data, function (i, n) {
448 | list.push('
');
449 | list.push('- ' + i + '
- ');
450 | $.each(n, function (j, m) {
451 | list.push(
452 | '' +
458 | ( simple ? $this.simplize(m.address, PROVINCE) : m.address) +
459 | '');
460 | });
461 | list.push('
');
462 | });
463 |
464 | return list.join('');
465 | },
466 |
467 | getList: function (data, type) {
468 | var list = [],
469 | $this = this,
470 | simple = this.options.simple;
471 | list.push('
- ');
472 |
473 | $.each(data, function (i, n) {
474 | list.push(
475 | '' +
481 | ( simple ? $this.simplize(n.address, type) : n.address) +
482 | '');
483 | });
484 | list.push('
');
485 |
486 | return list.join('');
487 | },
488 |
489 | simplize: function (address, type) {
490 | address = address || '';
491 | if (type === PROVINCE) {
492 | return address.replace(/[省,市,自治区,壮族,回族,维吾尔]/g, '');
493 | } else if (type === CITY) {
494 | return address.replace(/[市,地区,回族,蒙古,苗族,白族,傣族,景颇族,藏族,彝族,壮族,傈僳族,布依族,侗族]/g, '')
495 | .replace('哈萨克', '').replace('自治州', '').replace(/自治县/, '');
496 | } else if (type === DISTRICT) {
497 | return address.length > 2 ? address.replace(/[市,区,县,旗]/g, '') : address;
498 | }
499 | },
500 |
501 | tab: function (type) {
502 | var $selects = this.$dropdown.find('.city-select');
503 | var $tabs = this.$dropdown.find('.city-select-tab > a');
504 | var $select = this['$' + type];
505 | var $tab = this.$dropdown.find('.city-select-tab > a[data-count="' + type + '"]');
506 | if ($select) {
507 | $selects.hide();
508 | $select.show();
509 | $tabs.removeClass('active');
510 | $tab.addClass('active');
511 | }
512 | },
513 |
514 | reset: function () {
515 | this.$element.val(null).trigger('change');
516 | },
517 |
518 | destroy: function () {
519 | this.unbind();
520 | this.$element.removeData(NAMESPACE).removeClass('city-picker-input');
521 | this.$textspan.remove();
522 | this.$dropdown.remove();
523 | }
524 | };
525 |
526 | CityPicker.DEFAULTS = {
527 | simple: false,
528 | responsive: false,
529 | placeholder: '请选择省/市/区',
530 | level: 'district',
531 | province: '',
532 | city: '',
533 | district: ''
534 | };
535 |
536 | CityPicker.setDefaults = function (options) {
537 | $.extend(CityPicker.DEFAULTS, options);
538 | };
539 |
540 | // Save the other citypicker
541 | CityPicker.other = $.fn.citypicker;
542 |
543 | // Register as jQuery plugin
544 | $.fn.citypicker = function (option) {
545 | var args = [].slice.call(arguments, 1);
546 |
547 | return this.each(function () {
548 | var $this = $(this);
549 | var data = $this.data(NAMESPACE);
550 | var options;
551 | var fn;
552 |
553 | if (!data) {
554 | if (/destroy/.test(option)) {
555 | return;
556 | }
557 |
558 | options = $.extend({}, $this.data(), $.isPlainObject(option) && option);
559 | $this.data(NAMESPACE, (data = new CityPicker(this, options)));
560 | }
561 |
562 | if (typeof option === 'string' && $.isFunction(fn = data[option])) {
563 | fn.apply(data, args);
564 | }
565 | });
566 | };
567 |
568 | $.fn.citypicker.Constructor = CityPicker;
569 | $.fn.citypicker.setDefaults = CityPicker.setDefaults;
570 |
571 | // No conflict
572 | $.fn.citypicker.noConflict = function () {
573 | $.fn.citypicker = CityPicker.other;
574 | return this;
575 | };
576 |
577 | $(function () {
578 | $('[data-toggle="city-picker"]').citypicker();
579 | });
580 | });
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | $(function () {
2 |
3 | 'use strict';
4 |
5 |
6 | var $citypicker1 = $('#city-picker1');
7 |
8 | $citypicker1.citypicker();
9 |
10 | var $citypicker2 = $('#city-picker2');
11 |
12 | $citypicker2.citypicker({
13 | province: '江苏省',
14 | city: '常州市',
15 | district: '溧阳市'
16 | });
17 |
18 | var $citypicker3 = $('#city-picker3');
19 |
20 | $('#reset').click(function () {
21 | $citypicker3.citypicker('reset');
22 | });
23 |
24 | $('#destroy').click(function () {
25 | $citypicker3.citypicker('destroy');
26 | });
27 | //
28 | //$('#distpicker1').distpicker();
29 | //
30 | //$('#distpicker2').distpicker({
31 | // province: '---- 所在省 ----',
32 | // city: '---- 所在市 ----',
33 | // district: '---- 所在区 ----'
34 | //});
35 | //
36 | //$('#distpicker3').distpicker({
37 | // province: '浙江省',
38 | // city: '杭州市',
39 | // district: '西湖区'
40 | //});
41 | //
42 | //$('#distpicker4').distpicker({
43 | // placeholder: false
44 | //});
45 | //
46 | //$('#distpicker5').distpicker({
47 | // autoSelect: false
48 | //});
49 |
50 | });
51 |
--------------------------------------------------------------------------------