jQuery自定义下拉框

一、自定义原因

1.项目定制化需求,下拉框的样式包括滚动条都需要同现有客户端(QT qml开发)版本保持一致;
2.据有web前端经验开发人员告知,select滚动条样式不可以改变。后来google了之后发现是可以的,采用jquery-nicescroll.js插件,但我并没有尝试,原因有以下两点;
3.嵌入Qt的界面,下拉框滚动条只能鼠标滚动,单击滚动条不能滚动;
4.嵌入Qt的界面,在mainwindow出现滚动条的时候,打开下拉框的选择面板的时候,下拉框选择面板会错位;

二、解决方案

简而言之:采用input ul li标签实现自定义
如下图:


示例图

2.1)遇到的问题

No. 问题 解决方案 示例
1 监听事件对于动态加载的元素没有起效,即使使用jquery的on监听也无效 监听静态元素,通过on或者off的第二个选择器参数,指定到具体监听的元素 $('body').off('blur' , '.select-box .input-box input.ipt-select')
2 同一元素多次添加监听事件,执行的时候,事件中的方法会执行多次 监听事件前,先解绑该事件 $('body').off('blur' , '.select-box .input-box input.ipt-select').on('blur', '.select-box .input-box input.ipt-select', function () { $(this).parent().siblings('ul').addClass('hide'); })
3 下拉箭头click没有响应下拉框展示隐藏事件 因为下拉箭头同input响应click的事件相同,因此将箭头的css鼠标事件透传 .select-box .input-box span{pointer-events: none;}

2.2)具体代码

2.2.1)css代码
/* css变量定义 */
:root{
    --g-Width-input: 229px; /*输入框、搜索框宽度*/
    --g-height-input: 22px; /*输入框、搜索框高度*/
    --g-color-bg-input_nor: #2d2e30; /*控件公用颜色--输入框、搜索框、背景--nor状态--背景色*/
    --g-color-bg-input_hov: #232426; 
    --g-color-border_dow: 1px solid #1690f5; /*控件公用颜色--边框--dow状态--颜色*/
    --g-radius-input: 2px; /*输入框、搜索框圆角半径*/
    --g-color-border_nor: 1px solid #424447;  /*控件公用颜色--边框--nor状态--颜色*/

    --g-color-border_dow: 1px solid #1690f5; 
    --g-color-bg_hov:#1690f5;/*控件公用颜色--控件背景--hor状态--底色*/
     --g-color-b-text:#b5b5b5; 
    --g-color-w-border_modal:#fff;   
}
input[type="number"],
input[type="text"],
input[type="password"],
textarea.form-control {
    font-size: 12px;
    background-color: var(--g-color-bg-input_nor);
    color: var(--g-color-b-text);
    width: var(--g-Width-input);
    height: var(--g-height-input);
    line-height: 28px;
    padding: 0px 6px;
    border: var(--g-color-border_nor);
    /* border-radius: 0px; */
    border-radius: var(--g-radius-input);
}
input[type="number"]:hover,
input[type="text"]:hover,
input[type="text"]:hover +.search-icon,
textarea.form-control:hover{
    background-color: var(--g-color-bg-input_hov);
}
input[type="number"]:focus,
input[type="text"]:focus{
    background-color: var(--g-color-bg-input_hov);
    border: var(--g-color-border_dow);
}
input:focus {
    outline: none;
}
input[type="number"][disabled],
input[type="text"][disabled],
.form-control[disabled],
input[type="text"].disabled {
    background-color: transparent;
    color: var(--g-color-b-text);
    border: var( --g-color-border_nor);
    cursor: default;
}
input:-webkit-autofill {
    -webkit-box-shadow: 0 0 0px 1000px var(--g-color-bg-input_nor) inset !important;
    -webkit-text-fill-color: var(--g-color-b-text);
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none !important;
    margin: 0;
}

/* select框自定义样式 */
.select-box {
    position: relative;
    width: var(--g-Width-input);
    display: inline-block;
}
.select-box .input-box{
    position: relative;
    width: 100%;
}
.select-box .input-box .icon-box{
    position: relative;
    right: 18px;
}
.select-box .input-box input.ipt-select{
    width: 100%;
}
.select-box .input-box input.ipt-select:focus{
    background-color: var(--g-color-bg-input_hov);
    border: var(--g-color-border_dow);
}
.select-box .input-box span{
    pointer-events: none;
}
.select-box .input-box .icon-box{
    position: absolute;
    right: 5px;
    top: 1px;
}
.select-box .input-box .icon-box i{
    font-size: 14px;
    font-weight: 1000;
    transform: scale(0.65);
    -webkit-transform: scale(0.75);
    display: inline-block;
}
.select-box ul.select{
    position: absolute;
    background-color: var(--g-color-bg-input_hov);
    border: var(--g-color-border_dow);
    height: auto;
    width: 100%;
    max-height: 274px;
    min-width: 152px;
    top: 22px;
    left: 0px;
    z-index: 99;
}
.select-box ul.select > li{
    font-size: 12px;
    height: 22px;
    line-height: 22px;
    padding: 0px 6px 1px;
    text-align: left;
}
.select-box ul.select > li.selected,
.select-box ul.select > li[selected]{
    background-color: var(--g-color-bg_hov);
    color: var(--g-color-w-border_modal);
}
.select-box ul.select > li:hover{
    background-color: var(--g-color-bg_hov);
    color: var(--g-color-w-border_modal);
}
 .vertical-auto.select::-webkit-scrollbar { /*select滚动条对应的宽度 */
    width: 5px;
}
2.2.2)html代码
<!--使能的下拉框-->
<div class="select-box">
    <ul class="select hide vertical-auto">
        <li value="0">苹果</li>
        <li value="1">香蕉</li>
        <li value="2">橘子</li>
        <li value="3">梨</li>
    </ul>
</div>
<!--不使能的下拉框-->
<div class="select-box" disabled>
    <ul class="select hide vertical-auto">
        <li value="0">苹果</li>
        <li value="1">香蕉</li>
        <li value="2">橘子</li>
        <li value="3">梨</li>
    </ul>
</div>
2.2.2)js代码
/**
 * @description: 下拉框赋默认值,document ready的时候,需要调用该方法初始化
 * @param {*} dom $(document.documentElement)
 */
function initOptionSelect($dom){
    // 添加input框
    let selects = $dom.find('.select-box');
    for(let i = 0; i < selects.length; i++) {
        let $select = $(selects[i]);
        if($select.children('.input-box').length === 0) {
            // 添加不使能的input
            if($select.attr('disabled')) {
                $select.prepend(
                    `<div class="input-box">
                        <input type="text" class="ipt-select" readonly="readonly" disabled>
                        <span class="icon-box"><i class="icon iconfont icon-arrow-down"></i></span>
                    </div>`
                );
            } else {
                // 添加使能的input
                $select.prepend(
                    `<div class="input-box">
                        <input type="text" class="ipt-select" readonly="readonly">
                        <span class="icon-box"><i class="icon iconfont icon-arrow-down"></i></span>
                    </div>`
                );
            }
        } else {
            // select已经有input了,不需要再添加
        }
    }
    boundEventFun();
    // 默认选中第一条记录
    let uls = $dom.find('.select-box ul.select');
    for(let i = 0; i < uls.length; i++) {
        selectLiChange($(uls[i]).find('li:first'));
    }
}

/**
 * @description: 绑定自定义下拉框事件
 * 1)想要监听动态生成元素的事件,可以绑定静态元素,再在on或者off中添加需要监听元素的选择器即可
 * 2)如果初始化多次走到boundEventFun事件,会导致该元素监听多次,
 *    即监听事件中的方法会走多次,防止这种事情发生,应该在绑定监听事件前,先解绑一下。
 */
function boundEventFun(){
    // input框失焦,需要隐藏下拉框
    $('body').off('blur' , '.select-box .input-box input.ipt-select')
             .on('blur', '.select-box .input-box input.ipt-select', function () {
        $(this).parent().siblings('ul').addClass('hide');
    })
    
    // 下拉框中的input框点击的时候,需要控制下拉菜单的显隐
    $('body').off('click', '.select-box .input-box input.ipt-select')
             .on('click', '.select-box .input-box input.ipt-select', function () {
        if ($(this).parent().siblings('ul').hasClass('hide')) {
            $(this).parent().siblings('ul').removeClass('hide');
        } else {
            $(this).parent().siblings('ul').addClass('hide');
        }
    });
    
    // 触发change事件
    $('body').off('change', '.select-box .input-box input.ipt-select')
             .on('change', '.select-box .input-box input.ipt-select', function () {
        $(this).parent().parent().trigger('change');
    });
    
    // 监听鼠标滑动在下拉菜单的事件,清除选中样式以及属性
    $('body').off('mousemove', '.select-box ul.select')
             .on('mousemove', '.select-box ul.select', function () {
        $(this).children().removeClass('selected');
        $(this).children().removeAttr('selected');
    });
    
    // 监听鼠标选中某个li的事件
    $('body').off('mousedown', '.select-box ul.select > li')
             .on('mousedown', '.select-box ul.select > li', function () {
        selectLiChange($(this), true);
    })
}

/**
 * @description: 下拉框赋指定值
 * @param {*} dom 下拉框DOM元素 jquery对象
 * @param {*} value 默认值
 */
function setSelDefValue($dom, value) {
    if (value !== null && value !== '' && value !== undefined) {
        // 选中下拉框指定的一项
        selectLiChange($dom.find(`li[value=${value}]`), true);
    } else {
        // 默认选中下拉框第一项
        selectLiChange($dom.find("li:first"));
    }
}

/**
 * @description: li select状态发生变化,需要改变input值
 * @param {*} $this li
 *            needTriggerChange: 是否需要触发change信号
 */
function selectLiChange($this, needTriggerChange = false) {
    // 下拉框赋值
    let $select = $this.parent().parent('.select-box');
    $select.attr('value', $this.attr('value'));
    $select.val($this.attr('value'));
    // 设置下拉框的selectedIndex
    let lis = $select.find('li');
    let selectedIndex = 0;
    for(let i = 0; i < lis.length; i++) {
        let li = lis[i];
        if($(li).attr('value') === $this.attr('value')) {
            selectedIndex = i;
            break;
        }
    }
    $select.prop('selectedIndex', selectedIndex);
    // option添加select类
    $this.siblings().removeClass('selected');
    $this.siblings().removeAttr('selected');
    $this.addClass('selected');
    $this.attr('selected', 'selected');
    // input框赋值
    let $input = $this.parent('.select').siblings('.input-box').find('input');
    if($input.val() + '' !== $this.attr('value') + '') {
        if(needTriggerChange) {
            $select.trigger('change');
        }
    }
    $input.val($this.text());
    // 隐藏下拉框
    $this.parent('.select').addClass("hide");
}

三、教训总结

1.因为此次项目中【一、自定义原因】中的问题,都是在完全开发完成之后才发现,无论如何想到共通化的修改,带来的改动量以及风险都是很大的。
2.因此,如果界面定制化极强的开发,下拉框慎重考虑,采用自定义的不要采用html自带标签select。就目前而言是这样,将来某天标签发生变化了另说。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343