原生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.5中使用deferred对象的代码(翻译)
Mar 10 Javascript
js类型转换与引用类型详解(Boolean_Number_String)
Mar 07 Javascript
javascript 原型链维护和继承详解
Nov 26 Javascript
JavaScript获取表格(table)当前行的值、删除行、增加行
Jul 03 Javascript
BootStrap和jQuery相结合实现可编辑表格
Apr 21 Javascript
DataTables+BootStrap组合使用Ajax来获取数据并且动态加载dom的方法(排序,过滤,分页等)
Nov 09 Javascript
如何使用vuejs实现更好的Form validation?
Apr 07 Javascript
Bootstrap进度条与AJAX后端数据传递结合使用实例详解
Apr 23 Javascript
基于AngularJS实现的工资计算器实例
Jun 16 Javascript
ES6 javascript中class类的get与set用法实例分析
Oct 30 Javascript
vue.js 实现a标签href里添加参数
Nov 12 Javascript
jquery实现简单拖拽效果
Jul 20 jQuery
原生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
PHPMailer使用教程(PHPMailer发送邮件实例分析)
2012/12/06 PHP
php利用腾讯ip分享计划获取地理位置示例分享
2014/01/20 PHP
PHP调用C#开发的dll类库方法
2014/07/28 PHP
基于php判断客户端类型
2016/10/14 PHP
php 7新特性之类型申明详解
2017/06/06 PHP
Laravel 5.5 的自定义验证对象/类示例代码详解
2017/08/29 PHP
HR vs ForZe BO3 第一场 2.13
2021/03/10 DOTA
ext form 表单提交数据的方法小结
2008/08/08 Javascript
javascript 解决表单仍然提交即使监听处理函数返回false
2010/03/14 Javascript
JavaScript中的getTimezoneOffset()方法使用详解
2015/06/10 Javascript
Express的路由详解
2015/12/10 Javascript
基于jQuery实现鼠标点击导航菜单水波动画效果附源码下载
2016/01/06 Javascript
JavaScript的Ext JS框架中的GridPanel组件使用指南
2016/05/21 Javascript
JS中的作用域链
2017/03/01 Javascript
Angular2下使用pdf插件的方法详解
2017/04/29 Javascript
BootStrap表单控件之文本域textarea
2017/05/23 Javascript
基于JavaScript实现无限加载瀑布流
2017/07/21 Javascript
详解Angular4 路由设置相关
2017/08/26 Javascript
ReactNative 之FlatList使用及踩坑封装总结
2017/11/29 Javascript
微信小程序使用audio组件播放音乐功能示例【附源码下载】
2017/12/08 Javascript
vue项目中使用Svg的方法
2018/10/24 Javascript
node app 打包工具pkg的具体使用
2019/01/17 Javascript
elementUI select组件默认选中效果实现的方法
2019/03/25 Javascript
js实现跟随鼠标移动的小球
2019/08/26 Javascript
javascript贪吃蛇游戏设计与实现
2020/09/17 Javascript
django模型中的字段和model名显示为中文小技巧分享
2014/11/18 Python
python3实现的zip格式压缩文件夹操作示例
2019/08/17 Python
在Pytorch中使用样本权重(sample_weight)的正确方法
2019/08/17 Python
pandas按行按列遍历Dataframe的几种方式
2019/10/23 Python
python实现while循环打印星星的四种形状
2019/11/23 Python
加拿大女包品牌:Matt & Nat
2017/05/12 全球购物
美国购买肉、鸭、家禽、鹅肝和熟食网站:D’Artagnan
2018/11/13 全球购物
食堂个人先进事迹
2014/01/22 职场文书
六一儿童节主持词
2014/03/21 职场文书
电大毕业个人生自我鉴定
2014/03/26 职场文书
员工安全承诺书
2014/05/22 职场文书