文章目录
下拉框搜索分页功能开发
最近需要开发一个下拉框从服务器通过Ajax请求搜索数据库并且分页的组件,源码和demo放在下面可以直接使用
功能由searchable-select改造而成
功能
- 点击下拉框,自动聚焦输入框
- 上下键可以选中数据,选中数据后回车填值,关闭下拉框
- 输入框输入值后按下回车、点击查询按钮,触发搜索事件
- 分页,由于组件空间有限仅显示5页
使用
源码和demo都已经在下面给出了,自取,demo包括ajax获取数据
1.引入组件
html
<script src="searchserver-select.js"></script>
<link href="searchable-select.css" rel="stylesheet">
2.添加input输入框
html
<input id="b">
3.创建组件
自定义数据获取规则,以下为获取测试数据,真实开发Ajax demo下面有给出
js
//传入一个function,keyWord表示搜索框的值,另外两个不必多说
$('#b1').searchServerSelect({}, function (keyWord, pageIndex = 1, pageSize = 10) {
let res = {}
//以下为测试数据
let dataList = []
for (let i = 0; i < 10; i++) {
dataList.push({value: keyWord + i, label: keyWord + i})
}
res.pageIndex = pageIndex
res.pageSize = pageSize
res.itemCount = 105
res.data = dataList
//返回一个Promise
return new Promise(function(resolve, reject){
//当异步代码执行成功时,我们会调用resolve, 当异步代码失败时就会调用reject
setTimeout(function () {
resolve(res); //100ms后,代码正常返回数据!
},100)
});
});
源码和Demo(点个赞再走咯)
以下效果为demo,需要自行引入jquery
test.html
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>下拉框搜索</title>
<script src="../jquery.min.js"></script>
<script src="searchserver-select.js"></script>
<link href="searchable-select.css" rel="stylesheet">
<script type="text/javascript">
//下拉搜索分页框
$(function () {
$('#b').searchServerSelect({}, function (keyWord, pageIndex = 1, pageSize = 10) {
//服务器获取数据
return getData(keyWord,pageIndex,pageSize)
});
$('#b1').searchServerSelect({}, function (keyWord, pageIndex = 1, pageSize = 10) {
let res = {}
//测试数据
let dataList = []
for (let i = 0; i < 10; i++) {
dataList.push({value: keyWord + i, label: keyWord + i})
}
res.pageIndex = pageIndex
res.pageSize = pageSize
res.itemCount = 105
res.data = dataList
return new Promise(function(resolve, reject){
//当异步代码执行成功时,我们会调用resolve, 当异步代码失败时就会调用reject
setTimeout(function () {
resolve(res); //代码正常执行!
},100)
});
});
});
//TODO 自定义下拉框数据
function getData(keyWord,pageIndex = 1,pageSize = 10) {
console.log("以下为测试数据获取方式,请自定义数据")
let promise1 = $.ajax({
url: "/dict/baseShipPort",
data: {name:keyWord,current:pageIndex,pageSize:pageSize},
type: "post",
timeout:5000, //设置超时的时间
})
return new Promise(function (resolve, reject) {
promise1.then(function (data) {
if (data.success) {
let res = {}
let dataList = []
let pager = data.data
let records = pager.data
for (let i = 0; i < records.length; i++) {
dataList.push({value:records[i].portCode,label:records[i].displayName})
}
res.pageIndex = pager.pageIndex
res.pageSize = pager.pageSize
res.itemCount = pager.itemCount
res.data = dataList
resolve(res)
}else{
reject(data)
}
},function (data) {
console.log("以上为测试数据获取方式,请自定义数据")
reject(data)
})
})
}
</script>
</head>
<body>
<div style="display: flex">
<div style="width: 240px;margin-left: 20px">
下拉搜索分页 自定义服务器数据
<input id="b">
</div>
<div style="width: 240px;margin-left: 20px">
下拉搜索分页
<input id="b1">
<button type="button" onclick="console.log('值为:'+$('#b1').val())" >获取值</button>
</div>
</div>
</body>
</html>
searchable-select.css
css
/* select */
.fr{
float: right;
}
.fl{
float: left;
}
.searchable-select-hide {
display: none;
}
.searchable-select {
display: inline-block;
min-width: 100%;
font-size: 14px;
line-height: 1.428571429;
color: #555;
vertical-align: middle;
position: relative;
outline: none;
z-index: 9
}
.searchable-select-holder{
padding: 0 10px;
background-color: #fff;
background-image: none;
border: 1px solid #d9d9d9;
min-height: 32px;
line-height: 31px;
box-sizing: border-box;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075);
-webkit-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
}
.searchable-select-caret {
position: absolute;
width: 0;
height: 0;
box-sizing: border-box;
border-color: #a0a0a0 transparent transparent transparent;
top: 5px;
bottom: 0;
border-style: solid;
border-width: 5px;
margin: auto;
right: 5px;
}
.searchable-select-clear {
position: absolute;
box-sizing: border-box;
top: 5px;
bottom: 0;
margin: auto;
right: 18px;
cursor: pointer;
font-size: 15px;
}
.searchable-select-dropdown {
position: absolute;
background-color: #fff;
border: 1px solid #ccc;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 4px;
border-top: none;
top: 28px;
left: 0;
right: 0;
}
.searchable-select-input {
margin-top: 5px;
border: 1px solid #ccc;
outline: none;
padding: 4px;
width: 100%;
box-sizing: border-box;
}
.queryBtn{
border: none;
width: 19%;
margin-left: 1%;
background-color: #3f86d8;
color: white;
cursor: pointer;
padding: 4px 0;
}
.searchable-scroll {
margin-top: 4px;
position: relative;
}
.searchable-scroll.has-privious {
padding-top: 16px;
}
.searchable-scroll.has-next {
padding-bottom: 16px;
}
.searchable-has-privious {
top: 0;
}
.searchable-has-next {
bottom: 0;
}
.searchable-has-privious, .searchable-has-next {
height: 16px;
left: 0;
right: 0;
position: absolute;
text-align: center;
z-index: 10;
background-color: white;
line-height: 8px;
cursor: pointer;
}
.searchable-select-items {
max-height: 400px;
overflow-y: scroll;
position: relative;
}
.searchable-select-items::-webkit-scrollbar {
display: none;
}
.searchable-select-item {
padding: 5px 5px;
cursor: pointer;
min-height: 30px;
box-sizing: border-box;
transition: all 1s ease 0s;
}
.searchable-select-item.selected{
background: #3f86d8!important;
color: white;
}
.searchable-select-item.hover {
background: #9abde5;
color: white;
}
/* select */
/*2023 0912*/
/*新增分页功能*/
.searchable-pager{
position: relative;
width: 100%;
height: 20px;
}
.searchable-pager-item{
background-color: rgba(248, 248, 248, 0.9);
border-radius: 2px;
color: black;
padding: 3px 4px;
line-height: 20px;
border: none;
outline: none;
cursor: pointer;
margin-right: 3px;
display: inline-block;
}
.searchable-pager-item:hover{
background-color: #d2cfcf
}
.searchable-pager-item-active{
background-color: rgb(0,115,220);
color: white;
}
.searchable-loading{
position: absolute;
top: -24px;
right: 0;
z-index: 999;
background-color: #3f86d8;
color: white;
}
searchserver-select.js
js
/**
* 下拉搜索框+分页功能,从服务器获取数据
* @Date 2023-09-12
* @author www
*/
(function($){
$.expr[":"].searchableSelectContains = $.expr.createPseudo(function(arg) {
return function( elem ) {
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
$.searchServerSelect = 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();
});
//TODO 输入框变化事件
this.input.on('keydown', function(event){
event.stopPropagation();
if(event.which === 13){ //enter
//按下回车,如果没有选中的项,则查询,否则选中然后关闭
if(!_this.hasCurrentHoverItem()){
//查询
_this.filter();
} else {
//选中
_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(_this.inputOldValue === _this.input.val()){ //防止按下其他键带来的影响
return;
}
//输入内容变化,那么再次回车应该触发搜索而不是选中
if(event.which != 13 && event.which != 27 && event.which != 38 && event.which != 40){
_this.cancelHover()
}
})
}
var $sS = $.searchServerSelect;
$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" style="width: 80%" />');
this.queryBtn = $('<input type="button" class="queryBtn" value="查询" />');
this.inputOldValue = '';//控制查询频率
this.items = $('<div class="searchable-select-items"></div>');
this.caret = $('<span class="searchable-select-caret"></span>');
this.clear = $('<span class="searchable-select-clear">×</span>');
this.scrollPart = $('<div class="searchable-scroll"></div>');
this.pager = $('<div class="searchable-pager"></div>');
//当前高亮
this.currentHoverItem = false;
//当前选中
this.currentSelectedItem = false;
this.queryBtn.click(function () {
_this.filter(true)
})
this.clear.click(function (event) {
event.stopPropagation();
_this.cancelSelected()
})
this.dropdown.append(this.input);
this.dropdown.append(this.queryBtn);//查询按钮
this.dropdown.append(this.scrollPart);
this.scrollPart.append(this.items);
this.scrollPart.append(this.pager);
this.searchableElement.append(this.caret);
this.searchableElement.append(this.clear);
this.searchableElement.append(this.holder);
this.searchableElement.append(this.dropdown);
this.element.after(this.searchableElement);
this.buildItems();
},
//TODO 触发搜索
filter: function(btnClick=false){
let _this = this;
if(!btnClick && _this.inputOldValue === _this.input.val()){ //防止按下其他键带来的影响
return;
}
_this.inputOldValue = _this.input.val()
_this.buildItems();
},
//TODO 初始化添加项
buildItems: function(pageIndex = 1,pageSize = 10){
var _this = this;
let keyword = _this.input.val();
let promise = _this.getSelectData(keyword,pageIndex,pageSize)
let loading = $('<span class="searchable-loading">loading...</span>')
_this.pager.append(loading);
promise.then(function (data) {//回调成功
//清空原有内容
$(_this.items).html('');
_this.buildPager(data)
let dataList = data.data
_this.currentHoverItem = false;
if(dataList && dataList.length>0){
for (let i = 0; i < dataList.length; i++) {
var item = $('<div class="searchable-select-item" data-value="'+dataList[i].value+'">'+dataList[i].label+'</div>');
//如果选中的和当前的相等,则高亮
if(_this.currentSelectedItem && $(_this.currentSelectedItem).html() === dataList[i].label){
_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);
}
}
},function (err) {
console.log("服务器获取数据失败! ",err)
$(loading).remove()
//失败提示
loading = $('<span class="searchable-loading" style="background-color: #e28a8a">加载失败,请稍后</span>')
_this.pager.append(loading);
setTimeout(function () {
$(loading).remove()
},3000)
})
},
//渲染分页内容
buildPager:function(pager){
let _this = this;
let thisPager = _this.pager;
//清空内容
$(thisPager).html('')
$(thisPager).append(`<span class="searchable-pager-item fl">共${pager.itemCount}条</span>`)
if(pager.itemCount>0){
let pageCount = Math.ceil(pager.itemCount / pager.pageSize)
//渲染5个页码,最好是单数
let showPageBtnCount = 5;
let min = pager.pageIndex
let max = pager.pageIndex
while (max - min + 1 < showPageBtnCount){
if(min>1){
min -- ;
}if(max<pageCount){
max++;
}
if(min===1 && max===pageCount){
break;
}
}
let items = $('<div class="fr"></div>')
for (let i = min; i <= max; i++) {
let item
if(pager.pageIndex===i){
item = $(`<a class="searchable-pager-item searchable-pager-item-active">${i}</a>`);
}else{
item = $(`<a class="searchable-pager-item">${i}</a>`);
item.click(function(event){
//取消原来的点击事件,防止弹框消失
event.stopPropagation();
_this.buildItems(i,pager.pageSize)
});
}
$(items).append(item)
}
$(thisPager).append(items)
}
},
//TODO 获取数据,服务器
getSelectData:function(keywords,pageIndex = 1,pageSize = 10){
console.log(`getSelectData(${keywords},${pageIndex},${pageSize}) `)
if(!this.options.getDataFunction){
alert("未设置数据获取逻辑:getDataFunction")
}
return this.options.getDataFunction(keywords,pageIndex,pageSize);
},
show: function(){
this.dropdown.removeClass('searchable-select-hide');
this.input.focus();
this.status = 'show';
this.dropdown.css('z-index', 100); //打开下拉列表时调高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层级
},
//高亮第一个
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);
}
},
hasCurrentSelectedItem: function(){
return this.currentSelectedItem && this.currentSelectedItem.length > 0;
},
selectItem: function(item){
console.log("selectItem:",$(item)[0].innerHTML)
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');
},
//取消hover
cancelHover:function(){
if(this.hasCurrentHoverItem()){
this.currentHoverItem.removeClass('hover');
this.currentHoverItem = false;
}
},
//取消选中
cancelSelected:function () {
if(this.hasCurrentSelectedItem()){
this.currentSelectedItem.removeClass('selected');
this.element.val('')
this.currentSelectedItem = false
this.holder.text('')
this.holder.data('value', '');
}
}
});
$.fn.searchServerSelect = function(options,getDataFunction){
options.getDataFunction = getDataFunction;
this.each(function(){
var sS = new $sS($(this), options);
});
return this;
};
})(jQuery);
点个赞再走咯