sequence

In the process of page creation, we often need to use the drop-down box component, sometimes using the native select tag is inconvenient, because there is a shadow root, shadow root cause we sometimes have to change the style is very troublesome

Introduction to ShadowRoot

When using the Element UI drop down box, I encountered the phenomenon that the selected option text was too long, and the ellipsis could not be changed in time. It took another mouse click to take effect (as expected, bugs were still the theme of programmers).

There were other UI components on the web, but since I was using the Element UI in general at the time, it was hard to switch to other UI components.

There is no condition that only their own creation, their own hands rich food and clothing.

I’ve bloggable about dropdown components before: Customize a VUe-compliant dropdown component

This time with a slight modification, the drop-down box component now consists of two components

The effect of realization:





The text start

conceived

The drop-down box component is ready to be divided into two modules:

  • The first block is a content box that the user can see directly, with a triangular arrow

  • The second block is a drop-down list box triggered when the user clicks on the content box

The outer layer

First, define an external module component, wzc-select.vue

Font awesome is a free icon font library

  • Use NPM install font-awesome to download

  • If using main.js, add import ‘box-awesome/CSS/box-awesome.min.css’;

  • Use the CDN, use the link introduction: < link href = “/ / netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css” rel = “stylesheet >”

  • < I class=”imgthree fa fa-caret-up” >

External components can set width, height, and placeholder values when called

This is done by passing the value of the parent component to the child component, the numerical props.

For more information on how values are passed between components, see this article: 8 Ways Vue Components Communicate.

Props to accept width, height, and placeholder values. Use default to set the default values. If no external values are passed, use default

props: {
  placeholder: {
    type: String.default: 'Please select'
  },
  width: {
    type: Number.default: 180
  },
    height: {
      type: Number.default: 40}},Copy the code

Some properties are added using the root method. Here you can learn about: root-CSS

In vue, you can use compute to set a styleVar object

computed: {
  styleVar() {
    return {
      '--select-height': this.height + 'px'.'--select-width': this.width + 'px'}}}Copy the code

Bind styleVar to div via :style

<div class="wzc_select" :style="styleVar" >

This allows you to use the width and height directly when writing CSS in the style below

.wzc_select {
  border: 1px solid #E6E6E6;
  border-radius: 5px;
  height: var(--select-height);
  width: var(--select-width);
  line-height: var(--select-height);
}
Copy the code

The outer one is divided into two parts, and the drop-down list of Selectlist below is hidden by default.

<template>
    <div class="wzc_select" :style="styleVar" >
        <! -- Select box -->
        <div class="divSelect" :class="{ 'drop_down': isListShow }" ref="divSelect" >
            <div class="divSelectinput" @click="dropDownSelect">
                <! -- Selected content -->
                <div class="selectinfos" :title="label" :class="{'no_select': label == 'select'}"> {{ label }} </div>
                <! -- triangle icon isListShow -->
                <i class="imgthree fa fa-caret-up" :class="{ 'is-reverse': isListShow }"></i>
            </div>
        </div>
        <! -- Drop down list -->
        <transition name="drop-down" >
          	<! -- Drop down list isListShow to determine whether to fold -->
            <div class="Selectlist" v-show="isListShow" ref="dropDown">
                <div class="select_triangle"></div>
                <ul class="wzc_option_list">
                    <slot name="wzc_option"></slot>
                </ul>
            </div>
        </transition>
    </div>
</template>
Copy the code

If you want to add some action to the drop-down box, you can use the
box to stop the drop-down box.

Write the Transition animation into your CSS

.drop-down-enter {
  opacity: 0;
  transform:translate(0px, -80px) scaleY(0.2);
}
.drop-down-leave-to {
  opacity: 0;
  transform:translate(0px, -80px) scaleY(0.2);
}
.drop-down-enter-active {
	transition: all 0.5 s ease-in;
}
.drop-down-leave-active {
	transition: all 0.5 s ease;
}
Copy the code

You need to make a document click judgment when you click on the popup and when you click on the drop down, and if you click on the other part of the page, it collapses the drop down

document.addEventListener("click".function( e ){
  if(_this.$refs.divSelect) {
    if(!!!!! _this.$refs.divSelect.contains(e.target) || !! _this.$refs.dropDown.contains(e.target) )return;
    else
      _this.isListShow = false; }})Copy the code

After some thought and manipulation, the outer part is written

Let’s import wzc-select.vue into the page using import, and be sure to register it in Components

  • Import wzcSelect from ‘./wzc-select’

  • Register: Components :{wzcSelect}

At present, the current effect has been, but more basic


The inner layer

The inner code is actually much simpler, just displaying the incoming data from the outside

<template>
  <li class="wzc_option" :style="styleVar" @click="currentSelect">
    <div class="wzc_option_dropdown_item">{{ label }}</div>
  </li>
</template>
Copy the code

Receive the CSS width and height attribute in props, as well as the label content and optionID attribute

props: {
  / / wide
  width: {
    type: Number.default: -1,},/ / high
  height: {
    type: Number.default: 34,},/ / content
  label: {
    type: String,},// id
  optionid: {
    type: String,}},Copy the code

When selecting, use $parent to pass data to the outer wZc-select. vue component

currentSelect() {
      this.$parent.label = this.label;
      this.$parent.optionid = this.optionid;
      this.$parent.isListShow = !this.$parent.isListShow;
}
Copy the code

Import wzcOption from ‘./wzc-option’


The outer layer joins the inner layer

The inner layer is primarily a

  • object. When used in the outer layer, you can use slots to store the inner layer in the corresponding display location

    There are about the introduction of slot slots can refer to (of course, the specific study should be their own slowly over) :

    • Cn.vuejs.org/v2/api/#slo…

    • Cn.vuejs.org/v2/guide/co…

    When called in the parent component, you can add complete

    <wzc-select class="wzcs" :width="240" :height="40">
      <template v-slot:wzc_option>
        <wzc_option
          v-for="item in showlist"
          :key="item.item_id"
          :label="item.item_name"
          :optionid="item.item_id"
        ></wzc_option>
      </template>
    </wzc-select>
    Copy the code

    Use showlist for the test data

    showlist: [
      {
        item_name: "Option 00000000000000000000000000000".item_id: "0"}, {item_name: "Option 11111111111111111111111111111".item_id: "1"}, {item_name: "Option 222222222222222222222222222222".item_id: "2"}, {item_name: "Option 33333333333333333333333333333333".item_id: "3"],},Copy the code

    Ok, now the implementation of the drop-down box has the desired style





    Outer WZC -select.vue complete code

    <template>
        <div class="wzc_select" :style="styleVar" >
            <div class="divSelect" :class="{ 'drop_down': isListShow }" ref="divSelect" >
                <div class="divSelectinput" @click="dropDownSelect">
                    <! -- Selected content -->
                    <div class="selectinfos" :title="label" :class="{'no_select': label == 'select'}"> {{ label }} </div>
                    <! -- Triangle icon -->
                    <i class="imgthree fa fa-caret-up" :class="{ 'is-reverse': isListShow }"></i>
                </div>
            </div>
            <! -- Drop down list -->
            <transition name="drop-down" >
                <div class="Selectlist" v-show="isListShow" ref="dropDown">
                    <div class="select_triangle"></div>
                    <ul class="wzc_option_list">
                        <slot name="wzc_option"></slot>
                    </ul>
                </div>
            </transition>
        </div>
    </template>
    
    <script>
    export default {
        name:'wzc_select'.components: {},
        props: {
            placeholder: {
                type: String.default: 'Please select'
            },
            width: {
                type: Number.default: 180
            },
            height: {
                type: Number.default: 40}},data() {
            return {
                label: ' '.isListShow: false.optionid: ' '
            };
        },
        created() {
            this.label = this.placeholder;
        },
        mounted() {
            let _this = this;
            document.addEventListener("click".function( e ){
                if(_this.$refs.divSelect) {
                    if(!!!!! _this.$refs.divSelect.contains(e.target) || !! _this.$refs.dropDown.contains(e.target) )return;
                    else
                        _this.isListShow = false; }})},computed: {
            styleVar() {
                return {
                    '--select-height': this.height + 'px'.'--select-width': this.width + 'px'}}},methods: {
            dropDownSelect() {
                this.isListShow = !this.isListShow; ,}}};</script>
    <style scoped>
        .wzc_select {
            border: 1px solid #E6E6E6;
            border-radius: 5px;
            height: var(--select-height);
            width: var(--select-width);
            line-height: var(--select-height);
        }
        .divSelect {
            width: 100%;
            height: 100%;
            border-radius: 5px;
        }
        .drop_down {
            box-shadow: 0px 0px 6px #709DF7;
        }
        .divSelectinput {
            width: calc(100% - 20px);
            height: 100%;
            margin: 0 5px 0 15px;
            display: flex;
        }
        .selectinfos {
            width: 87.5%;
            cursor: pointer;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .no_select {
            color: #D3DCE6;
        }
        .imgthree {
            width: 12.5%;
            line-height: var(--select-height);
            text-align: center;
            transform: rotate(180deg);
            transition: all 0.3 s;
        }
        .imgthree:before {
            cursor: pointer;
            color: #D3DCE6;
        }
        .imgthree.is-reverse {
            transform: rotate(0deg);
        }
    
        .Selectlist {
            margin-top: 10px;
            z-index: 800;
            position: relative;
            background-color: #fff;
        }
        .wzc_option_list {
            border-radius:5px;
            border:1px solid #E4E7ED;
            width: 100%; 
            padding: 3px 0px;
            box-shadow: 0px 0px 6px #709DF7;
            background-color: #fff;
            margin: 0;
        }
        .select_triangle {
            width: 14px;
            height: 7px;
            position: relative;
            left: 15px;
        }
        .select_triangle::before {
            position: absolute;
            content: "";
            left: 0px;
            width: 0;
            height: 0;
            border-top: 0px solid transparent;
            border-left: 9px solid transparent;
            border-right: 9px solid transparent;
            border-bottom: 8px solid #EBEEF5;
        }
        .select_triangle::after {
            position: absolute;
            left: 2px;
            top: 2px;
            content: "";
            width: 0;
            height: 0;
            border-top: 0px solid transparent;
            border-left: 7px solid transparent;
            border-right: 7px solid transparent;
            border-bottom: 8px solid #fff;  
        }
        .drop-down-enter {
            opacity: 0;
            transform:translate(0px, -80px) scaleY(0.2);
        }
        .drop-down-leave-to {
            opacity: 0;
            transform:translate(0px, -80px) scaleY(0.2);
        }
        .drop-down-enter-active {
            transition: all 0.5 s ease-in;
        }
        .drop-down-leave-active {
            transition: all 0.5 s ease;
        }
    </style>
    Copy the code




    Inner wZC-option. vue complete code

    <template>
      <li class="wzc_option" :style="styleVar" @click="currentSelect">
        <div class="wzc_option_dropdown_item">{{ label }}</div>
      </li>
    </template>
    
    <script>
    export default {
      name: "wzc_select".components: {},
      props: {
        width: {
          type: Number.default: -1,},height: {
          type: Number.default: 34,},label: {
          type: String,},optionid: {
          type: String,}},data() {
        return {};
      },
      created() {},
      mounted() {},
      watch: {},
      computed: {
        styleVar() {
          return {
            "--option-height": this.height + "px"."--option-width": this.width == -1? "100%" : this.width + "px"}; }},methods: {
        currentSelect() {
          this.$parent.label = this.label;
          this.$parent.optionid = this.optionid;
          this.$parent.isListShow = !this.$parent.isListShow;
          // this.$emit('slot-content', {label: this.label, optionid: this.optionid} );}}};</script>
    <style scoped>
    .wzc_option {
      list-style: none;
      height: var(--option-height);
      width: var(--option-width);
      
    }
    .wzc_option:hover {
      color: #409eff;
      font-weight: 700;
      background-color: #f5f7fa;
    }
    .wzc_option_dropdown_item {
      height: 100%;
      width: calc(100% - 30px);
      line-height: var(--option-height);
      cursor: pointer;
      margin: 0 auto;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    
    </style>
    Copy the code