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协议的方式加载

原文地址:https://www.cnblogs.com/supermanGuo/p/11864858.html

时间: 2024-10-08 23:34:12

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

React Native 调用原生Android组件

在如今的App中,已经有成千上万的原生UI部件了--其中的一些是平台的一部分,另一些可能来自于一些第三方库,而且可能你自己还收藏了很多.React Native已经封装了大部分最常见的组件,譬如ScrollView和TextInput,但不可能封装全部组件.而且,说不定你曾经为自己以前的App还封装过一些组件,React Native肯定没法包含它们.幸运的是,在React Naitve应用程序中封装和植入已有的组件非常简单. 比如WebView,官方并没有提供Android端的实现,那么我们现

如何实现select组件的选择输入过滤作用

实现select组件的选择输入过滤作用的js代码如下: /** *其中//******之间的部分显示的是在没有选择输入过滤功能的代码上加入的功能代码 ** / /** * @description This plugin allows you to make a select box editable like a text box while keeping it's select-option features * @description no stylesheets or images

freemarker写select组件(三)

freemarker写select组件 1.宏定义 <#macro select id datas value="" key="" text=""> <select id="${id}" name="${id}"> <option>---请选择---</option> <#list datas as data> <#if key!="

在使用element ui时 select组件获取label问题

最近在工作的时候需要实现多个条件联合查询,这个其实就是一条sql语句.但是,我们数据库中存储的是根据前端传来id的值所对应的值,而不是直接存的id,使用select组件时,却只能取到value值,无法作为后台查询的条件. 我们需要的是select 组件的label值,而不是看不见的value值. 首先,根据element ui select组件使用手册,给组件添加'change'方法 1 @change=changeValue 再根据Array 的 find方法找出对应的值即可. 1 chang

freemarker写select组件(二)

freemarker写select组件 1.宏定义 <#macro select id datas value=""> <select id="${id}" name="${id}"> <option>---请选择---</option> <#list datas as data> <#if value == data> <option value="${da

Android React Native使用原生UI组件

Android React Native 已经将几个常用的原生组件进行了封装,比如 ScrollView 和 TextInput,但是并不是所有系统的原始组件都被封装了,因此有的时候我们不得不自己动手封装一下,从而能够使用那些React Native没有为我们封装的原生组件,比如WebView,官方并没有提供Android端的实现,那么我们现在就动手封装一下WebView. 之前写过一篇文章Android React Native使用原生模块,而使用原生UI组件的方法和使用原生模块的方法十分类似

freemarker写select组件报错总结(五)

1.错误描述 六月 26, 2014 10:44:49 下午 freemarker.log.JDK14LoggerFactory$JDK14Logger error 严重: Template processing error: "The only legal comparisons are between two numbers, two strings, or two dates.\nLeft hand operand is a freemarker.template.SimpleScalar

freemarker写select组件(四)

freemarker写select组件 1.宏定义 <#macro select id datas value="" key="" text="" headKey="" headValue=""> <select id="${id}" name="${id}"> <option>---请选择---</option> &l

freemarker写select组件报错总结(七)

1.错误描述 六月 26, 2014 11:26:27 下午 freemarker.log.JDK14LoggerFactory$JDK14Logger error 严重: Template processing error: "Expected collection or sequence. datas evaluated instead to freemarker.core.HashLiteral$SequenceHash on line 7, column 18 in inc/select