资讯专栏INFORMATION COLUMN

美化select下拉框

CompileYouth / 1819人阅读

摘要:在写示例的时候,用到了下拉框,但是原生的下拉框是在是有点难看,然后模仿着写了点,一个是直接在写好的上进行美化,一个是用生成,然后定义类名及相应的事件来处理效果图直接是在上美化文件请选择你喜欢的文字万水千山,陪你一起看万水千山,陪你一起看万水

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

html文件

请选择你喜欢的文字
  • 万水千山,陪你一起看
  • 万水千山,陪你一起看1
  • 万水千山,陪你一起看2
  • 万水千山,陪你一起看3
  • 万水千山,陪你一起看4

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文件

*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的注意事项

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99883.html

相关文章

  • 前端小白也能快速学会的博客园博客美化全攻略

    摘要:前端小白也能快速学会的博客园博客美化全攻略呦,博客园的自我修养是什么第一条,别只顾收藏和偷师呀,记得点推荐或关注本人喔美化方法论简介一般而言,需要选一个默认的,然后在该基础上调整。或者也可进博客园园子页面,发状态博客园团队,申请开通权限。 前端小白也能快速学会的博客园博客美化全攻略 A呦V,博客园er的自我修养是什么?第一条,别只顾收藏和偷师呀,记得点推荐或关注本人喔~ 美化方法论简...

    StonePanda 评论0 收藏0
  • 写网页时拿起就用的小技巧

    摘要:允许以特定的方式去定义匹配某个区域的特定元素。在规定一个框的宽高之外给这个框加内边距和边框。和默认值在规定的一个框的宽高之内给这个框加内边距和边框。 1. box-sizing:允许以特定的方式去定义匹配某个区域的特定元素。 content-box:在规定一个框的宽高之外给这个框加内边距和边框。 border-box:(textarea和select默认值)在规定的一个框的宽高之内给这...

    econi 评论0 收藏0
  • 写网页时拿起就用的小技巧

    摘要:允许以特定的方式去定义匹配某个区域的特定元素。在规定一个框的宽高之外给这个框加内边距和边框。和默认值在规定的一个框的宽高之内给这个框加内边距和边框。 1. box-sizing:允许以特定的方式去定义匹配某个区域的特定元素。 content-box:在规定一个框的宽高之外给这个框加内边距和边框。 border-box:(textarea和select默认值)在规定的一个框的宽高之内给这...

    rubyshen 评论0 收藏0
  • 写网页时拿起就用的小技巧

    摘要:允许以特定的方式去定义匹配某个区域的特定元素。在规定一个框的宽高之外给这个框加内边距和边框。和默认值在规定的一个框的宽高之内给这个框加内边距和边框。 1. box-sizing:允许以特定的方式去定义匹配某个区域的特定元素。 content-box:在规定一个框的宽高之外给这个框加内边距和边框。 border-box:(textarea和select默认值)在规定的一个框的宽高之内给这...

    QiShare 评论0 收藏0

发表评论

0条评论

CompileYouth

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<