html给下拉框添加搜索、分页功能(通过ajax从服务器获取搜索数据)

文章目录

下拉框搜索分页功能开发

最近需要开发一个下拉框从服务器通过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);

点个赞再走咯

相关推荐
好好学操作系统4 分钟前
autodl 保存 数据 跨区
linux·运维·服务器
KingRumn7 分钟前
Linux同步机制之信号量
linux·服务器·网络
嵌入式学习菌7 分钟前
SPIFFS文件系统
服务器·物联网
旺仔Sec7 分钟前
2026年度河北省职业院校技能竞赛“Web技术”(高职组)赛项竞赛任务
运维·服务器·前端
BullSmall28 分钟前
linux 根据端口查看进程
linux·运维·服务器
herinspace32 分钟前
管家婆软件年结存后快马商城操作注意事项
服务器·数据库·windows
Hard but lovely1 小时前
linux:----进程守护化(Daemon)&&会话的原理
linux·运维·服务器
一水鉴天1 小时前
整体设计 定稿 之24 dashboard.html 增加三层次动态记录体系仪表盘 之2 程序 (Q208 之1)
前端·html
芝麻馅汤圆儿1 小时前
sockperf 工具
linux·服务器·数据库
YJlio1 小时前
桌面工具学习笔记(11.1):BgInfo——给服务器桌面“刻”上关键信息
服务器·笔记·学习