jquery.cxselect.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*!
  2. * jQuery cxSelect
  3. * @name jquery.cxselect.js
  4. * @version 1.4.1
  5. * @date 2016-11-02
  6. * @author ciaoca
  7. * @email ciaoca@gmail.com
  8. * @site https://github.com/ciaoca/cxSelect
  9. * @license Released under the MIT license
  10. */
  11. (function(factory) {
  12. if (typeof define === 'function' && define.amd) {
  13. define(['jquery'], factory);
  14. } else {
  15. factory(window.jQuery || window.Zepto || window.$);
  16. };
  17. }(function($) {
  18. var cxSelect = function() {
  19. var self = this;
  20. var dom, settings, callback;
  21. // 分配参数
  22. for (var i = 0, l = arguments.length; i < l; i++) {
  23. if (cxSelect.isJquery(arguments[i]) || cxSelect.isZepto(arguments[i])) {
  24. dom = arguments[i];
  25. } else if (cxSelect.isElement(arguments[i])) {
  26. dom = $(arguments[i]);
  27. } else if (typeof arguments[i] === 'function') {
  28. callback = arguments[i];
  29. } else if (typeof arguments[i] === 'object') {
  30. settings = arguments[i];
  31. };
  32. };
  33. var api = new cxSelect.init(dom, settings);
  34. if (typeof callback === 'function') {
  35. callback(api);
  36. };
  37. return api;
  38. };
  39. cxSelect.isElement = function(o){
  40. if (o && (typeof HTMLElement === 'function' || typeof HTMLElement === 'object') && o instanceof HTMLElement) {
  41. return true;
  42. } else {
  43. return (o && o.nodeType && o.nodeType === 1) ? true : false;
  44. };
  45. };
  46. cxSelect.isJquery = function(o){
  47. return (o && o.length && (typeof jQuery === 'function' || typeof jQuery === 'object') && o instanceof jQuery) ? true : false;
  48. };
  49. cxSelect.isZepto = function(o){
  50. return (o && o.length && (typeof Zepto === 'function' || typeof Zepto === 'object') && Zepto.zepto.isZ(o)) ? true : false;
  51. };
  52. cxSelect.getIndex = function(n, required) {
  53. return required ? n : n - 1;
  54. };
  55. cxSelect.getData = function(data, space) {
  56. if (typeof space === 'string' && space.length) {
  57. space = space.split('.');
  58. for (var i = 0, l = space.length; i < l; i++) {
  59. data = data[space[i]];
  60. };
  61. };
  62. return data;
  63. };
  64. cxSelect.init = function(dom, settings) {
  65. var self = this;
  66. if (!cxSelect.isJquery(dom) && !cxSelect.isZepto(dom)) {return};
  67. var theSelect = {
  68. dom: {
  69. box: dom
  70. }
  71. };
  72. self.attach = cxSelect.attach.bind(theSelect);
  73. self.detach = cxSelect.detach.bind(theSelect);
  74. self.setOptions = cxSelect.setOptions.bind(theSelect);
  75. self.clear = cxSelect.clear.bind(theSelect);
  76. theSelect.changeEvent = function(className) {
  77. //兼容layui
  78. if(typeof className === 'object'){
  79. className = this.className;
  80. }
  81. cxSelect.selectChange.call(theSelect, className);
  82. };
  83. theSelect.settings = $.extend({}, $.cxSelect.defaults, settings, {
  84. url: theSelect.dom.box.data('url'),
  85. emptyStyle: theSelect.dom.box.data('emptyStyle'),
  86. required: theSelect.dom.box.data('required'),
  87. firstTitle: theSelect.dom.box.data('firstTitle'),
  88. firstValue: theSelect.dom.box.data('firstValue'),
  89. jsonSpace: theSelect.dom.box.data('jsonSpace'),
  90. jsonName: theSelect.dom.box.data('jsonName'),
  91. jsonValue: theSelect.dom.box.data('jsonValue'),
  92. jsonSub: theSelect.dom.box.data('jsonSub')
  93. });
  94. var _dataSelects = theSelect.dom.box.data('selects');
  95. if (typeof _dataSelects === 'string' && _dataSelects.length) {
  96. theSelect.settings.selects = _dataSelects.split(',');
  97. };
  98. self.setOptions();
  99. self.attach();
  100. // 使用独立接口获取数据
  101. if (!theSelect.settings.url && !theSelect.settings.data) {
  102. cxSelect.start.apply(theSelect);
  103. // 设置自定义数据
  104. } else if ($.isArray(theSelect.settings.data)) {
  105. cxSelect.start.call(theSelect, theSelect.settings.data);
  106. // 设置 URL,通过 Ajax 获取数据
  107. } else if (typeof theSelect.settings.url === 'string' && theSelect.settings.url.length) {
  108. $.getJSON(theSelect.settings.url, function(json) {
  109. cxSelect.start.call(theSelect, json);
  110. });
  111. };
  112. };
  113. // 设置参数
  114. cxSelect.setOptions = function(opts) {
  115. var self = this;
  116. if (opts) {
  117. $.extend(self.settings, opts);
  118. };
  119. // 初次或重设选择器组
  120. if (!$.isArray(self.selectArray) || !self.selectArray.length || (opts && opts.selects)) {
  121. self.selectArray = [];
  122. if ($.isArray(self.settings.selects) && self.settings.selects.length) {
  123. var _tempSelect;
  124. for (var i = 0, l = self.settings.selects.length; i < l; i++) {
  125. _tempSelect = self.dom.box.find('select.' + self.settings.selects[i]);
  126. if (!_tempSelect || !_tempSelect.length) {break};
  127. self.selectArray.push(_tempSelect);
  128. };
  129. };
  130. };
  131. if (opts) {
  132. if (!$.isArray(opts.data) && typeof opts.url === 'string' && opts.url.length) {
  133. $.getJSON(self.settings.url, function(json) {
  134. cxSelect.start.call(self, json);
  135. });
  136. } else {
  137. cxSelect.start.call(self, opts.data);
  138. };
  139. };
  140. };
  141. // 绑定
  142. cxSelect.attach = function() {
  143. var self = this;
  144. if (!self.attachStatus) {
  145. self.dom.box.on('change', 'select', self.changeEvent);
  146. //监听下拉事件 ##兼容layui
  147. layui.form.on('select()',function(data){
  148. self.changeEvent(data.elem.className);
  149. });
  150. };
  151. if (typeof self.attachStatus === 'boolean') {
  152. cxSelect.start.call(self);
  153. };
  154. self.attachStatus = true;
  155. };
  156. // 移除绑定
  157. cxSelect.detach = function() {
  158. var self = this;
  159. self.dom.box.off('change', 'select', self.changeEvent);
  160. self.attachStatus = false;
  161. };
  162. // 清空选项
  163. cxSelect.clear = function(index) {
  164. var self = this;
  165. var _style = {
  166. display: '',
  167. visibility: ''
  168. };
  169. index = isNaN(index) ? 0 : index;
  170. // 清空后面的 select
  171. for (var i = index, l = self.selectArray.length; i < l; i++) {
  172. self.selectArray[i].empty().prop('disabled', true);
  173. if (self.settings.emptyStyle === 'none') {
  174. _style.display = 'none';
  175. } else if (self.settings.emptyStyle === 'hidden') {
  176. _style.visibility = 'hidden';
  177. };
  178. self.selectArray[i].css(_style);
  179. };
  180. };
  181. cxSelect.start = function(data) {
  182. var self = this;
  183. if ($.isArray(data)) {
  184. self.settings.data = cxSelect.getData(data, self.settings.jsonSpace);
  185. };
  186. if (!self.selectArray.length) {return};
  187. // 保存默认值
  188. for (var i = 0, l = self.selectArray.length; i < l; i++) {
  189. if (typeof self.selectArray[i].attr('data-value') !== 'string' && self.selectArray[i][0].options.length) {
  190. self.selectArray[i].attr('data-value', self.selectArray[i].val());
  191. };
  192. };
  193. if (self.settings.data || (typeof self.selectArray[0].data('url') === 'string' && self.selectArray[0].data('url').length)) {
  194. cxSelect.getOptionData.call(self, 0);
  195. } else {
  196. self.selectArray[0].prop('disabled', false).css({
  197. 'display': '',
  198. 'visibility': ''
  199. });
  200. };
  201. };
  202. // 获取选项数据
  203. cxSelect.getOptionData = function(index) {
  204. var self = this;
  205. if (typeof index !== 'number' || isNaN(index) || index < 0 || index >= self.selectArray.length) {return};
  206. var _indexPrev = index - 1;
  207. var _select = self.selectArray[index];
  208. var _selectData;
  209. var _valueIndex;
  210. var _dataUrl = _select.data('url');
  211. var _jsonSpace = typeof _select.data('jsonSpace') === 'undefined' ? self.settings.jsonSpace : _select.data('jsonSpace');
  212. var _query = {};
  213. var _queryName;
  214. var _selectName;
  215. var _selectValue;
  216. cxSelect.clear.call(self, index);
  217. // 使用独立接口
  218. if (typeof _dataUrl === 'string' && _dataUrl.length) {
  219. if (index > 0) {
  220. for (var i = 0, j = 1; i < index; i++, j++) {
  221. _queryName = self.selectArray[j].data('queryName');
  222. _selectName = self.selectArray[i].attr('name');
  223. _selectValue = self.selectArray[i].val();
  224. if (typeof _queryName === 'string' && _queryName.length) {
  225. _query[_queryName] = _selectValue;
  226. } else if (typeof _selectName === 'string' && _selectName.length) {
  227. _query[_selectName] = _selectValue;
  228. };
  229. };
  230. };
  231. $.getJSON(_dataUrl, _query, function(json) {
  232. _selectData = cxSelect.getData(json, _jsonSpace);
  233. cxSelect.buildOption.call(self, index, _selectData);
  234. });
  235. // 使用整合数据
  236. } else if (self.settings.data && typeof self.settings.data === 'object') {
  237. _selectData = self.settings.data;
  238. for (var i = 0; i < index; i++) {
  239. _valueIndex = cxSelect.getIndex(self.selectArray[i][0].selectedIndex, typeof self.selectArray[i].data('required') === 'boolean' ? self.selectArray[i].data('required') : self.settings.required);
  240. if (typeof _selectData[_valueIndex] === 'object' && $.isArray(_selectData[_valueIndex][self.settings.jsonSub]) && _selectData[_valueIndex][self.settings.jsonSub].length) {
  241. _selectData = _selectData[_valueIndex][self.settings.jsonSub];
  242. } else {
  243. _selectData = null;
  244. break;
  245. };
  246. };
  247. cxSelect.buildOption.call(self, index, _selectData);
  248. };
  249. };
  250. // 构建选项列表
  251. cxSelect.buildOption = function(index, data) {
  252. var self = this;
  253. var _select = self.selectArray[index];
  254. var _required = typeof _select.data('required') === 'boolean' ? _select.data('required') : self.settings.required;
  255. var _firstTitle = typeof _select.data('firstTitle') === 'undefined' ? self.settings.firstTitle : _select.data('firstTitle');
  256. var _firstValue = typeof _select.data('firstValue') === 'undefined' ? self.settings.firstValue : _select.data('firstValue');
  257. var _jsonName = typeof _select.data('jsonName') === 'undefined' ? self.settings.jsonName : _select.data('jsonName');
  258. var _jsonValue = typeof _select.data('jsonValue') === 'undefined' ? self.settings.jsonValue : _select.data('jsonValue');
  259. if (!$.isArray(data)) {return};
  260. var _html = !_required ? '<option value="' + String(_firstValue) + '">' + String(_firstTitle) + '</option>' : '';
  261. // 区分标题、值的数据
  262. if (typeof _jsonName === 'string' && _jsonName.length) {
  263. // 无值字段时使用标题作为值
  264. if (typeof _jsonValue !== 'string' || !_jsonValue.length) {
  265. _jsonValue = _jsonName;
  266. };
  267. for (var i = 0, l = data.length; i < l; i++) {
  268. _html += '<option value="' + String(data[i][_jsonValue]) + '">' + String(data[i][_jsonName]) + '</option>';
  269. };
  270. // 数组即为值的数据
  271. } else {
  272. for (var i = 0, l = data.length; i < l; i++) {
  273. _html += '<option value="' + String(data[i]) + '">' + String(data[i]) + '</option>';
  274. };
  275. };
  276. _select.html(_html).prop('disabled', false).css({
  277. 'display': '',
  278. 'visibility': ''
  279. });
  280. // 初次加载设置默认值
  281. if (typeof _select.attr('data-value') === 'string') {
  282. _select.val(String(_select.attr('data-value'))).removeAttr('data-value');
  283. if (_select[0].selectedIndex < 0) {
  284. _select[0].options[0].selected = true;
  285. };
  286. };
  287. if (_required || _select[0].selectedIndex > 0) {
  288. _select.trigger('change');
  289. };
  290. //刷新下拉 ##兼容layui
  291. layui.form.render('select');
  292. };
  293. // 改变选择时的处理
  294. cxSelect.selectChange = function(name) {
  295. var self = this;
  296. if (typeof name !== 'string' || !name.length) {return};
  297. var index;
  298. name = name.replace(/\s+/g, ',');
  299. name = ',' + name + ',';
  300. // 获取当前 select 位置
  301. for (var i = 0, l = self.selectArray.length; i < l; i++) {
  302. if (name.indexOf(',' + self.settings.selects[i] + ',') > -1) {
  303. index = i;
  304. break;
  305. };
  306. };
  307. if (typeof index === 'number' && index > -1) {
  308. index += 1;
  309. cxSelect.getOptionData.call(self, index);
  310. };
  311. };
  312. $.cxSelect = function() {
  313. return cxSelect.apply(this, arguments);
  314. };
  315. // 默认值
  316. $.cxSelect.defaults = {
  317. selects: [], // 下拉选框组
  318. url: null, // 列表数据文件路径(URL)或数组数据
  319. data: null, // 自定义数据
  320. emptyStyle: null, // 无数据状态显示方式
  321. required: false, // 是否为必选
  322. firstTitle: '请选择', // 第一个选项的标题
  323. firstValue: '', // 第一个选项的值
  324. jsonSpace: '', // 数据命名空间
  325. jsonName: 'n', // 数据标题字段名称
  326. jsonValue: '', // 数据值字段名称
  327. jsonSub: 's' // 子集数据字段名称
  328. };
  329. $.fn.cxSelect = function(settings, callback) {
  330. this.each(function(i) {
  331. $.cxSelect(this, settings, callback);
  332. });
  333. return this;
  334. };
  335. }));