【web前端】可筛选[输入搜索]的select

Why 要弄这玩意?

前几天,产品同学一直嚷着,下拉框太长,他们眼睛不好●﹏●,好难选到需要的数据,严厉并且坚决要求下拉框需要附带搜索功能。


某开发:忍忍呗,下拉框本来就不带搜索的咧
产品同学:那个xxx平台不就有带搜索的下拉么!!
某开发:那是之前咱们使用了yyy框架啊,这次的项目没用那玩意,而且【巴拉巴拉一大堆…
产品同学●﹏●???假装懂了
产品同学:就加一个搜索功能,不是很简单么?
某开发(+﹏+)~狂晕ing。。。


某开发同学,已经举手投降了,那,就动手干了呗。

动手前准备

1、网络资料,有现成的 js 插件否?

丫的,还真有,像 select2.js[压缩后,大约70k]、Chosen[压缩后28k]。
至于还有没有其它的,就没仔细找了。不过能确定一个事,网上现成的,绝对是不能忍受的庞大[个人的感受]。

2、select是否可输入?

答案是否。
纵观下载的几个代码,都是通过input模拟输入,而展开的内容下拉框,也不是select元素,
而是通过其它标签模拟的弹出层。因为 select 无法通过代码触发展开[么?试了挺多方法,好像也不成,求大神指点]

3、如何监听 input 的值更变了?

input元素有 oninput 和 onpropertychange 事件,分别对应 chrome/firefox 和 ie 的内容更变的事件。

Happy Coding

项目中,用到select的地方非常多,所以,编写的插件,绝对不能有太多的配置,目标1:零配置
不想影响到 select 的取值、样式等,希望插件编写完后,不用更改相关的方法,目标2: 无缝嵌入

项目中,也没用到 option 分组、多选等功能,这里,也不做相关的考虑,这里,直接贴上代码,基于jquery:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>网上找了个插件,但是不好用</title>
    <script src="jquery.js" charset="utf-8"></script>
    <style>
        body,html{padding:0;margin:0;width:100%;height:100%;}
    </style>
</head>
<body>
    <div style="height:500px;margin:20px;">
        <!-- option必须带有 value 的值 -->
        <select id="magicsuggest" data-edit-select="1">
            <option value="1">哇哈哈</option>
            <option value="2">天天</option>
            <option value="3">xx</option>
            <option value="1">哇哈哈哇哈哈哇哈哈哇哈哈哇哈哈</option>
            <option value="2">天天</option>
            <option value="3">xx</option>
            <option value="1">哇哈哈</option>
            <option value="2">天天</option>
            <option value="3">xx</option>
            <option value="1">哇哈哈</option>
            <option value="2">天天</option>
            <option value="3">xx</option>
        </select>
    </div>
    <div style="height:1000px;"></div>
</body>

<!-- 下面这段代码,就是可编辑select,嗯,那个丑陋的样式,被我也写在里面了 -->
<script type="text/javascript">
$.fn.filterSelect = (function(){
    // 我就 很 纠结的,把样式内嵌在这里了,让你怎么改!!!!
    var isInit = false;
    function initCss(){
        isInit = true;
        var style = document.createElement("style");
        var csstext = '.m-input-select{display:inline-block;*display:inline;position:relative;-webkit-user-select:none;}\
                        \n.m-input-select ul, .m-input-select li{padding:0;margin:0;}\
                        \n.m-input-select .m-input{padding-right:22px;}\
                        \n.m-input-select .m-input-ico{position:absolute;right:0;top:0;width:22px;height:100%;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAATElEQVQoU2NkIBEwkqiegTwNcXFx/4m1CW4DMZoWLVrEiOIkfJpAikGuwPADNk0wxVg1gASRNSErxqkBpgldMV4NuEKNvHggNg5A6gBo4xYmyyXcLAAAAABJRU5ErkJggg==) no-repeat 50% 50%;}\
                        \n.m-input-select .m-list-wrapper{}\
                        \n.m-input-select .m-list{display:none;position:absolute;z-index:1;top:100%;left:0;right:0;max-width:100%;max-height:250px;overflow:auto;border-bottom:1px solid #ddd;}\
                        \n.m-input-select .m-list-item{cursor:default;padding:5px;margin-top:-1px;list-style:none;background:#fff;border:1px solid #ddd;border-bottom:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\
                        \n.m-input-select .m-list-item:hover{background:#2D95FF;}\
                        \n.m-input-select .m-list-item-active{background:#2D95FF;}';
        style = $("<style>"+ csstext +"</style>")[0];
        // ie 竟然坑了...
        // if(style.styleSheet) {
        //     style.styleSheet.cssText = csstext;
        // }else{
        //     style.appendChild(document.createTextNode(csstext));
        // };

        var head = document.head || document.getElementsByTagName("head")[0];
        if(head.hasChildNodes()){
            head.insertBefore(style, head.firstChild);
        }else{
            head.appendChild(style);
        };

    };

    return function(){
        !isInit && initCss();

        var $body = $("body");
        this.each(function(i, v){
            var $sel = $(v), $div = $('<div class="m-input-select"></div>');
            var $input = $("<input type='text' class='m-input' />");
            // var $wrapper = $("<div class='m-list-wrapper'><ul class='m-list'></ul></div>");
            var $wrapper = $("<ul class='m-list'></ul>");
            $div = $sel.wrap($div).hide().addClass("m-select").parent();
            $div.append($input).append("<span class='m-input-ico'></span>").append($wrapper);

            // 遮罩层显示 + 隐藏
            var wrapper = {
                show: function(){
                    $wrapper.show();
                    this.$list = $wrapper.find(".m-list-item:visible");
                    this.setIndex(this.$list.filter(".m-list-item-active"));
                    this.setActive(this.index);
                },
                hide: function(){
                    $wrapper.hide();
                },
                next: function(){
                    return this.setActive(this.index + 1);
                },
                prev: function(){
                    return this.setActive(this.index - 1);
                },
                $list: $wrapper.find(".m-list-item"),
                index: 0,
                $cur: [],
                setActive: function(i){
                    // 找到第1个 li,并且赋值为 active
                    var $list = this.$list, size = $list.size();
                    if(size <= 0){
                        this.$cur = [];
                        return;
                    }
                    $list.filter(".m-list-item-active").removeClass("m-list-item-active");
                    if(i < 0){
                        i = 0;
                    }else if(i >= size){
                        i = size - 1;
                    }
                    this.index = i;
                    this.$cur = $list.eq(i).addClass("m-list-item-active");
                    this.fixScroll(this.$cur);
                    return this.$cur;
                },
                fixScroll: function($elem){
                    // console.log($wrapper);
                    var height = $wrapper.height(), top = $elem.position().top, eHeight = $elem.outerHeight();
                    var scroll = $wrapper.scrollTop();
                    // 因为 li 的 实际 top,应该要加上 滚上 的距离
                    top += scroll;
                    if(scroll > top){
                        $wrapper.scrollTop(top);
                    }else if(top + eHeight > scroll + height){
                        // $wrapper.scrollTop(top + height - eHeight);
                        $wrapper.scrollTop(top + eHeight - height);
                    }
                },
                setIndex: function($li){
                    if($li.size() > 0){
                        this.index = this.$list.index($li);
                        $li.addClass("m-list-item-active").siblings().removeClass("m-list-item-active");
                    }else{
                        this.index = 0;
                    }
                }
            };

            // input 的操作
            var operation = {
                // 文字更变了,更新 li, 最低效率的一种
                textChange: function(){
                    val = $.trim($input.val());
                    $wrapper.find(".m-list-item").each(function(i, v){
                        if(v.innerHTML.indexOf(val) >= 0){
                            $(v).show();
                        }else{
                            $(v).hide();
                        }
                    });
                    wrapper.show();
                },
                // 设值
                setValue: function($li){
                    if($li && $li.size() > 0){
                        var val = $.trim($li.html());
                        $input.val(val).attr("placeholder", val);
                        wrapper.setIndex($li);
                        $sel.val($li.attr("data-value")).trigger("change");
                    }else{
                        $input.val(function(i, v){
                            return $input.attr("placeholder");
                        });
                    };
                    wrapper.hide();
                    this.offBody();
                },
                onBody: function(){
                    var self = this;
                    setTimeout(function(){
                        self.offBody();
                        $body.on("click", self.bodyClick);
                    }, 10);
                },
                offBody: function(){
                    $body.off("click", this.bodyClick);
                },
                bodyClick: function(e){
                    var target = e.target;
                    if(target != $input[0] && target != $wrapper[0]){
                        wrapper.hide();
                        operation.setValue();
                        operation.offBody();
                    }
                }
            };

            // 遍历 $sel 对象
            function resetOption(){
                var html = "", val = "";
                $sel.find("option").each(function(i, v){
                    if(v.selected && !val){
                        val = v.text;
                    };
                    html += '<li class="m-list-item'+ (v.selected ? " m-list-item-active" : "") +'" data-value="'+ v.value +'">'+ v.text +'</li>';
                });
                $input.val(val);
                $wrapper.html(html);
            };
            $sel.on("optionChange", resetOption).trigger("optionChange");
            $sel.on("setEditSelectValue", function(e, val){
                // console.log(val);
                var $all = $wrapper.find(".m-list-item"), $item;
                for(var i = 0, max = $all.size(); i < max; i++){
                    $item = $all.eq(i);
                    if($item.attr("data-value") == val){
                        operation.setValue($item);
                        return;
                    }
                }
            });

            // input 聚焦
            $input.on("focus", function(){
                this.value = "";
                operation.textChange();
                operation.onBody();
            }).on("input propertychange", function(e){
                operation.textChange();
            }).on("keydown", function(e){
                // 上 38, 下 40, enter 13
                switch(e.keyCode){
                    case 38:
                        wrapper.prev();
                        break;
                    case 40:
                        wrapper.next();
                        break;
                    case 13:
                        operation.setValue(wrapper.$cur);
                        break;
                }
            });

            $div.on("click", ".m-input-ico", function(){
                // 触发 focus 和 blur 事件
                // focus 是因为 input 有绑定
                // 而 blur,实际只是失去焦点而已,真正隐藏 wrapper 的是 $body 事件
                $wrapper.is(":visible") ? $input.blur() : ($input.val("").trigger("focus"));
            });

            // 选中
            $wrapper.on("click", ".m-list-item", function(){
                operation.setValue($(this));
                return false;
            });

            setTimeout(function(){
                // for ie
                wrapper.hide();
            }, 1)


        });

        return this;
    };
})();
</script>


<!-- 这段代码,是遍历所有拥有 data-edit-select 属性的元素,并把他们变为可编辑 -->
<script>
// 使用了这个插件,select该怎么用就怎么用
// 任何选择,同样会触发 select 的 更变的说【即select的值会同步更新】
//
var $select = $("select[data-edit-select]").filterSelect();
// --> 这时候的  $select === $("#magicsuggest");
// 也可以 用 $("#magicsuggest").on("change"),两者等价
$select.on("change", function(){
    // console.log(this.value)
});
// 也可以通过 $("#magicsuggest").val() 拿到最新的值
// 通过 $("#magicsuggest").trigger("setEditSelectValue", 2); 设置选中的值为 2
// 通过 $("#magicsuggest").trigger("optionChange") 触发 更新 option 的值
</script>
</html>

一个功能简的,带过滤输入框的 select 就完成了。至于 input 的样式什么的,喜欢的再慢慢优化咧。
默认样式,仅兼容至 ie7。

什么,代码太难看了?结构太凌乱了?一股脑子写完的代码,不重用 = 不优化~。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值