一、自定义原因
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代码
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(
`
`
);
} else {
// 添加使能的input
$select.prepend(
`
`
);
}
} 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。就目前而言是这样,将来某天标签发生变化了另说。