(function () {
  const selectListDemo = [
    {name: 'test1', value: 1},
    {name: 'test2', value: 2},
    {name: 'test3', value: 3}
  ]

  class MidociSelect extends HTMLElement {
    static get observedAttributes() {
      return ['acitve-title', 'active-sub-title']
    }

    constructor() {
      super()
      this.attachShadow({mode: 'open'})
      this.shadowRoot.innerHTML = `
        <style>
          :host{
            --themeColor:rgb(24,144,255);
            box-sizing: border-box;
            font-size: 14px;
            --borderColor:#eee;
          }

          .wrapper{
            position: relative;
            display: inline-flex;
            align-items: center;
            padding-left: 10px;
            width: 95px;
            height: 36px;
            border: 1px solid var(--borderColor);
            color: #333;
            border-radius: 2px;
            user-select: none;
            transition: .3s cubic-bezier(.12, .4, .29, 1.46);
            outline:none
          }

          .wrapper:hover{
            border: 1px solid var(--themeColor);
            transition: .3s cubic-bezier(.12, .4, .29, 1.46);
          }

          .title{

          }

          .arrow-out{
            position: absolute;
            right: 12px;
            top: 50%;
            transform: translateY(0px) rotateX(0deg);
            transition: .3s cubic-bezier(.12, .4, .29, 1.46);
          }

          .wrapper.flip>.arrow-out{
            transform: translateY(-3px) rotateX(180deg);
            transition: .3s cubic-bezier(.12, .4, .29, 1.46);
          }

          .arrow{
            display: flex;
            width: 6px;
            height:6px;
            border: none;
            border-left: 1px solid #333;
            border-bottom: 1px solid #333;
            transform: translateY(-50%) rotateZ(-45deg);
            transition: .3s cubic-bezier(.12, .4, .29, 1.46);
          }

          .wrapper:hover .arrow{
            border-left: 1px solid var(--themeColor);
            border-bottom: 1px solid var(--themeColor);
            transition: .3s cubic-bezier(.12, .4, .29, 1.46);
          }



          .list{
            z-index: 100;
            position: absolute;
            top: 130%;
            left: 0;
            background-color: #fff;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            visibility: hidden;
            min-width: 100%;
            border-radius: 3px;
            transform: scale(0);
            transform-origin: top;
            transition: .3s cubic-bezier(.12, .4, .29, 1.46);
          }

          .wrapper.flip>.list{
          visibility: visible;
            transform: scale(1);
            transition: .3s cubic-bezier(.12, .4, .29, 1.46);
          }

          .item{
            display: flex;
            align-items: center;
            padding-left: 10px;
            width: 95px;
            height: 36px;
            color: #333;
            border-radius: 2px;
            user-select: none;
            background-color: #fff;
            transition: background-color .3s ease-in-out;
          }

          .item:hover{
            background-color: rgba(24,144,255,0.1);
            transition: background-color .3s ease-in-out;
          }
        </style>

        <div class="wrapper" tabindex="1">
          <span class="title">1</span>
          <span class="arrow-out">
            <span class="arrow"></span>
          </span>
          <div class="list" >
            <div class="item">1</div>
            <div class="item">2</div>
            <div class="item">3</div>
            <div class="item">4</div>
          </div>
        </div>
      `
      this._wrapperDom = null
      this._listDom = null
      this._titleDom = null
      this._list = []
      this._arrowFlip = false
      this._value = null
      this._name = null
    }

    connectedCallback() {
      this._wrapperDom = this.shadowRoot.querySelector('.wrapper')
      this._listDom = this.shadowRoot.querySelector('.list')
      this._titleDom = this.shadowRoot.querySelector('.title')
      this.initEvent()
      this.list = selectListDemo
    }

    disconnectedCallback() {
      this._wrapperDom.removeEventListener('click', this.flipArrow.bind(this))
      this._wrapperDom.removeEventListener('blur', this.blurWrapper.bind(this))

      this.shadowRoot.querySelectorAll('.item')
        .forEach((item, index) => {
          item.removeEventListener('click', this.change.bind(this, index))
        })
    }

    attributeChangedCallback(attr, oldVal, newVal) {
      // const attribute = attr.toLowerCase()
      // if (attribute === 'descriptions') {
      //   console.log(1)
      //   this.render(newVal)
      // }
    }

    set list(list) {
      if (!this.shadowRoot) return
      this._list = list
      this.render(list)
    }

    get list() {
      return this._list
    }

    set value(value) {
      this._value = value
    }

    get value() {
      return this._value
    }

    set name(name) {
      this._name = name
    }

    get name() {
      return this._name
    }

    initEvent() {
      this.initArrowEvent()
      this.blurWrapper()
    }

    initArrowEvent() {
      this._wrapperDom.addEventListener('click', this.flipArrow.bind(this))
    }

    initChangeEvent() {
      this.shadowRoot.querySelectorAll('.item')
        .forEach((item, index) => {
          item.addEventListener('click', this.change.bind(this, index))
        })
    }

    change(index) {
      this.changeTitle(this._list, index)

      let changeInfo = {
        detail: {
          value: this._value,
          name: this._name
        },
        bubbles: true
      }
      let changeEvent = new CustomEvent('change', changeInfo)
      this.dispatchEvent(changeEvent)
    }

    changeTitle(list, index) {
      this._value = list[index].value
      this._name = list[index].name
      this._titleDom.innerText = this._name
    }

    flipArrow() {
      if (!this._arrowFlip) {
        this.showList()
      } else {
        this.hideList()
      }
    }

    showList() {
      this._arrowFlip = true
      this._wrapperDom.classList = 'wrapper flip'
    }

    hideList() {
      this._arrowFlip = false
      this._wrapperDom.classList = 'wrapper'
    }

    blurWrapper() {
      this._wrapperDom.addEventListener('blur', (event) => {
        event.stopPropagation()
        this.hideList()
      })
    }

    render(list) {
      if (!list instanceof Array) return
      let listString = ''
      list.forEach((item) => {
        listString += `
          <div class="item" data-value="${item.value}">${item.name}</div>
        `
      })
      this._listDom.innerHTML = listString
      this.changeTitle(list, 0)
      this.initChangeEvent()
    }
  }

  const FrozenMidociSelect = Object.freeze(MidociSelect);
  customElements.define('midoci-select', FrozenMidociSelect);
})()

使用

<script type="module" async>
    import './MidociSelect.js'
</script>

<midoci-select></midoci-select>

<script>
    const list = [
        {name: '全平台', value: 1},
        {name: '东券', value: 2},
        {name: '京券', value: 3}
      ]

    window.onload=function(){
        document.querySelector('midoci-select').list=list

        console.log(document.querySelector('midoci-select').value)
        console.log(document.querySelector('midoci-select').name)

        document.querySelector('midoci-select').addEventListener('change', (event) => {
        console.log('选中的 value:', event.detail.value)
        console.log('选中的 name:', event.detail.name)
      })
    }
</script>
03-05 14:05