city-picker.min.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  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. (function (factory) {
  11. if (typeof define === 'function' && define.amd) {
  12. // AMD. Register as anonymous module.
  13. define(['jquery', 'ChineseDistricts'], factory);
  14. } else if (typeof exports === 'object') {
  15. // Node / CommonJS
  16. factory(require('jquery'), require('ChineseDistricts'));
  17. } else {
  18. // Browser globals.
  19. factory(jQuery, ChineseDistricts);
  20. }
  21. })(function ($, ChineseDistricts) {
  22. 'use strict';
  23. if (typeof ChineseDistricts === 'undefined') {
  24. throw new Error('The file "city-picker.data.js" must be included first!');
  25. }
  26. var NAMESPACE = 'citypicker';
  27. var EVENT_CHANGE = 'change.' + NAMESPACE;
  28. var PROVINCE = 'province';
  29. var CITY = 'city';
  30. var DISTRICT = 'district';
  31. var COUNTY = 'county';
  32. function CityPicker(element, options) {
  33. this.$element = $(element);
  34. this.$dropdown = null;
  35. this.options = $.extend({}, CityPicker.DEFAULTS, $.isPlainObject(options) && options);
  36. this.active = false;
  37. this.dems = [];
  38. this.needBlur = false;
  39. this.init();
  40. }
  41. CityPicker.prototype = {
  42. constructor: CityPicker,
  43. init: function () {
  44. this.defineDems();
  45. this.render();
  46. this.bind();
  47. this.active = true;
  48. },
  49. //界面显示处理
  50. render: function () {
  51. var p = this.getPosition(),
  52. placeholder = this.$element.attr('placeholder') || this.options.placeholder,
  53. textspan = '<span class="city-picker-span" style="' +
  54. this.getWidthStyle(p.width) + 'height:' +
  55. p.height + 'px;line-height:' + (p.height - 1) + 'px;">' +
  56. (placeholder ? '<span class="placeholder">' + placeholder + '</span>' : '') +
  57. '<span class="title"></span><div class="arrow"></div>' + '</span>',
  58. dropdown = '<div class="city-picker-dropdown" style="left:0px;top:100%;' +
  59. this.getWidthStyle(p.width, true) + '">' +
  60. '<div class="city-select-wrap">' +
  61. '<div class="city-select-tab">' +
  62. '<a class="active" data-count="province">省份</a>' +
  63. (this.includeDem('city') ? '<a data-count="city">城市</a>' : '') +
  64. (this.includeDem('district') ? '<a data-count="district">区名</a>' : '') +
  65. (this.includeDem('county') ? '<a data-count="county">县名</a>' : '') +
  66. '</div>' +
  67. '<div class="city-select-content">' +
  68. '<div class="city-select province" data-count="province"></div>' +
  69. (this.includeDem('city') ? '<div class="city-select city" data-count="city"></div>' : '') +
  70. (this.includeDem('district') ? '<div class="city-select district" data-count="district"></div>' : '') +
  71. (this.includeDem('county') ? '<div class="city-select county" data-count="county"></div>' : '') +
  72. '</div></div>';
  73. this.$element.addClass('city-picker-input');
  74. this.$textspan = $(textspan).insertAfter(this.$element);
  75. this.$dropdown = $(dropdown).insertAfter(this.$textspan);
  76. var $select = this.$dropdown.find('.city-select');
  77. // setup this.$province, this.$city and/or this.$district object
  78. $.each(this.dems, $.proxy(function (i, type) {
  79. this['$' + type] = $select.filter('.' + type + '');
  80. }, this));
  81. this.refresh();
  82. },
  83. refresh: function (force) {
  84. // clean the data-item for each $select
  85. var $select = this.$dropdown.find('.city-select');
  86. $select.data('item', null);
  87. // parse value from value of the target $element
  88. var val = this.$element.val() || '';
  89. val = val.split('/');
  90. $.each(this.dems, $.proxy(function (i, type) {//遍历dems
  91. if (val[i] && i < val.length) {
  92. this.options[type] = val[i];//把当前显示值赋值给options
  93. } else if (force) {
  94. this.options[type] = '';
  95. }
  96. this.output(type);//输出下拉框显示数据
  97. }, this));
  98. this.tab(PROVINCE);
  99. this.feedText();//界面显示选择的内容
  100. this.feedVal();//input标签value赋值
  101. },
  102. //dems赋值
  103. defineDems: function () {
  104. var stop = false;
  105. $.each([PROVINCE, CITY, DISTRICT,COUNTY], $.proxy(function (i, type) {
  106. if (!stop) {
  107. this.dems.push(type);
  108. }
  109. if (type === this.options.level) {
  110. stop = true;
  111. }
  112. }, this));
  113. },
  114. includeDem: function (type) {
  115. return $.inArray(type, this.dems) !== -1;
  116. },
  117. getPosition: function () {
  118. var p, h, w, s, pw;
  119. p = this.$element.position();
  120. s = this.getSize(this.$element);
  121. h = s.height;
  122. w = s.width;
  123. if (this.options.responsive) {
  124. pw = this.$element.offsetParent().width();
  125. if (pw) {
  126. w = w / pw;
  127. if (w > 0.99) {
  128. w = 1;
  129. }
  130. w = w * 100 + '%';
  131. }
  132. }
  133. return {
  134. top: p.top || 0,
  135. left: p.left || 0,
  136. height: h,
  137. width: w
  138. };
  139. },
  140. getSize: function ($dom) {
  141. var $wrap, $clone, sizes;
  142. if (!$dom.is(':visible')) {
  143. $wrap = $("<div />").appendTo($("body"));
  144. $wrap.css({
  145. "position": "absolute !important",
  146. "visibility": "hidden !important",
  147. "display": "block !important"
  148. });
  149. $clone = $dom.clone().appendTo($wrap);
  150. sizes = {
  151. width: $clone.outerWidth(),
  152. height: $clone.outerHeight()
  153. };
  154. $wrap.remove();
  155. } else {
  156. sizes = {
  157. width: $dom.outerWidth(),
  158. height: $dom.outerHeight()
  159. };
  160. }
  161. return sizes;
  162. },
  163. getWidthStyle: function (w, dropdown) {
  164. if (this.options.responsive && !$.isNumeric(w)) {
  165. return 'width:' + w + ';';
  166. } else {
  167. return 'width:' + (dropdown ? Math.max(320, w) : w) + 'px;';
  168. }
  169. },
  170. //绑定事件
  171. bind: function () {
  172. var $this = this;
  173. $(document).on('click', (this._mouteclick = function (e) {
  174. var $target = $(e.target);
  175. var $dropdown, $span, $input;
  176. if ($target.is('.city-picker-span')) {
  177. $span = $target;
  178. } else if ($target.is('.city-picker-span *')) {
  179. $span = $target.parents('.city-picker-span');
  180. }
  181. if ($target.is('.city-picker-input')) {
  182. $input = $target;
  183. }
  184. if ($target.is('.city-picker-dropdown')) {
  185. $dropdown = $target;
  186. } else if ($target.is('.city-picker-dropdown *')) {
  187. $dropdown = $target.parents('.city-picker-dropdown');
  188. }
  189. if ((!$input && !$span && !$dropdown) ||
  190. ($span && $span.get(0) !== $this.$textspan.get(0)) ||
  191. ($input && $input.get(0) !== $this.$element.get(0)) ||
  192. ($dropdown && $dropdown.get(0) !== $this.$dropdown.get(0))) {
  193. $this.close(true);
  194. }
  195. }));
  196. this.$element.on('change', (this._changeElement = $.proxy(function () {
  197. this.close(true);
  198. this.refresh(true);
  199. }, this))).on('focus', (this._focusElement = $.proxy(function () {
  200. this.needBlur = true;
  201. this.open();
  202. }, this))).on('blur', (this._blurElement = $.proxy(function () {
  203. if (this.needBlur) {
  204. this.needBlur = false;
  205. this.close(true);
  206. }
  207. }, this)));
  208. this.$textspan.on('click', function (e) {
  209. var $target = $(e.target), type;
  210. $this.needBlur = false;
  211. if ($target.is('.select-item')) {
  212. type = $target.data('count');
  213. $this.open(type);
  214. } else {
  215. if ($this.$dropdown.is(':visible')) {
  216. $this.close();
  217. } else {
  218. $this.open();
  219. }
  220. }
  221. }).on('mousedown', function () {
  222. $this.needBlur = false;
  223. });
  224. this.$dropdown.on('click', '.city-select a', function () {
  225. var $select = $(this).parents('.city-select');
  226. var $active = $select.find('a.active');
  227. var last = $select.next().length === 0;
  228. $active.removeClass('active');
  229. $(this).addClass('active');
  230. if ($active.data('code') !== $(this).data('code')) {
  231. $select.data('item', {
  232. address: $(this).attr('title'), code: $(this).data('code')
  233. });
  234. $(this).trigger(EVENT_CHANGE);
  235. $this.feedText();
  236. $this.feedVal(true);
  237. if (last) {
  238. $this.close();
  239. }
  240. }
  241. }).on('click', '.city-select-tab a', function () {
  242. if (!$(this).hasClass('active')) {
  243. var type = $(this).data('count');
  244. $this.tab(type);
  245. }
  246. }).on('mousedown', function () {
  247. $this.needBlur = false;
  248. });
  249. if (this.$province) {
  250. this.$province.on(EVENT_CHANGE, (this._changeProvince = $.proxy(function () {
  251. if(this.output(CITY)){//判断下一个tab是否有数据,没有则关闭下拉
  252. $this.close();
  253. return;
  254. };
  255. this.tab(CITY);
  256. }, this)));
  257. }
  258. if (this.$city) {
  259. this.$city.on(EVENT_CHANGE, (this._changeCity = $.proxy(function () {
  260. if(this.output(DISTRICT)){
  261. $this.close();
  262. return;
  263. };
  264. this.tab(DISTRICT);
  265. }, this)));
  266. }
  267. if (this.$district) {
  268. this.$district.on(EVENT_CHANGE, (this._changeDistrict = $.proxy(function () {
  269. if(this.output(COUNTY)){
  270. $this.close();
  271. return;
  272. };
  273. this.tab(COUNTY);
  274. }, this)));
  275. }
  276. },
  277. //显示下拉
  278. open: function (type) {
  279. type = type || PROVINCE;
  280. this.$dropdown.show();
  281. this.$textspan.addClass('open').addClass('focus');
  282. this.tab(type);
  283. },
  284. //关闭下拉
  285. close: function (blur) {
  286. this.$dropdown.hide();
  287. this.$textspan.removeClass('open');
  288. if (blur) {
  289. this.$textspan.removeClass('focus');
  290. }
  291. },
  292. //解绑事件
  293. unbind: function () {
  294. $(document).off('click', this._mouteclick);
  295. this.$element.off('change', this._changeElement);
  296. this.$element.off('focus', this._focusElement);
  297. this.$element.off('blur', this._blurElement);
  298. this.$textspan.off('click');
  299. this.$textspan.off('mousedown');
  300. this.$dropdown.off('click');
  301. this.$dropdown.off('mousedown');
  302. if (this.$province) {
  303. this.$province.off(EVENT_CHANGE, this._changeProvince);
  304. }
  305. if (this.$city) {
  306. this.$city.off(EVENT_CHANGE, this._changeCity);
  307. }
  308. if (this.$district) {
  309. this.$district.off(EVENT_CHANGE, this._changeDistrict);
  310. }
  311. },
  312. //获取选择项信息
  313. getText: function () {
  314. var text = '';
  315. this.$dropdown.find('.city-select')
  316. .each(function () {
  317. var item = $(this).data('item'),
  318. type = $(this).data('count');
  319. if (item) {
  320. text += ($(this).hasClass('province') ? '' : '/') + '<span class="select-item" data-count="' +
  321. type + '" data-code="' + item.code + '">' + item.address + '</span>';
  322. }
  323. });
  324. return text;
  325. },
  326. getPlaceHolder: function () {
  327. return this.$element.attr('placeholder') || this.options.placeholder;
  328. },
  329. //显示placeholder或者选择的区域
  330. feedText: function () {
  331. var text = this.getText();
  332. if (text) {
  333. this.$textspan.find('>.placeholder').hide();
  334. this.$textspan.find('>.title').html(this.getText()).show();
  335. } else {
  336. this.$textspan.find('>.placeholder').text(this.getPlaceHolder()).show();
  337. this.$textspan.find('>.title').html('').hide();
  338. }
  339. },
  340. getCode: function (count) {
  341. var obj = {}, arr = [];
  342. this.$textspan.find('.select-item')
  343. .each(function () {
  344. var code = $(this).data('code');
  345. var count = $(this).data('count');
  346. obj[count] = code;
  347. arr.push(code);
  348. });
  349. return count ? obj[count] : arr.join('/');
  350. },
  351. getVal: function () {
  352. var text = '';
  353. var code='';
  354. this.$dropdown.find('.city-select')
  355. .each(function () {
  356. var item = $(this).data('item');
  357. if (item) {
  358. text += ($(this).hasClass('province') ? '' : '/') + item.address;
  359. code += ($(this).hasClass('province') ? '' : '_') + item.code;
  360. }
  361. });
  362. $("#addrValue").val(code);
  363. return text;
  364. },
  365. //input的value赋值
  366. feedVal: function (trigger) {
  367. this.$element.val(this.getVal());
  368. if(trigger) {
  369. this.$element.trigger('cp:updated');
  370. }
  371. },
  372. //输出数据
  373. output: function (type) {
  374. var $this = this;
  375. var options = this.options;
  376. //var placeholders = this.placeholders;
  377. var $select = this['$' + type];
  378. var data = type === PROVINCE ? {} : [];
  379. var item;
  380. var districts;
  381. var code;
  382. var matched = null;
  383. var value;
  384. if (!$select || !$select.length) {
  385. return;
  386. }
  387. item = $select.data('item');
  388. value = (item ? item.address : null) || options[type];
  389. code = (
  390. type === PROVINCE ? 86 :
  391. type === CITY ? this.$province && this.$province.find('.active').data('code') :
  392. type === DISTRICT ? this.$city && this.$city.find('.active').data('code') :
  393. type === COUNTY ? this.$district && this.$district.find('.active').data('code') : code
  394. );
  395. districts = $.isNumeric(code) ? ChineseDistricts[code] : null;
  396. if ($.isPlainObject(districts)) {
  397. $.each(districts, function (code, address) {
  398. var provs;
  399. if (type === PROVINCE) {
  400. provs = [];
  401. for (var i = 0; i < address.length; i++) {
  402. if (address[i].address === value) {
  403. matched = {
  404. code: address[i].code,
  405. address: address[i].address
  406. };
  407. }
  408. provs.push({
  409. code: address[i].code,
  410. address: address[i].address,
  411. selected: address[i].address === value
  412. });
  413. }
  414. data[code] = provs;
  415. } else {
  416. if (address === value) {
  417. matched = {
  418. code: code,
  419. address: address
  420. };
  421. }
  422. data.push({
  423. code: code,
  424. address: address,
  425. selected: address === value
  426. });
  427. }
  428. });
  429. }
  430. $select.html(type === PROVINCE ? this.getProvinceList(data) :
  431. this.getList(data, type));
  432. $select.data('item', matched);//当前tab添加item(包含选择对象的内容)
  433. if(! (type === PROVINCE)){//标识:下一个选项没有数据则关闭
  434. if(data.length==0){
  435. return true;
  436. }
  437. }
  438. },
  439. //遍历省份
  440. getProvinceList: function (data) {
  441. var list = [],
  442. $this = this,
  443. simple = this.options.simple;
  444. $.each(data, function (i, n) {
  445. list.push('<dl class="clearfix">');
  446. list.push('<dt>' + i + '</dt><dd>');
  447. $.each(n, function (j, m) {
  448. list.push(
  449. '<a' +
  450. ' title="' + (m.address || '') + '"' +
  451. ' data-code="' + (m.code || '') + '"' +
  452. ' class="' +
  453. (m.selected ? ' active' : '') +
  454. '">' +
  455. ( simple ? $this.simplize(m.address, PROVINCE) : m.address) +
  456. '</a>');
  457. });
  458. list.push('</dd></dl>');
  459. });
  460. return list.join('');
  461. },
  462. //遍历市或区或县
  463. getList: function (data, type) {
  464. var list = [],
  465. $this = this,
  466. simple = this.options.simple;
  467. list.push('<dl class="clearfix"><dd>');
  468. $.each(data, function (i, n) {
  469. list.push(
  470. '<a' +
  471. ' title="' + (n.address || '') + '"' +
  472. ' data-code="' + (n.code || '') + '"' +
  473. ' class="' +
  474. (n.selected ? ' active' : '') +
  475. '">' +
  476. ( simple ? $this.simplize(n.address, type) : n.address) +
  477. '</a>');
  478. });
  479. list.push('</dd></dl>');
  480. return list.join('');
  481. },
  482. //简化名字
  483. simplize: function (address, type) {
  484. address = address || '';
  485. if (type === PROVINCE) {
  486. return address.replace(/[省,市,自治区,壮族,回族,维吾尔]/g, '');
  487. } else if (type === CITY) {
  488. return address.replace(/[市,地区,回族,蒙古,苗族,白族,傣族,景颇族,藏族,彝族,壮族,傈僳族,布依族,侗族]/g, '')
  489. .replace('哈萨克', '').replace('自治州', '').replace(/自治县/, '');
  490. } else if (type === DISTRICT) {
  491. return address.length > 2 ? address.replace(/[市,区,县,旗]/g, '') : address;
  492. }
  493. },
  494. //处理tab显示
  495. tab: function (type) {
  496. var $selects = this.$dropdown.find('.city-select');
  497. var $tabs = this.$dropdown.find('.city-select-tab > a');
  498. var $select = this['$' + type];
  499. var $tab = this.$dropdown.find('.city-select-tab > a[data-count="' + type + '"]');
  500. if ($select) {
  501. $selects.hide();
  502. $select.show();
  503. $tabs.removeClass('active');
  504. $tab.addClass('active');
  505. }
  506. },
  507. reset: function () {
  508. this.$element.val(null).trigger('change');
  509. },
  510. destroy: function () {
  511. this.unbind();
  512. this.$element.removeData(NAMESPACE).removeClass('city-picker-input');
  513. this.$textspan.remove();
  514. this.$dropdown.remove();
  515. }
  516. };
  517. CityPicker.DEFAULTS = {
  518. simple: false,
  519. responsive: false,
  520. placeholder: '请选择省/市/区/县',
  521. level: 'county',
  522. province: '',
  523. city: '',
  524. district: '',
  525. county:''
  526. };
  527. CityPicker.setDefaults = function (options) {
  528. $.extend(CityPicker.DEFAULTS, options);
  529. };
  530. // Save the other citypicker
  531. CityPicker.other = $.fn.citypicker;
  532. // Register as jQuery plugin
  533. $.fn.citypicker = function (option) {
  534. var args = [].slice.call(arguments, 1);
  535. return this.each(function () {
  536. var $this = $(this);
  537. var data = $this.data(NAMESPACE);
  538. var options;
  539. var fn;
  540. if (!data) {
  541. if (/destroy/.test(option)) {
  542. return;
  543. }
  544. options = $.extend({}, $this.data(), $.isPlainObject(option) && option);
  545. $this.data(NAMESPACE, (data = new CityPicker(this, options)));
  546. }
  547. if (typeof option === 'string' && $.isFunction(fn = data[option])) {
  548. fn.apply(data, args);
  549. }
  550. });
  551. };
  552. $.fn.citypicker.Constructor = CityPicker;
  553. $.fn.citypicker.setDefaults = CityPicker.setDefaults;
  554. // No conflict
  555. $.fn.citypicker.noConflict = function () {
  556. $.fn.citypicker = CityPicker.other;
  557. return this;
  558. };
  559. $(function () {
  560. $('[data-toggle="city-picker"]').citypicker();
  561. });
  562. });