!(function($) { var iconHtml = ''; var wrapHtml = `
` + `
` + `
` + `
`; var ulHtml = ``; $.widget('ui.combobox', { options: { /** * @cfg {String} placeholder 为空时的提示信息 */ placeholder: '-- Please Select --', /** * @cfg {Boolean} editable 是否手动输入,默认为 true */ editable: true, /** * @cfg {String} [dataTextField='name'] 数据源显示字段 */ dataTextField: "name", /** * @cfg {String} [dataValueField='value'] 数据源取值字段 */ dataValueField: "value", /** * @cfg {Array} [dataSource='name'] 数据源 */ dataSource: [], /** * @cfg {Number} [delay=500] 延迟搜索,避免过多无效搜索,单位毫秒ms,默认是500ms */ delay: 500, /** * @cfg {Function} [change=null] 定义改变下拉框值事件函数,事件名称combobox:change */ change: null }, /** jQueryUI Widget生命周期方法,生成HTML,事件绑定 */ _create: function() { console.log('--- create ---'); // 包装节点 this.element.addClass('filter-title').attr('placeholder', this.options.placeholder); this.element.wrap(wrapHtml); this.element.after(iconHtml); this.element.parent().after(ulHtml); this.$filterText = this.element.parent(); this.$ul = this.element.parent().siblings('ul'); this.$icon = this.element.siblings('span'); // 存放输入框搜索的定时器 this.timeoutArr = []; // 设置是否可编辑 this.setEditable(this.options.editable); // 读取 dataSource,处理数据 this._initDataSource(); // 绑定事件 this._delegateEvent(); }, /** jQueryUI Widget生命周期方法 */ _init: function() { console.log('--- init ---'); }, _setOptions: function(options) { for (var key in options) { this._setOption(key, options[key]); } return this; }, _setOption: function (key, value) { $.Widget.prototype._setOption.apply(this, arguments); if (key === 'editable') { this.setEditable(value); } if (key === 'disabled') { this.$filterText.attr('disabled', value); this.element.attr('disabled', value); this.$icon.attr('disabled', value); } return this; }, setEditable: function (editable) { editable = !!editable; this.options.editable = editable; if (editable) { this.element.removeAttr('readonly'); this._on(this.element, { 'keydown': '_onKeyDown' }); } else { this.element.attr("readonly", true); this._off(this.element); } return this; }, _destroy: function() { // 移除属性 this.element.removeClass('input-combobox filter-title').removeAttr('data-value') .attr('readonly', false).val(''); // 移除 dom this.$filterText.parent().after(this.element); this.$filterText.parent().remove(); }, _onKeyDown: function(e) { console.log('--- keydown ---'); var timeout = setTimeout(function(context) { return function() { context.timeoutArr.shift(); var inputVal = context.element.val(); if (inputVal) { context.$ul.find('li').addClass('hide').not('.filter-disabled') .filter(function(index, item) { return item.firstChild.text.indexOf(inputVal) > -1; }).removeClass('hide'); } else { context.$ul.find('li').removeClass('hide'); } console.log('--- delay ' + context.options.delay + 'ms search ---'); }; }(this), this.options.delay); if (this.timeoutArr.length > 0) { var preTimeout = this.timeoutArr.shift(); clearTimeout(preTimeout); } this.timeoutArr.push(timeout); }, _initDataSource: function() { // dataSource没有数据,不处理 if (!this.options.dataSource || this.options.dataSource.length <= 0) { this.options.dataSource = []; return; } // 过滤掉 dataSource中键值对与 dataTextField、dataValueField不符合的记录 this.options.dataSource = this.options.dataSource.filter(function(currentValue, index, arr) { return currentValue[this.options.dataTextField] != null && currentValue[this.options.dataValueField] != null; }, this); // 清空掉 item选项 this.$ul.empty(); if (this.options.dataSource && this.options.dataSource.length) { $.each(this.options.dataSource, function (i, item) { this._appendToUl(item); }.bind(this)); } }, _appendToUl: function(item) { // 处理dataTextField的值为非字符串的问题 item[this.options.dataTextField] += ""; // 组建 item var liHtml = `
  • #{text}
  • `; liHtml = $.fn.format(liHtml, { value: item[this.options.dataValueField], text: item[this.options.dataTextField], title: item[this.options.dataTextField], class: ((item.selected ? 'filter-selected' : '') + ' ' + (item.disabled ? 'filter-disabled' : '')).trim() }); this.$ul.append(liHtml); if (item.selected) { // 设置 item选中 this._select(item); } }, _delegateEvent: function() { // 展示/隐藏 组件内容 this.$filterText.on('click.combobox', 'input, span', function() { if (!this.options.disabled) { this.$ul.slideToggle(100); this.$ul.toggleClass('filter-open'); this.$icon.toggleClass('filter-show'); } }.bind(this)); $(this.document).on('mousedown.combobox', function(e) { var target = e.target ? e.target : e.srcElement; // 过滤某些元素,点击后不触发 if (target.parentNode !== this.$filterText.get(0) && target !== this.$filterText.get(0) && target.parentNode.parentNode !== this.$ul.get(0)) { this.$ul.slideUp(100); this.$ul.removeClass('filter-open'); this.$icon.removeClass('filter-show'); } }.bind(this)); // 选择某一项内容,回显 $(this.$ul).on('click.combobox', 'li:not(.filter-disabled)', function(e) { var currentTarget = e.currentTarget; // 设置其选中样式 this._select({ value: $(currentTarget).attr('data-value'), name: currentTarget.firstChild.text }); // 隐藏选项组 this.$ul.slideToggle(100); this.$ul.toggleClass('filter-open'); this.$icon.toggleClass('filter-show'); this.$ul.find('li').removeClass('hide'); }.bind(this)); }, /** * @method value 取值或者赋值 * @param {String} [value] 设置值选中,为空则取控件值 * @return {String} 控件值,赋值操作则没有返回值 */ value: function(value) { if (arguments.length === 0) { // 取值操作 console.log('--- get value ---'); return this.$ul.find('li[class="filter-selected"]').attr('data-value'); } // 赋值操作 console.log('--- set value ---'); var selectedItem = null; for(var i = 0, len = this.options.dataSource.length; i < len; i++) { var item = this.options.dataSource[i], isSelected = item.selected; if (item[this.options.dataValueField] === value && !item.disabled) { selectedItem = item; break; } } this._select(selectedItem); }, _select: function(item) { if (item) { var value = item[this.options.dataValueField], text = item[this.options.dataTextField]; this.element.val(text); // 判断选择的节点是否和上一个选择的节点相同 if (this.element.attr('data-value') !== value) { this.$ul.find('li').removeClass('filter-selected').filter(function(index, element) { return $(element).attr('data-value') === value; }.bind(this)).addClass('filter-selected'); this.element.attr('data-value', value); this._trigger('change', null, { value: value, text: text }); } } else { this.$ul.find('li').removeClass('filter-selected'); this.element.val(''); this.element.removeAttr('data-value'); this._trigger('change'); } }, /** * @method clear 清空选择内容 */ clear: function() { this._select(null); }, /** * @method getSelectedItem 获取被选中的项键值对 * @return 键值对 */ getSelectedItem: function() { console.log('--- getSelectedItem ---'); return { value: this.element.attr('data-value'), text: this.element.val() } }, /** * @method append 向 dataSource里追加数据,显示在页面上 * @param {Array} [items] 追加的内容 * @return this */ append: function(items) { // 传入参数不是数组,直接返回控件对象 this if (!(items instanceof Array)) { return this; } // 过滤掉键值对与 options参数不一致的内容 items = items.filter(function(item, index) { return item.hasOwnProperty(this.options.dataValueField) && item.hasOwnProperty(this.options.dataTextField); }.bind(this)); if (items.length === 0) { return this; } // 将追加的数据放入到 options里,并追加到 ul元素里面 Array.prototype.push.apply(this.options.dataSource, items); $.each(items, function(index, item) { this._appendToUl(item); }.bind(this)); return this; }, /** * @method remove 从 dataSource中移除某些项 * @param {Array} [items] 移除的内容 * @return this */ remove: function(items) { // 传入参数不是数组,直接返回控件对象 this if (!(items instanceof Array)) { return this; } // 过滤掉键值对与 options参数不一致的内容 items = items.filter(function(item, index) { return item.hasOwnProperty(this.options.dataValueField) && item.hasOwnProperty(this.options.dataTextField); }.bind(this)); if (items.length === 0) { return this; } // 从 options里移除,并从 ul元素里面移除 $.each(items, function(index, item) { var $target = this.$ul.find('li[data-value=' + item[this.options.dataValueField] + ']') .find('a[title='+ item[this.options.dataTextField] +']').end().addClass('hide'); var pos = $target.prevAll().length; this.options.dataSource.splice(pos, 1); // 若移除的元素被选中,则需要清空选中 if ($target.hasClass('filter-selected')) { this._select(null); } }.bind(this)); return this; } }); /** 在 jQuery的原型链上新增一个格式化函数 */ jQuery.fn.format = function(str) { var args = Array.prototype.slice.call(arguments, 1); var reg = /\#{([^{}]+)}/gm; return str.replace(reg, function(match, name, index, str) { // 判断括号匹配的内容是否是数字 var content = Number(name); if (content >= 0) { return args[content]; } // 不是数字的话,应该就是对象 var object = args[0]; if (object && object !== void(0)) { return object[name]; } // 未匹配到,返回空串 return ''; }); } })(jQuery)