原生JS实现自定义下拉单选选择框功能


Posted in Javascript onOctober 12, 2018

浏览器自带的原生下拉框不太美观,而且各个浏览器表现也不一致,UI一般给的下拉框也是和原生的下拉框差别比较大的,这就需要自己写一个基本功能的下拉菜单/下拉选择框了。最近,把项目中用到的下拉框组件重新封装了一下,以构造函数的方式进行封装,主要方法和事件定义在原型上,下面是主要的实现代码并添加了比较详细的注释,分享出来供大家参考。代码用了ES6部分写法如需兼容低版本浏览器请把相关代码转成es5写法,或者直接bable转下。

先放个预览图吧,后面有最终的动态效果图:(样式和交互参考了阿里和Iview UI库)

原生JS实现自定义下拉单选选择框功能

下面是主要的HTML代码(包含部分js调用代码):

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Diy Select</title>
  <link rel="stylesheet" href="index.css" rel="external nofollow" >
</head>
<body>
  <div id="main" class="main"></div>

  <script src="index.js"></script>
  <script>
    document.addEventListener("DOMContentLoaded",function(){
      const select1 = new $Selector({
        eleSelector:"#main",
        options:[
          {name:"选项1",value:"0"},
          {name:"选项2",value:"1"},
          {name:"选项3",value:"2"}
        ],
        defaultText:"选项2"
      });
    })
  </script>
</body>
</html>

页面中定义了id为main的div,即为选择框所要添加到的元素。传入参数即可。

接着就是样式CSS部分了,没啥说的了,手动滑稽:

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

.main {
  padding: 40px;
}

.my-select {
  display: inline-block;
  width: auto;
  min-width: 80px;
  box-sizing: border-box;
  vertical-align: middle;
  color: #515a6e;
  font-size: 14px;
  line-height: normal;
  position: relative;
}

.select-selection {
  display: block;
  box-sizing: border-box;
  outline: 0;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  cursor: pointer;
  position: relative;
  background-color: #fff;
  border-radius: 4px;
  border: 1px solid #dcdee2;
  transition: all .2s ease-in-out;
}

.select-selection:hover,
.select-selection.select-focus {
  border-color: #57a3f3;
  box-shadow: 0 0 0 2px rgba(45, 140, 240, .2);
}

.select-selected-value {
  display: block;
  height: 28px;
  line-height: 28px;
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-left: 8px;
  padding-right: 24px;
}

.icon-select-arrow {
  position: absolute;
  top: 50%;
  right: 8px;
  line-height: 1;
  margin-top: -7px;
  font-size: 14px;
  color: #808695;
  transition: all .2s ease-in-out;
  display: inline-block;
  font-style: normal;
  font-weight: 400;
  font-variant: normal;
  text-transform: none;
  text-rendering: auto;
  line-height: 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  vertical-align: middle;
}

.icon-select-arrow::before {
  content: "";
  display: block;
  width: 6px;
  height: 6px;
  background-color: transparent;
  border-left: 1.5px solid #808695;
  border-bottom: 1.5px solid #808695;
  transform: rotate(-45deg);
}

.select-dropdown {
  width: auto;
  min-width: 80px;
  max-height: 200px;
  overflow: auto;
  margin: 5px 0;
  padding: 5px 0;
  background-color: #fff;
  box-sizing: border-box;
  border-radius: 4px;
  box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
  position: absolute;
  z-index: 2;
  transform-origin: center top 0px;
  transition: all 0.3s;
  will-change: top, left;
  top: 30px;
  left: 0;
  transform: scale(1, 0);
  opacity: 0;
}

.select-item {
  line-height: normal;
  padding: 7px 16px;
  clear: both;
  color: #515a6e;
  font-size: 12px !important;
  white-space: nowrap;
  list-style: none;
  cursor: pointer;
  transition: background .2s ease-in-out;
}

.select-item.select-item-selected,
.select-item:hover {
  color: #2d8cf0;
  background-color: #f3f3f3;
}

样式部分就不做什么解释了,下面放入压轴的JS,同样也无需过多解释,注释写的很详细了,上代码:

/* jshint esversion: 6 */
(function (window, document) {
  let Selector = function (option) {
    //执行初始化方法,
    this._init(option);
  };

  Selector.prototype = {
    //初始化传入参数并定义初始化的相关变量
    _init({
      eleSelector = "", //传入的选择器 id,class,tag等,用于将选择框渲染到此选择器所在的元素
      options = [{
        name: "请选择",
        value: "0",
      }], //传入的下拉框对象,name为选择的文字,value为值
      defaultText = "请选择" //提供的默认选择的值
    }) {
      
      //将传入的数据绑定到this上
      this.parentEle = document.querySelector(eleSelector) || document.body; //要邦定的dom 
      this.options = options; //选择值数组对象
      this.defaultText = defaultText; //默认值

      this.dropboxShow = false; //定义存储下拉框的显示隐藏状态
      this.defaultValue = ""; //定义村赤默认选中的值
      this._creatElement(); //初始化后执行创建元素方法
    },

    //创建下拉选择框dom
    _creatElement() {
      //选择框最外层的包裹元素
      let wrapEle = document.createElement("div");
      wrapEle.className = "my-select";

      //根据传入的值获取选择框默认的值和内容
      this.options.forEach(item => {
        if (item.name === "this.defaultText") {
          this.defaultValue = item.value;
        }
      });

      let selectWarpBox = document.createElement("div"); //选择框包裹元素
      selectWarpBox.className = "select-selection";

      let inputHideBox = document.createElement("input"); //隐藏保存选择值得元素
      inputHideBox.type = "hidden";
      inputHideBox.value = this.defaultValue;

      let selectShowBox = document.createElement("div"); //选择框默认展示框
      let selectNameBox = document.createElement("span"); //选择框展现的值ele
      selectNameBox.className = "select-selected-value";
      selectNameBox.id = "select-option";
      selectNameBox.innerText = this.defaultText; //将传入的默认值赋值
      let selectIcon = document.createElement("i"); //图标ele
      selectIcon.className = "arrow-down icon-select-arrow";
      //将span和角标添加到外层div
      selectShowBox.appendChild(selectNameBox);
      selectShowBox.appendChild(selectIcon);

      selectWarpBox.appendChild(inputHideBox);
      selectWarpBox.appendChild(selectShowBox);

      //下拉框
      let dropbox = document.createElement("div"),
        ulbox = document.createElement("ul");

      dropbox.id = "select-drop";
      dropbox.className = "select-dropdown";
      ulbox.className = "select-dropdown-list";
      //遍历传入的选项数组对象,生成下拉菜单的li元素并赋值
      this.options.forEach((item) => {
        let itemLi = document.createElement("li");
        if (this.defaultText === item.name) {
          itemLi.className = "select-item select-item-selected";
        } else {
          itemLi.className = "select-item";
        }

        itemLi.setAttribute("data-value", item.value);
        itemLi.innerText = item.name;
        ulbox.appendChild(itemLi);

      });
      //将下拉框ul推入到包裹元素
      dropbox.appendChild(ulbox);

      wrapEle.appendChild(selectWarpBox);
      wrapEle.appendChild(dropbox);

      this.parentEle.appendChild(wrapEle); //将生成的下拉框添加到所选元素中

      //把需要操作的dom挂载到当前实例
      //this.wrapEle = wrapEle;   //最外层包裹元素
      this.eleSelect = selectWarpBox; //选择框
      this.eleDrop = dropbox; //下拉框
      this.eleSpan = selectNameBox; //显示文字的span节点

      //绑定事件处理函数
      this._bind(this.parentEle);
    },

    //点击下拉框事件处理函数
    _selectHandleClick() {
      if (this.dropboxShow) {
        this._selectDropup();
      } else {
        this._selectDropdown();
      }
    },

    //收起下拉选项
    _selectDropup() {
      this.eleDrop.style.transform = "scale(1,0)";
      this.eleDrop.style.opacity = "0";
      this.eleSelect.className = "select-selection";
      this.dropboxShow = false;
    },

    //展示下拉选项
    _selectDropdown() {
      this.eleDrop.style.transform = "scale(1,1)";
      this.eleDrop.style.opacity = "1";
      this.eleSelect.className = "select-selection select-focus";
      this.dropboxShow = true;
    },

    //点击下拉选项进行赋值
    _dropItemClick(ele) {
      this.defaultValue = ele.getAttribute("data-value");
      //document.querySelector("#select-value").value = ele.getAttribute("data-value");
      this.eleSpan.innerText = ele.innerText;
      ele.className = "select-item select-item-selected";
      //对点击选中的其他所有兄弟元素修改class去除选中样式
      this._siblingsDo(ele, function (ele) {
        if (ele) {
          ele.className = "select-item";
        }
      });
      this._selectDropup();
    },

    //node遍历是否是子元素包裹元素
    _getTargetNode(ele, target) {
      //ele是内部元素,target是你想找到的包裹元素
      if (!ele || ele === document) return false;
      return ele === target ? true : this._getTargetNode(ele.parentNode, target);
    },

    //兄弟元素遍历处理函数
    _siblingsDo(ele, fn) {

      (function (ele) {
        fn(ele);
        if (ele && ele.previousSibling) {
          arguments.callee(ele.previousSibling);
        }
      })(ele.previousSibling);

      (function (ele) {
        fn(ele);
        if (ele && ele.nextSibling) {
          arguments.callee(ele.nextSibling);
        }
      })(ele.nextSibling);

    },

    //绑定下拉框事件处理函数
    _bind(parentEle) {
      let _this = this;
      //事件委托到最外层包裹元素进行绑定处理
      parentEle.addEventListener("click", function (e) {
        const ele = e.target;
        
        //遍历当前点击的元素,如果是选中框内的元素执行
        if (_this._getTargetNode(ele, _this.eleSelect)) {
          if (_this.dropboxShow) {
            _this._selectDropup();
          } else {
            _this._selectDropdown();
          }
        } else if (ele.className === "select-item") { //如果是点击的下拉框的选项执行
          _this._dropItemClick(ele);
        } else { //点击其他地方隐藏下拉框
          _this._selectDropup();
        }

      });

    }

  };
  //将构造函数挂载到全局window
  window.$Selector = Selector;
})(window, document);

到此,一个自定义下拉菜单就出炉了,下面是动态效果:

原生JS实现自定义下拉单选选择框功能

至此,从css自定义的表单元素到下拉框元素都已经自定义完毕,使用bootstrap的同学把这些加进去就能基本保持各浏览器效果一致性和美观性了,就到这吧先。

推荐:

感兴趣的朋友可以关注小编的微信公众号【码农那点事儿】,更多网页制作特效源码及学习干货哦!!!

总结

以上所述是小编给大家介绍的原生js实现一个自定义下拉单选选择框功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jQuery 1.8 Release版本发布了
Aug 14 Javascript
js实现两点之间画线的方法
May 12 Javascript
js+HTML5实现canvas多种颜色渐变效果的方法
Jun 05 Javascript
jQuery 更改checkbox的状态,无效的解决方法
Jul 22 Javascript
jQuery实现页面点击后退弹出提示框的方法
Aug 24 Javascript
Node.js的特点详解
Feb 03 Javascript
如何在 Vue.js 中使用第三方js库
Apr 25 Javascript
Node.Js生成比特币地址代码解析
Apr 21 Javascript
es6 symbol的实现方法示例
Apr 02 Javascript
详解vuex持久化插件解决浏览器刷新数据消失问题
Apr 15 Javascript
深入理解Antd-Select组件的用法
Feb 25 Javascript
原生js实现碰撞检测
Mar 12 Javascript
原生JS实现轮播图效果
Oct 12 #Javascript
详解KOA2如何手写中间件(装饰器模式)
Oct 11 #Javascript
Element Input组件分析小结
Oct 11 #Javascript
element el-input directive数字进行控制
Oct 11 #Javascript
详解angular2.x创建项目入门指令
Oct 11 #Javascript
详解vscode中vue代码颜色插件
Oct 11 #Javascript
微信小程序之裁剪图片成圆形的实现代码
Oct 11 #Javascript
You might like
php array_walk() 数组函数
2011/07/12 PHP
PHP生成等比缩略图类和自定义函数分享
2014/06/25 PHP
Composer设置忽略版本匹配的方法
2016/04/27 PHP
php简单实现批量上传图片的方法
2016/05/09 PHP
thinkPHP2.1自定义标签库的导入方法详解
2016/07/20 PHP
php生出随机字符串
2017/07/06 PHP
PHP设计模式之组合模式定义与应用示例
2020/02/01 PHP
JavaScript之HTMLCollection接口代码
2011/04/27 Javascript
移动节点的jquery代码
2014/01/13 Javascript
jquery获取元素索引值index()示例
2014/02/13 Javascript
如何正确使用Nodejs 的 c++ module 链接到 OpenSSL
2014/08/03 NodeJs
浅谈JS验证表单文本域输入空格的问题
2017/02/14 Javascript
微信小程序 wx:for的使用实例详解
2017/04/27 Javascript
js轮播图的插件化封装详解
2017/07/17 Javascript
详解vue 模版组件的三种用法
2017/07/21 Javascript
Bootstrap栅格系统的使用详解
2017/10/30 Javascript
JavaScript实现简单贪吃蛇效果
2020/03/09 Javascript
echarts实现获取datazoom的起始值(包括x轴和y轴)
2020/07/20 Javascript
Vue获取微博授权URL代码实例
2020/11/04 Javascript
Python中利用sqrt()方法进行平方根计算的教程
2015/05/15 Python
详解用TensorFlow实现逻辑回归算法
2018/05/02 Python
tensorflow实现简单的卷积网络
2018/05/24 Python
基于Python实现定时自动给微信好友发送天气预报
2018/10/25 Python
Python从文件中读取指定的行以及在文件指定位置写入
2019/09/06 Python
Python3 使用selenium插件爬取苏宁商家联系电话
2019/12/23 Python
使用 Python 在京东上抢口罩的思路详解
2020/02/27 Python
python实现简单的tcp 文件下载
2020/09/16 Python
MySQL面试题
2014/01/12 面试题
党的群众路线教育实践活动心得体会900字
2014/03/07 职场文书
单位委托书范本
2014/04/04 职场文书
会计岗位说明书
2014/07/29 职场文书
优秀大学生申请书
2019/06/24 职场文书
JavaScript 防篡改对象的用法示例
2021/04/24 Javascript
tensorflow+k-means聚类简单实现猫狗图像分类的方法
2021/04/28 Python
html5中sharedWorker实现多页面通信的示例代码
2021/05/07 Javascript
springboot 全局异常处理和统一响应对象的处理方式
2022/06/28 Java/Android