|
|
(function($){
|
|
|
// a case insensitive jQuery :contains selector
|
|
|
$.expr[":"].searchableSelectContains = $.expr.createPseudo(function(arg) {
|
|
|
return function( elem ) {
|
|
|
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
|
|
|
};
|
|
|
});
|
|
|
|
|
|
/*$.expr[":"].searchableSelectContains = function(obj,index,meta){
|
|
|
return $(obj).text().toUpperCase().indexOf(meta[3].toUpperCase()) >= 0;
|
|
|
};*/
|
|
|
|
|
|
|
|
|
$.searchableSelect = function(element, options) {
|
|
|
this.element = element;
|
|
|
this.options = options || {};
|
|
|
this.init();
|
|
|
|
|
|
var _this = this;
|
|
|
|
|
|
this.searchableElement.click(function(event){
|
|
|
// event.stopPropagation();
|
|
|
_this.show();
|
|
|
}).on('keydown', function(event){
|
|
|
if (event.which === 13 || event.which === 40 || event.which == 38){
|
|
|
event.preventDefault();
|
|
|
_this.show();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
$(document).on('click', null, function(event){
|
|
|
if(_this.searchableElement.has($(event.target)).length === 0)
|
|
|
_this.hide();
|
|
|
});
|
|
|
|
|
|
this.input.on('keydown', function(event){
|
|
|
event.stopPropagation();
|
|
|
if(event.which === 13){ //enter
|
|
|
event.preventDefault();
|
|
|
_this.selectCurrentHoverItem();
|
|
|
_this.hide();
|
|
|
} else if (event.which == 27) { //ese
|
|
|
_this.hide();
|
|
|
} else if (event.which == 40) { //down
|
|
|
_this.hoverNextItem();
|
|
|
} else if (event.which == 38) { //up
|
|
|
_this.hoverPreviousItem();
|
|
|
}
|
|
|
}).on('keyup', function(event){
|
|
|
if(event.which != 13 && event.which != 27 && event.which != 38 && event.which != 40)
|
|
|
_this.filter();
|
|
|
})
|
|
|
}
|
|
|
|
|
|
var $sS = $.searchableSelect;
|
|
|
|
|
|
$sS.fn = $sS.prototype = {
|
|
|
version: '0.0.1'
|
|
|
};
|
|
|
|
|
|
$sS.fn.extend = $sS.extend = $.extend;
|
|
|
|
|
|
$sS.fn.extend({
|
|
|
init: function(){
|
|
|
var _this = this;
|
|
|
this.element.hide();
|
|
|
|
|
|
this.searchableElement = $('<div tabindex="0" class="searchable-select"></div>');
|
|
|
this.holder = $('<div class="searchable-select-holder"></div>');
|
|
|
this.dropdown = $('<div class="searchable-select-dropdown searchable-select-hide"></div>');
|
|
|
this.input = $('<input type="text" class="searchable-select-input" />');
|
|
|
this.items = $('<div class="searchable-select-items"></div>');
|
|
|
this.caret = $('<span class="searchable-select-caret"></span>');
|
|
|
|
|
|
this.scrollPart = $('<div class="searchable-scroll"></div>');
|
|
|
this.hasPrivious = $('<div class="searchable-has-privious">...</div>');
|
|
|
this.hasNext = $('<div class="searchable-has-next">...</div>');
|
|
|
|
|
|
this.hasNext.on('mouseenter', function(){
|
|
|
_this.hasNextTimer = null;
|
|
|
|
|
|
var f = function(){
|
|
|
var scrollTop = _this.items.scrollTop();
|
|
|
_this.items.scrollTop(scrollTop + 20);
|
|
|
_this.hasNextTimer = setTimeout(f, 50);
|
|
|
}
|
|
|
|
|
|
f();
|
|
|
}).on('mouseleave', function(event) {
|
|
|
clearTimeout(_this.hasNextTimer);
|
|
|
});
|
|
|
|
|
|
this.hasPrivious.on('mouseenter', function(){
|
|
|
_this.hasPriviousTimer = null;
|
|
|
|
|
|
var f = function(){
|
|
|
var scrollTop = _this.items.scrollTop();
|
|
|
_this.items.scrollTop(scrollTop - 20);
|
|
|
_this.hasPriviousTimer = setTimeout(f, 50);
|
|
|
}
|
|
|
|
|
|
f();
|
|
|
}).on('mouseleave', function(event) {
|
|
|
clearTimeout(_this.hasPriviousTimer);
|
|
|
});
|
|
|
|
|
|
this.dropdown.append(this.input);
|
|
|
this.dropdown.append(this.scrollPart);
|
|
|
|
|
|
this.scrollPart.append(this.hasPrivious);
|
|
|
this.scrollPart.append(this.items);
|
|
|
this.scrollPart.append(this.hasNext);
|
|
|
|
|
|
this.searchableElement.append(this.caret);
|
|
|
this.searchableElement.append(this.holder);
|
|
|
this.searchableElement.append(this.dropdown);
|
|
|
this.element.after(this.searchableElement);
|
|
|
|
|
|
this.buildItems();
|
|
|
this.setPriviousAndNextVisibility();
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* 2023/09/27:设置值:参考filter
|
|
|
* @param value
|
|
|
*/
|
|
|
set:function(value){
|
|
|
let n;
|
|
|
if(value !== undefined){
|
|
|
n = this.items.find('.searchable-select-item[data-value=\''+value+'\']')
|
|
|
n.removeClass('searchable-select-hide');
|
|
|
}
|
|
|
if(this.currentSelectedItem.hasClass('searchable-select-hide') && this.items.find('.searchable-select-item:not(.searchable-select-hide)').length > 0){
|
|
|
this.hoverFirstNotHideItem();
|
|
|
}
|
|
|
|
|
|
this.setPriviousAndNextVisibility();
|
|
|
//触发点击事件
|
|
|
if(n){
|
|
|
$(n).click()
|
|
|
}
|
|
|
},
|
|
|
|
|
|
filter: function(){
|
|
|
var text = this.input.val();
|
|
|
this.items.find('.searchable-select-item').addClass('searchable-select-hide');
|
|
|
if(text != ''){
|
|
|
this.items.find('.searchable-select-item:searchableSelectContains('+text+')').removeClass('searchable-select-hide');
|
|
|
}else{
|
|
|
this.items.find('.searchable-select-item').removeClass('searchable-select-hide');
|
|
|
}
|
|
|
if(this.currentSelectedItem.hasClass('searchable-select-hide') && this.items.find('.searchable-select-item:not(.searchable-select-hide)').length > 0){
|
|
|
this.hoverFirstNotHideItem();
|
|
|
}
|
|
|
|
|
|
this.setPriviousAndNextVisibility();
|
|
|
},
|
|
|
|
|
|
hoverFirstNotHideItem: function(){
|
|
|
this.hoverItem(this.items.find('.searchable-select-item:not(.searchable-select-hide)').first());
|
|
|
},
|
|
|
|
|
|
selectCurrentHoverItem: function(){
|
|
|
if(!this.currentHoverItem.hasClass('searchable-select-hide'))
|
|
|
this.selectItem(this.currentHoverItem);
|
|
|
},
|
|
|
|
|
|
hoverPreviousItem: function(){
|
|
|
if(!this.hasCurrentHoverItem())
|
|
|
this.hoverFirstNotHideItem();
|
|
|
else{
|
|
|
var prevItem = this.currentHoverItem.prevAll('.searchable-select-item:not(.searchable-select-hide):first')
|
|
|
if(prevItem.length > 0)
|
|
|
this.hoverItem(prevItem);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
hoverNextItem: function(){
|
|
|
if(!this.hasCurrentHoverItem())
|
|
|
this.hoverFirstNotHideItem();
|
|
|
else{
|
|
|
var nextItem = this.currentHoverItem.nextAll('.searchable-select-item:not(.searchable-select-hide):first')
|
|
|
if(nextItem.length > 0)
|
|
|
this.hoverItem(nextItem);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
buildItems: function(){
|
|
|
var _this = this;
|
|
|
this.element.find('option').each(function(){
|
|
|
var item = $('<div class="searchable-select-item" data-value="'+$(this).attr('value')+'">'+$(this).text()+'</div>');
|
|
|
|
|
|
if(this.selected){
|
|
|
_this.selectItem(item);
|
|
|
_this.hoverItem(item);
|
|
|
}
|
|
|
|
|
|
item.on('mouseenter', function(){
|
|
|
$(this).addClass('hover');
|
|
|
}).on('mouseleave', function(){
|
|
|
$(this).removeClass('hover');
|
|
|
}).click(function(event){
|
|
|
event.stopPropagation();
|
|
|
_this.selectItem($(this));
|
|
|
_this.hide();
|
|
|
});
|
|
|
|
|
|
_this.items.append(item);
|
|
|
});
|
|
|
|
|
|
this.items.on('scroll', function(){
|
|
|
_this.setPriviousAndNextVisibility();
|
|
|
})
|
|
|
},
|
|
|
show: function(){
|
|
|
this.dropdown.removeClass('searchable-select-hide');
|
|
|
this.input.focus();
|
|
|
this.status = 'show';
|
|
|
this.setPriviousAndNextVisibility();
|
|
|
this.dropdown.css('z-index', 9999999); //打开下拉列表时调高z-index层级
|
|
|
},
|
|
|
|
|
|
hide: function(){
|
|
|
if(!(this.status === 'show'))
|
|
|
return;
|
|
|
|
|
|
if(this.items.find(':not(.searchable-select-hide)').length === 0)
|
|
|
this.input.val('');
|
|
|
this.dropdown.addClass('searchable-select-hide');
|
|
|
this.searchableElement.trigger('focus');
|
|
|
this.status = 'hide';
|
|
|
this.dropdown.css('z-index', 1); //关闭下拉列表时恢复z-index层级
|
|
|
},
|
|
|
|
|
|
hasCurrentSelectedItem: function(){
|
|
|
return this.currentSelectedItem && this.currentSelectedItem.length > 0;
|
|
|
},
|
|
|
|
|
|
selectItem: function(item){
|
|
|
if(this.hasCurrentSelectedItem())
|
|
|
this.currentSelectedItem.removeClass('selected');
|
|
|
|
|
|
this.currentSelectedItem = item;
|
|
|
item.addClass('selected');
|
|
|
|
|
|
this.hoverItem(item);
|
|
|
|
|
|
this.holder.text(item.text());
|
|
|
var value = item.data('value');
|
|
|
this.holder.data('value', value);
|
|
|
this.element.val(value);
|
|
|
|
|
|
if(this.options.afterSelectItem){
|
|
|
this.options.afterSelectItem.apply(this);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
hasCurrentHoverItem: function(){
|
|
|
return this.currentHoverItem && this.currentHoverItem.length > 0;
|
|
|
},
|
|
|
|
|
|
hoverItem: function(item){
|
|
|
if(this.hasCurrentHoverItem())
|
|
|
this.currentHoverItem.removeClass('hover');
|
|
|
|
|
|
if(item.outerHeight() + item.position().top > this.items.height())
|
|
|
this.items.scrollTop(this.items.scrollTop() + item.outerHeight() + item.position().top - this.items.height());
|
|
|
else if(item.position().top < 0)
|
|
|
this.items.scrollTop(this.items.scrollTop() + item.position().top);
|
|
|
|
|
|
this.currentHoverItem = item;
|
|
|
item.addClass('hover');
|
|
|
},
|
|
|
|
|
|
setPriviousAndNextVisibility: function(){
|
|
|
if(this.items.scrollTop() === 0){
|
|
|
this.hasPrivious.addClass('searchable-select-hide');
|
|
|
this.scrollPart.removeClass('has-privious');
|
|
|
} else {
|
|
|
this.hasPrivious.removeClass('searchable-select-hide');
|
|
|
this.scrollPart.addClass('has-privious');
|
|
|
}
|
|
|
|
|
|
if(this.items.scrollTop() + this.items.innerHeight() >= this.items[0].scrollHeight){
|
|
|
this.hasNext.addClass('searchable-select-hide');
|
|
|
this.scrollPart.removeClass('has-next');
|
|
|
} else {
|
|
|
this.hasNext.removeClass('searchable-select-hide');
|
|
|
this.scrollPart.addClass('has-next');
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
//TODO 如果只有一个元素,则返回创建的下拉框组件,否则返回数组
|
|
|
$.fn.searchableSelect = function(options){
|
|
|
let res = []
|
|
|
this.each(function(){
|
|
|
var sS = new $sS($(this), options);
|
|
|
res.push(sS)
|
|
|
});
|
|
|
if(res.length === 1){
|
|
|
return res[0]
|
|
|
}
|
|
|
return res;
|
|
|
// return this;
|
|
|
};
|
|
|
|
|
|
})(jQuery);
|