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);

点个赞再走咯

相关推荐
赵大仁14 分钟前
在 CentOS 7 上安装 Node.js 20 并升级 GCC、make 和 glibc
linux·运维·服务器·ide·ubuntu·centos·计算机基础
vvw&19 分钟前
Docker Build 命令详解:在 Ubuntu 上构建 Docker 镜像教程
linux·运维·服务器·ubuntu·docker·容器·开源
冷冰鱼42 分钟前
【问题实录】服务器ping不通win11笔记本
运维·服务器
wayuncn43 分钟前
web服务器之云主机、物理机租用、服务器托管的区别
运维·服务器
冷曦_sole1 小时前
linux-21 目录管理(一)mkdir命令,创建空目录
linux·运维·服务器
聚名网1 小时前
服务器如何划分空间?
运维·服务器
学前端的小朱1 小时前
处理字体图标、js、html及其他资源
开发语言·javascript·webpack·html·打包工具
world=hello1 小时前
关于科研中使用linux服务器的集锦
linux·服务器
枫欢1 小时前
将现有环境192.168.1.100中的svn迁移至新服务器192.168.1.4;
服务器·python·svn
华纳云IDC服务商1 小时前
如何自动解决服务器弹性伸缩问题?
运维·服务器