乐趣区

美化select下拉框

在写示例的时候,用到了下拉框,但是原生的下拉框是在是有点难看,然后模仿着写了点,一个是直接在写好的 Dom 上进行美化,一个是用 js 生成,然后定义类名及相应的事件来处理
1. 效果图

2. 直接是在 Dom 上美化
html 文件
<div class=”root”>
<div id=”selectedItem”>
<div id=”promptText”><span id=”spanText”> 请选择你喜欢的文字 </span><img src=”../images/arrowup.png” id=”arrows” /></div>
<ul class=”choiceDescription”>
<li class=”item”> 万水千山,陪你一起看 </li>
<li class=”item”> 万水千山,陪你一起看 1 </li>
<li class=”item”> 万水千山,陪你一起看 2 </li>
<li class=”item”> 万水千山,陪你一起看 3 </li>
<li class=”item”> 万水千山,陪你一起看 4 </li>
</ul>
</div>
</div>
css 文件
ul{
margin: 0;
padding: 0;
list-style: none;
}
/* 下拉框包含层 */
#selectedItem{
width: 240px;
cursor: pointer;
}
/* 已选中的选项 */
#promptText{
position: relative;
padding-left: 10px;
width: 230px;
height: 30px;
line-height: 30px;
border: 1px solid #d3d3d3;
border-radius: 4px;
background: #fff;
color: #999;
font-size: 14px;
}
/* 图标 */
#arrows{
position: absolute;
top: 0;
right: 0;
width: 30px;
height: 30px;
vertical-align: middle;
}
#arrows:focus{
outline: none;
}
/* 下拉可选项包含层 */
.choiceDescription{
position: absolute;
display: none;
/*overflow: hidden;*/
margin-top: 2px;
width: 240px;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0 1px 6px rgba(0, 0, 0, .1);
background: #fff;
}
.show{
display: block;
}
/* 下拉可选项 */
.item{
height: 30px;
line-height: 30px;
padding-left: 10px;
font-size: 15px;
color: #666;
}
.item:hover{
color: #fff;
background: rgba(49, 255, 195, 0.67);
}
js 文件
(function() {
let choiceDescription = document.getElementsByClassName(‘choiceDescription’)[0];
let arrows = document.getElementById(‘arrows’);
/* 用于判断是否是下拉 */
let isDown = false;

let selectedItem = document.getElementById(‘selectedItem’);
/* 对点击下拉进行监听 */
selectedItem.addEventListener(‘click’, function() {
isDown = !isDown;
if(isDown) {
/* 如果是下拉状态,则显示下拉的选项,并把图标显示为向下的图标 */
choiceDescription.className += ‘ show’;
arrows.src = ‘../images/arrowdown.png’;
} else {
choiceDescription.className = ‘choiceDescription’;
arrows.src = ‘../images/arrowup.png’;
}
});

choiceDescription.addEventListener(‘click’, function(e) {
let promptText = document.getElementById(‘spanText’);
let selectElement = e.target;

/* 判断是否点击的是 li 标签,防止点击了 li 标签以外的空白位置 */
while(selectElement.tagName !== ‘LI’) {

/* 如果点中的是当前容器层 */
if(selectElement == choiceDescription) {
selectElement = null;
break;
}

/* 若果不是,则再找父级容器 */
selectElement = selectElement.parentNode;
}

/* innerText、innerHTML、value
* innerText 是指 html 标签里的文字信息,单纯的文本,不会有 html 标签,存在兼容性
* innerHTML 是指包含在 html 标签里的所有子元素,包括空格、html 标签
* value 表单里的元素属性值
* */
if(selectElement) {
promptText.innerHTML = e.target.innerHTML;
}
});
})()
在已有的 Dom 节点上对 Dom 绑定事件,我这里的宽度是固定死的,相对来说不是很友好
3.js 自动生成进行美化
html 文件
<div id=”select” class=”select”></div>

<script src=”autoGenerateSelect.js”></script>
<script>
(function() {

/* 当 onload 事件触发时,页面上所有的 DOM,样式表,脚本,图片,flash 都已经加载完成了
* 当 DOMContentLoaded 事件触发时,仅当 DOM 加载完成,不包括样式表,图片,flash
*/
document.addEventListener(‘DOMContentLoaded’,function(){
new $Selector({
elementSelector:’#select’,
options:[
{name:’ 选项 1 ′,value:’0′},
{name:’ 选项 2 ′,value:’1′},
{name:’ 选项 3 ′,value:’2′}
],
defaultText:’ 选项 2 ′
});
})
})()
</script>
*css 文件
html, body, ul{
margin: 0;
padding: 0;
}
ul{
list-style: none;
}
#select{
padding: 30px 40px 0;
}
/* 下拉框 */
.dropDown{
position: relative;
display: inline-block;
min-width: 120px;
box-sizing: border-box;
color: #515a6e;
font-size: 14px;
}
/* 已选中的值包含层 */
.selectedOption{
position: relative;
box-sizing: border-box;
outline: none;
user-select: none;
cursor: pointer;
background: #fff;
border-radius: 4px;
border: 1px solid #dcdee2;
transition: all .2s ease-in-out;
}
.selectedValue{
display: block;
overflow: hidden;
height: 28px;
line-height: 28px;
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
padding-left: 8px;
padding-right: 24px;
}
/* 图标 */
.arrowDown{
position: absolute;
display: inline-block;
top: 50%;
right: 8px;
margin-top: -7px;
font-size: 14px;
color: #808695;
transition: all .2s ease-in-out;
/* 字体抗锯齿渲染 */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.arrowDown:before{
content: “”;
display: block;
width: 6px;
height: 6px;
background-color: transparent;
border-left: 2px solid #808695;
border-bottom: 2px solid #808695;
transform: rotate(-45deg);
}

/* 所有选项的包含层 */
.optionsContainer{
position: absolute;
top: 30px;
left: 0;
min-width: 120px;
max-height: 200px;
margin: 5px 0;
padding: 5px 0;
background: #fff;
box-sizing: border-box;
border-radius: 4px;
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
z-index: 2;
transform-origin: center top 0px;
transition: all 0.3s;
will-change: top, left;

transform: scale(1, 0);
opacity: 0;
}

/* 每个选项 */
.optionsItem{
line-height: normal;
padding: 7px 16px;
color: #515a6e;
font-size: 12px;
white-space: nowrap;
cursor: pointer;
transition: background .2s ease-in-out;
}
.itemSelected, .optionsItem:hover{
color: #2d8cf0;
background-color: #f3f3f3;
}
对下拉框初始化
/* 私有方法:初始化下拉框 */
_initSelector({
/* 传入 id,class,tag,用于挂载下拉框 */
elementSelector = ”,

/* 传入的下拉框选项 */
options = [{
name: ‘ 请选择你喜欢的颜色 ’,
value: ‘0’
}],
defaultText = ‘ 请选择你喜欢的颜色 ’
}) {
/* 找到要挂载的 Dom 节点 */
this.parentElement = document.querySelector(elementSelector) || document.body;
this.options = options;
this.defaultText = defaultText;

/* 下拉框的显示与隐藏状态 */
this.downStatus = false;
/* 下拉框默认选中的值 */
this.defaultValue = ”;
this._createElement();
},

创建元素节点
/* 创建 Dom 节点 */
_createElement() {
/* 创建下拉框最外层 */
let dropDown = document.createElement(‘div’);
dropDown.className = ‘dropDown’;

/* 已选中的选项值 */
let selectedOption = document.createElement(‘div’);
selectedOption.className = ‘selectedOption’;

/* 选中的值 */
let selectedValue = document.createElement(‘span’);
selectedValue.className = ‘selectedValue’;
/* 先赋值为默认值 */
selectedValue.innerText = this.defaultText;

/* 向下的图标 */
let downIcon = document.createElement(‘i’);
downIcon.className = ‘arrowDown’;

/* 将已选中的值的层添加到 Dom 节点中 */
selectedOption.appendChild(selectedValue);
selectedOption.appendChild(downIcon);

/* 创建选项的外层容器 */
let optionsContainer = document.createElement(‘div’);
optionsContainer.className = ‘optionsContainer’;

/* 用 ul 来包含选项层 */
let ulOptionsList = document.createElement(‘ul’);
ulOptionsList.className = ‘ulOptionsList’;

/* 循环创建每个选项 */
this.options.forEach((item) => {
let optionsItem = document.createElement(‘li’);

/* 是否是选中状态 */
if(item.name == this.defaultText) {
optionsItem.className = ‘optionsItem itemSelected’;
} else {
optionsItem.className = ‘optionsItem’;
}
optionsItem.innerText = item.name;
ulOptionsList.appendChild(optionsItem);
});

/* 添加到每个对应的元素里面 */
optionsContainer.appendChild(ulOptionsList);
dropDown.appendChild(selectedOption);
dropDown.appendChild(optionsContainer);
this.parentElement.appendChild(dropDown);

/* 设置 Dom 元素,挂载、绑定事件 */
/* 已选中的选项的包含层 */
this.selectedOption = selectedOption;
/* 选中的值 */
this.selectedValue = selectedValue;
/* 下拉框选项包含层 */
this.optionsContainer = optionsContainer;
this._handleShowOptions(this.parentElement);

this._unifyWidth(selectedOption);
},
显示与隐藏相关事件
/* 显示与隐藏事件 */
_handleShowOptions(element) {
element.addEventListener(‘click’, (e) => {
let clickNode = e.target;

this._unifyWidth(this.selectedOption);

/* 点击的是否是下拉框 */
if(this._isOptionNode(clickNode, this.selectedOption)) {
if(this.downStatus) {
this._hiddenDropDown();
} else {
this._showDropDown();
}
} else if(clickNode.className == ‘optionsItem’) {
this._handleSelected(clickNode);
} else {
this._hiddenDropDown();
}
})
},
/* 判断是否是下拉框选项 */
_isOptionNode(clickNode, target) {
if (!clickNode || clickNode === document) return false;
return clickNode === target ? true : this._isOptionNode(clickNode.parentNode, target);
},
/* 显示下拉框选项 */
_showDropDown() {
this.optionsContainer.style.transform = ‘scale(1, 1)’;
this.optionsContainer.style.opacity = ‘1’;
this.selectedOption.className = ‘selectedOption’;
this.downStatus = true;
},
/* 隐藏下拉框选项 */
_hiddenDropDown() {
this.optionsContainer.style.transform = ‘scale(1, 0)’;
this.optionsContainer.style.opacity = ‘0’;
this.selectedOption.className = ‘selectedOption’;
this.downStatus = false;
},
定义点击事件
/* 对每个选项的点击事件 */
_handleSelected(clickNode) {
this.selectedValue.innerText = clickNode.innerText;
clickNode.className = ‘optionsItem itemSelected’;
this._siblingsDom(clickNode, function(clickNode) {
if(clickNode) {
clickNode.className = ‘optionsItem’;
}
});
this._hiddenDropDown();
},

/* 兄弟节点处理函数 */
_siblingsDom(clickNode, callback) {

/* arguments 是一个对应于传递给函数的参数的类数组对象
* arguments 对象是所有(非箭头)函数中都可用的局部变量
* 包含传递给函数的每个参数,第一个参数在索引 0 处
* arguments 对象不是一个 Array, 它类似于 Array,
* 但除了 length 属性和索引元素之外没有任何 Array 属性
* */

(function (ele) {
/* arguments.callee
* 指向当前执行的函数
* */
callback(ele);
if (ele && ele.previousSibling) {
arguments.callee(ele.previousSibling);
}
})(clickNode.previousSibling);

(function (ele) {
callback(ele);
if (ele && ele.nextSibling) {
arguments.callee(ele.nextSibling);
}
})(clickNode.nextSibling);
},
判断宽度
/* 判断宽度 */
_unifyWidth(selectedOption) {
/* 找到所有的 li 标签 */
let optionsItem = document.querySelectorAll(‘.optionsItem’);
let standardWidth = selectedOption.offsetWidth;

/* 对每个 li 标签设置宽度 */
optionsItem.forEach((item) => {
standardWidth = item.offsetWidth > standardWidth ? item.offsetWidth : standardWidth;
item.style.width = standardWidth – 32 + ‘px’;
selectedOption.style.width = standardWidth + ‘px’;
});
}
(function() {

/* 定义 selector 下拉框 */
let Selector = function(params) {
/* 初始化 */
this._initSelector(params);
};

Selector.prototype = {
/* 将上面的方法全部放在 Selector 原型上 */
};

/* 挂载到 window 上 */
window.$Selector = Selector;
})();
关于原型与原型链,可以查看我记录的 js 面试

使用 js 生成的话,相对来说,代码长点,我这里的话,对宽度有做判断,但是不完美
关于 DOMContentLoaded 可以查看这篇文章 DOMContentLoaded

正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯 ^_^)

往期好文推荐:

判断 iOS 和 Android 及 PC 端
纯 css 实现瀑布流(multi-column 多列及 flex 布局)
实现单行及多行文字超出后加省略号
微信小程序之购物车和父子组件传值及 calc 的注意事项

退出移动版