zoukankan      html  css  js  c++  java
  • vue自定义select组件

    1.目的

      看了很多element-ui的源码,决定自己实现一个简单的select组件,遇到的几个难点,便记录下来.

    2.难点一

      element-ui中的select组件通过v-model可以绑定数据,但在我平时用v-model只是在input中使用过,只知道v-model可以双向绑定数据,但并不清楚其中的实现过程,所以 需要清晰的了解v-model是什么,如下.

    <input v-model="test"/>  
    
    <input :value="test" @input="test = $event.target.value"/> // 第一行和第二行的性质是一样的,v-model是一个vue的语法糖
     
    

      以上是input输入框中的v-model,input标签在输入的时候默认会触发'input'事件, 但是自定义的组件并不会,所以需要我们自己手动发送一个'input'事件,其次是,使用了v-model指令以后会默认动态绑定一个属性值value,因此我们在自定义组件中可以在props接收value,并绑定到组件当中,从而实现了双向绑定,具体可以看参考完整代码.

    3.难点二

      当select组件显示选择框时,合理的逻辑是是点击空白或者点击自身都要将选择框关闭, 起初实现是在document中绑定一个click事件用于关闭选择框,当然select点击得阻止事件冒泡,这样的实现方式是在一个页面只有一个select组件是没有问题的,但是当出现多个select组件就会出现一个bug,点击完一个select以后点击另外一个是无法关闭前一个select框的选择框的,问题出在因为每个select框都被阻止了事件的冒泡,自然不会触发document的click事件,从而无法关闭,知晓原因,解决方案如下:

    // 显示选择框
    showSel(){
            this.show = true;
           addEvent(document, 'click',this.hideSel, true);
    }
    
    // 隐藏选择框
    hideSel(e){
        this.show = false;
      // 如果是子元素,则阻止事件捕获 if(this.$refs.sel && this.$refs.sel.contains(e.target)){ stopEvent(e); } removeEvent(document,'click',this.hideSel,true); } // 显示或隐藏 toggle(){ this.show && this.hideSel() || this.showSel(); } // 注意:其中addEvent,removeEvent,stopEvent是为了兼容处理而自定义的方法

      以上就是这次编写select组件的所得,附上完整实例代码.

    <template>
        <div class="select" @click="toggle" ref="sel">
            <div class="input">
                <input
                    type="text" 
                     :placeholder="placeholder"
                     readonly
                     :value = 'value'
                     @blur="handle">
                <img src="../images/drop.svg">
            </div>
            <ul
                class="content"
                :class="{'bottom' : position == 'bottom', 'top' : position == 'top'}"
                v-show="show && values.length"
                ref="content">
    
                <li v-for="item in values">{{item}}</li>
            </ul>
    
        </div>
    </template>
    
    <script>
    import { addEvent, removeEvent, stopEvent } from '../service/utli.js';
    export default {
    
        name : 'comSelect',
        data(){
            return{
                val : '',
                show : false,
                position : 'bottom'
            }
        },
        props : {
            values : {
                type : Array,
                default(){
                    return []
                }
            },
            value : {
                
            },
            placeholder:{
                type : String,
                default : '请选择'
            },
        },
        mounted(){
            this.computePos();
        },
        methods:{
            getElementTop(element){
                var actualTop = element.offsetTop;
                 var current   = element.offsetParent;
             while (current !== null){
                    actualTop += current.offsetTop;
                    current = current.offsetParent;
             }
                return actualTop;
            },
    
            // 计算选择框是往上弹出还是往下弹出
            computePos(){
    
                let elHeight       = this.$refs.sel.offsetHeight;
                let absPos            = this.getElementTop(this.$refs.sel);
                let contentHeight = this.values.length*40;
    
                let docScrollHei  = document.body.scrollTop 
                    || document.documentElement.scrollTop || 0;
    
                let docHeight =  document.documentElement.clientHeight
                    || document.body.clientHeight || 0;
    
                if((elHeight+absPos+contentHeight-docScrollHei)>docHeight){
                    this.position = 'top';
                }else{
                    this.position = 'bottom';
                }
            },
            setVal(item){
                this.$emit('input',item);
            },
            handle(){
                this.$emit('blur');
            },
            showSel(){
                this.show = true;
                addEvent(document, 'click',this.hideSel, true);
            },
            hideSel(e){
                this.show = false;
                console.log(this.$refs.sel.contains(e.target));
                if(this.$refs.sel && this.$refs.sel.contains(e.target)){
                    // 如果是子元素则阻止事件捕获
                    stopEvent(e);
              this.setVal(e.target.innerHtml); } removeEvent(document,
    'click',this.hideSel,true); }, toggle(){ this.show && this.hideSel() || this.showSel(); } } } </script> <style scoped lang="scss"> @import '../style/mixin.scss'; .select{ 100%; height: 100%; position: relative; cursor: pointer; } .input{ 100%; height: 100%; position: relative; cursor: pointer; } .input>input{ 100%; height: 100%; cursor: pointer; } .input>img{ right: 0; top: 50%; 12px; height: 12px; position: absolute; transform: translateY(-50%); } .content{ 100%; max-height: px(300); overflow-y: scroll; border-radius: 10px; @include padding(4px 0); position: absolute; left: 0; background-color: white; box-shadow: 0 0 20px 2px #ccc; @include prix(transform, translateY(5px)); z-index: 2; } .content::-webkit-scrollbar {display: none;} .bottom{ top: 100%; } .top{ bottom: 125%; } .content>li{ height: 40px; line-height: 40px; 100%; @include padding(0 0 0 10px); } .content>li:hover{ color: #409eff; background-color: rgba(33,33,33,.2); } </style>
  • 相关阅读:
    DFS 之 全排列
    蓝桥杯: 标题:第几个幸运数
    第K个幸运数字(4、7)
    C++将十进制数转化为二进制
    C++中数组声名后不初始化,数组里的值都是0吗?
    html和jsp区别
    中缀表达式转换为后缀表达式
    多个Activity之间共享数据的方式
    Jupyter Notebook入门教程
    Android之Activity生命周期详解
  • 原文地址:https://www.cnblogs.com/024-faith/p/select.html
Copyright © 2011-2022 走看看