es6 实现原生select组件,只用于学习,有哪些不足请指出

select 组件效果

 

 

 

 

 

 这里不废话了直接上代码

HTML结构和select组件样式

<!DOCTYPE html>
<html lang="en">
<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">
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .sel-plugin{
            position: relative;
            display: inline-block;
            max-width: 280px;
            height: 42px;
            font-size: 14px;
            border: 1px solid rgb(221, 221, 221);
            vertical-align: middle;
        }
        .sel-plugin-focus{
            border: solid 1px #3c95b7;
        }
        .sel-plugin__content{
            height: 100%;
            padding-left: 10px;
            padding-right:35px;
            background: rgb(255, 255, 255);
            border-radius: 2px;
            
            font-family: MicrosoftYaHei;
            color: rgb(51, 51, 51);
            cursor: pointer;
            -webkit-user-select:none;
            -moz-user-select:none;
            -ms-user-select:none;
            user-select:none;
            overflow: hidden;
            text-overflow:ellipsis;
            white-space: nowrap;
        }
        .sel-plugin__arrow{
            position: absolute;
            width: 20px;
            height: 20px;
            top:50%;
            right: 10px;
            transform: scale(0.8) translateY(-50%);
            cursor: pointer;
        }
        .sel-plugin__arrow::before{
            content: "";
            display: block;
            border-top:10px solid #2d0909;
            border-right:10px solid transparent;
            border-bottom:10px solid transparent;
            border-left:10px solid transparent;
            margin-top:2px;
        }
        .sel-plugin__items{
            display: none;
            border: 1px solid #eee;
            position: absolute;
            width: 99%;
            max-height: 200px;
            overflow-y: auto;
            top: 102%;
        }
        .sel-plugin__items-show{
            display: block;
        }
        .sel-plugin__ul{
            list-style: none;
        }
        .sel-plugin__ul li{
            height: 30px;
            line-height: 30px;
            padding-left: 10px;
            cursor: pointer;
            overflow: hidden;
            text-overflow:ellipsis;
            white-space: nowrap;
        }
        .sel-plugin__li-sel,.sel-plugin__ul li:hover{
            background-color:#f5f7fa;
        }
        
    </style>
    <title>Document</title>
</head>
<body>
    球队:<div class="js-sel-plugin"></div>
    <script type="module"> 
        import SelPlugin from './src/js/SelPlugin.js';
        // 使用
        let sel = new SelPlugin({
            el:".js-sel-plugin",
            // val:2,
            data:[//下拉框数据,默认数据,可覆盖
            {val:1, txt:'维拉人朴茨茅斯'},
            {val:2, txt:'农夫农场主阿斯顿维拉'},
            {val:3, txt:'军港西布罗姆维奇'},
            {val:4, txt:'萝卜裤'},
            {val:5, txt:'拖拉机手诺维'},
            {val:6, txt:'蓝军桑德兰'}
          ]
          
        });
      
    </script>
</body>
</html>

javascript代码

class SelPlugin {

  constructor(option) {

    this.opt = Object.assign({
      el: null,//挂载的元素,必须的,类型 string 选择器字符串
      height: 30,//下拉框高度
      onoff: false,//列表展示开关
      focus_onoff: false,//下拉框是否获取焦点
      val: 0,//设置默认选中的值,必须data包含的值
      data: [//下拉框数据,默认数据,可覆盖
        { val: 1, txt: '请选择' }
      ],
      changeCall: function (obj) {//数据当发生改变时回调

      },
      clickCall:function(obj){//点击选项卡回调

      }
    }, option);
    this.init();

  }
  //初始化
  init() {

    if (!this.opt.el)
      throw 'selPlugin el cannot be empty';

    if (!this.opt.data || this.opt.data.length == 0) {
      this.opt.data = [
        { val: 1, txt: '请选择' }
      ];
    }

    this.el = document.querySelector(this.opt.el);
    this.cur_val_txt = this.opt.val ? this.opt.data.find(item => item.val == this.opt.val) : this.opt.data[0];
    if (!this.cur_val_txt) {
      this.cur_val_txt = this.opt.data[0];
    }
    this.stop_ele = null;//下拉列表停留选项元素,默认为null
    this.render();
    this.addEvent();

  }
  //添加下拉框行为事件
  addEvent() {

    let div_items = this.el.querySelector(".sel-plugin__items");
    this.el.addEventListener("click", this.onOff.bind(this));
    div_items.addEventListener("mousemove", this.mmove.bind(this));
    div_items.addEventListener("mouseleave", this.mleave.bind(this));
    document.addEventListener("click", this.watchDocClick.bind(this));
    document.addEventListener('keydown', this.keydown.bind(this));

  }
  //获取焦点时能通过键盘上下左右箭头进行选择下拉文本
  keydown(e) {

    if (!this.opt.focus_onoff)
      return;

    let code = e.keyCode;
    let index = this.getIndex();
    let length = this.opt.data.length - 1;
    switch (code) {
      case 37:
        if (index > 0) {
          this.updateVal(--index);
        }
        break;
      case 38:
        if (index > 0) {
          this.updateVal(--index);
        }
        break;
      case 39:
        if (index < length) {
          this.updateVal(++index);
        }
        break;
      case 40:
        if (index < length) {
          this.updateVal(++index);
        }
        break;
      case 13:
        this.selHide();
        break;
    }

  }
  //通过下标更改值
  updateVal(index) {
    this.cur_val_txt = this.opt.data[index];
    let content = this.el.querySelector('.sel-plugin__content');
    content.innerHTML = this.cur_val_txt.txt;
    let lis = this.el.querySelectorAll("li");
    [...lis].forEach((item, i) => {
      item.classList.remove("sel-plugin__li-sel")
      if (index == i) {
        item.classList.add('sel-plugin__li-sel');
      }
    });
    this.opt.changeCall && this.opt.changeCall(this.cur_val_txt);

  }
  //移出下拉框行为事件
  removeEvent() {

    let div_items = this.el.querySelector(".sel-plugin__items");
    this.el.removeEventListener("click", this.onOff.bind(this));
    div_items.removeEventListener("mousemove", this.mmove.bind(this));
    div_items.removeEventListener("mouseleave", this.mleave.bind(this));
    document.removeEventListener("click", this.watchDocClick.bind(this));
    document.removeEventListener('keydown', this.keydown.bind(this));

  }
  //监听document点击,如果当前点击的目标元素不是下拉框则然下拉框失去焦点
  watchDocClick({ target }) {

    if (!this.isContain(target)) {
      this.selHide();
      this.blur();
    }

  }
  //获取下拉框焦点
  focus() {

    this.opt.focus_onoff = true;
    this.el.classList.add("sel-plugin-focus");

  }
  //下拉框失去焦点
  blur() {

    this.opt.focus_onoff = false;
    this.el.classList.remove("sel-plugin-focus");

  }
  //查找父辈或祖辈元素类名包含sel-plugin 如果有返回true
  isContain(tag) {

    while (tag && tag.classList.length && !tag.classList.contains("sel-plugin")) {
      tag = tag.parentNode;
    }
    return tag && tag.classList.length ? tag.classList.contains("sel-plugin") : false;

  }
  //下拉列表显示或隐藏
  onOff(e) {
    this.opt.clickCall && this.opt.clickCall();
    this.focus();
    this.opt.onoff = !this.opt.onoff;
    let div_items = this.el.querySelector(".sel-plugin__items");

    if (this.opt.onoff) {
      div_items.classList.add("sel-plugin__items-show");
    } else {
      div_items.classList.remove("sel-plugin__items-show");
    }
    if (e.target.tagName.toLowerCase() == "li") {
      let val = e.target.dataset.value;
      this.cur_val_txt = this.opt.data.find(item => item.val == val);
      this.el.querySelector(".sel-plugin__content").innerHTML = this.cur_val_txt.txt;
      this.opt.changeCall && this.opt.changeCall(this.cur_val_txt);
    }

    window.event ? window.event.cancelBubble = true : e.stopPropagation();


  }
  //下拉隐藏
  selHide() {

    let div_items = this.el.querySelector(".sel-plugin__items");
    this.opt.onoff = false;
    div_items.classList.remove("sel-plugin__items-show");

  }

  mmove({ target }) {

    let lis = this.el.querySelectorAll("li");
    [...lis].forEach(item => item.classList.remove("sel-plugin__li-sel"));
    if (target.tagName.toLowerCase() == "li") {
      this.stop_ele = target;
    }

  }

  mleave(e) {

    this.stop_ele && this.stop_ele.classList.add("sel-plugin__li-sel")

  }
  //渲染下拉框
  render() {

    let unit = "px";
    this.el.classList.add('sel-plugin');
    this.el.style.width = this.opt.width + unit;
    this.el.style.height = this.opt.height + unit;
    this.opt.focus_onoff ? this.el.classList.add("sel-plugin-focus") : '';

    let div_content = document.createElement('div');
    div_content.className = 'sel-plugin__content';

    div_content.innerHTML = this.cur_val_txt.txt;
    div_content.style.lineHeight = this.opt.height + unit;
    this.el.appendChild(div_content);

    let div_arrow = document.createElement('div');
    div_arrow.className = 'sel-plugin__arrow';
    div_arrow.innerHTML = "<div></div>";
    this.el.appendChild(div_arrow);

    let div_items = document.createElement('div');
    div_items.className = 'sel-plugin__items';
    div_items.style.width = this.opt.width + unit;
    this.opt.onoff ? div_items.classList.add("sel-plugin__items-show") : '';
    this.el.appendChild(div_items);

    let ul = document.createElement('ul');
    ul.classList.add('sel-plugin__ul');
    div_items.appendChild(ul);

    let classstr = "";
    let html = this.opt.data.map(item => {
      classstr = item.val == this.cur_val_txt.val ? "class='sel-plugin__li-sel'" : '';
      return `<li ${classstr} data-value='${item.val}'>${item.txt}</li>`;
    }).join('');
    ul.innerHTML = html;

  }
  //获取当前选项索引
  getIndex() {

    let lis = this.el.querySelectorAll("li");
    let index = 0;
    [...lis].forEach((item, i) => {
      if (item.classList.contains("sel-plugin__li-sel")) {
        index = i;
      }
    });
    return index;

  }
  //获取值
  getVal() {

    return this.cur_val_txt;

  }

  updateData(data, cur_item) {

    this.opt.data = data.length>0 ? data : [{ val: 1, txt: '请选择' }];
    this.opt.cur_val_txt = cur_item ? cur_item : this.opt.data[0];
    let div_content = this.el.querySelector(".sel-plugin__content");
    let ul = this.el.querySelector(".sel-plugin__ul");

    div_content.innerHTML = this.opt.cur_val_txt.txt;
    ul.innerHTML = "";
    let classstr = "";
    let html = this.opt.data.map(item => {
      classstr = item.val == this.cur_val_txt.val ? "class='sel-plugin__li-sel'" : '';
      return `<li ${classstr} data-value='${item.val}'>${item.txt}</li>`;
    }).join('');
    ul.innerHTML = html;

  }


}
export default SelPlugin;

代码复制到自己电脑,浏览页面必须安装本地服务器运行代码,否则或报错,因为我用的是es6 模块加载,必须是http协议的方式加载

 

posted @ 2019-11-15 10:36  a魏国  阅读(661)  评论(0编辑  收藏  举报