原生js实现input搜索框实时检索功能实现,支持选中或回车生成tag标签(超简单)

问题:实现input搜索框实时检索的功能,支持回车或选中后生成tag标签,公司设计出功能需求后,在市场上没找到差不多类似的,只能手撸一个了

解决方法:

1、参考网上文章:input中回车生成标签实现及分析_vue input中回车生成标签-优快云博客

但是会出现 appendchild 添加的li元素点击事件失效问题,使用事件委托也无法解决,最后采用for of 循环标签处理完成,细节部分还需要优化,比如外部传入组件的下拉联想数组可以替换,模糊搜索时判定laber名称返回value值等等

效果图

话不多说直接上代码

<template>
    <!-- class="h32InputRadius" -->
    <div class="TagInput">
        <el-select
            v-model="tagsArr"
            multiple
            collapse-tags
            ref="selectw"
            @focus = 'selectFocus()'
            placeholder="请选择">
        </el-select>
        <el-dialog :title="title" :visible.sync="voidVisible" width="480px" append-to-body>
            <!-- 外层div -->
            <div class="arrbox" @click="onclick">
                <div style="display: flex;justify-content: start;align-items: start;flex-wrap: wrap">
                    <!-- 标签 -->
                    <div v-for="(item,index) in tagsArr" :key="index" class="spanbox">
                        <span class="tagspan">{{item}}</span>
                        <i class="el-tag__close el-icon-close" @click="removeTag(index,item)"></i>
                    </div>
                    <!-- 输入框 -->
                     <div>
                        <input
                            :placeholder="placeholder"
                            v-model="currentval"
                            @keyup.enter="addTags"
                            :style="inputStyle"
                            class="inputTag blueInput"
                            ref="inputTag"
                            type="text"
                        />
                        <ul id="list">
                            <li index="1" v-for="(v,i) in data" :key="i" class="oLi" @click="liSelect(v)">{{ v }}</li>
                        </ul>
                     </div>
                </div>
                <!-- 清除按钮,总数显示 -->
                 <div class="bottom">
                    <div>{{ tagsArr.length }}</div>
                    <div :style="{color:tagsArr.length>0?'#7EC050':'#999'}" style="cursor: pointer;" @click="tagsArr = []">清除</div>
                 </div>
            </div>
            <span slot="footer" class="dialog-footer">
                <JcButton type="normal" @click="submit()">确 定</JcButton>
            </span>
        </el-dialog>
    </div>
</template>

<script>
import JcButton from './JcButton.vue'
export default {
    name: 'TagInput',
    props: {
        parentArr: {
            type: Array,
            default () {
                return []
            }
        },
        limit: { // 最多生成标签数
			type: Number,
            default: 100
        },
        placeholder: {
			type: String,
            default: '请输入'
        },
        title:{
            type:String,
            default:'选择客户'
        }
    },
    components: {
        JcButton
    },
    created () {
        if (this.parentArr.length) {
            this.tagsArr = this.parentArr
        }
    },
    data () {
        return {
            voidVisible:false,
            currentval: '',
            tagsArr: [],
            inputLength: '',
            data:[]
        }
    },
    watch: {
        tagsArr () {
            this.$emit('change', this.tagsArr)
        },
        currentval (val) {
            console.log(val);
            // 实时改变input输入框宽度,防止输入内容超出input默认宽度显示不全
            this.inputLength = this.$refs.inputTag.value.length * 12 + 20;

            let fruits = ["中国","日本","美国","俄罗斯","加拿大","英国","澳大利亚","西班牙","德国","孟买加","阿拉伯","印度","印度尼西亚"];
            this.data = this.searchByIndexOf(val, fruits);//模糊搜索,返回数组中存在的数据
        },
        parentArr () {
            this.tagsArr = this.parentArr.length ? this.parentArr : []
        }
    },
    computed: {
        inputStyle () {
            let style = {};
            style.width = `${this.inputLength}px`;
            return style;
        },
    },
    mounted() {
        this.tagsArr = this.parentArr;
    },
    methods: {
        removeTag (index, item) {
            this.tagsArr.splice(index, 1)
        },
        addTags() {
            if (this.tagsArr.length === this.limit) {
                this.$emit('on-limit', true);
                return;
            }
            if (this.tagsArr.indexOf(this.currentval) > -1) {
                this.$message.warning('该标签已存在')
                this.$emit('on-repeat', true);
                return;
            }
            // 格式校验
            // let reg = /^[1-9]+[0-9]*$/
            // if (!reg.test(this.currentval)) {
            //     alert('格式不正确!');
            //     this.currentval = '';
            //     return;
            // }
            // 当输入值用,或空格隔开时
            if(this.currentval.indexOf(",") != -1){
                this.currentval.split(',').forEach(element => {
                    // 如果输入的数据存在,则跳过不添加
                    if (this.tagsArr.indexOf(element) > -1) {

                    }else{
                        // 去除前后空格,空项不添加
                        if(element == ' '){

                        }else{
                            this.tagsArr.push(element.trim())
                        }
                    }
                });
            }else if(this.currentval.indexOf(" ") != -1){
                this.currentval.split(' ').forEach(element => {
                    // 如果输入的数据存在,则跳过不添加
                    if (this.tagsArr.indexOf(element) > -1) {

                    }else{
                        // 去除前后空格,空项不添加
                        if(element == ' '){

                        }else{
                            this.tagsArr.push(element.trim())
                        }
                    }
                });
            }else{
                 // 去除前后空格,空项不添加
                 if(this.currentval == ' ' || this.currentval == ''){}else{
                    this.tagsArr.push(this.currentval.trim());
                }
            }
            this.currentval = '';
        },
        onclick() {
            this.$nextTick(()=>{
                this.$refs.inputTag.focus();
            })
        },
        selectFocus(){
            this.voidVisible = true
            this.$refs.selectw.visible = false;
        },
        submit(){
            this.addTags()
            this.voidVisible = false
        },

         //模糊查询:利用字符串的indexOf方法
         searchByIndexOf(keyWord, list){
            if(!(list instanceof Array)){
                return ;
            }
            if(keyWord == ""){
                return [];
            }else{
                var len = list.length;
                var arr = [];
                for(var i=0;i<len;i++){
                    //如果字符串中不包含目标字符会返回-1
                    if(list[i].indexOf(keyWord)>=0){
                        arr.push(list[i]);
                    }
                }
                return arr;
            }
        },
        liSelect(value){
            this.currentval = value
            this.addTags()
        },
    }
}
</script>

<style  lang="less">
    .TagInput{
        width: 100%;
        font-size:0;
        height: 32px;
        .el-select{
            width: 100%;
            height: 32px !important;
            .el-input.el-input--suffix{
                font-size:0;
                height: 32px !important;
                .el-input__inner{
                    height: 32px !important;
                }
                .el-input__suffix{
                    height: 32px !important;
                    line-height: 32px !important;
                }
            }
        }
    }
    .el-dialog {
        .el-dialog__header {
            padding: 28px 24px 16px !important;
            border-bottom: none !important;

            .el-dialog__title {
                font-size: 18px !important;
                color: #1d2129 !important;
            }

            .el-dialog__close {
                font-size: 18px !important;
                color: #1d2129 !important;
            }
        }

        .el-dialog__body {
            padding: 0 24px 14px !important;
            padding-top: 0px !important;
              /* 外层div */
            .arrbox {
                height: 194px;
                width: 100%;
                box-sizing:border-box;
                background-color: white;
                border: 1px solid #dcdee2;
                border-radius: 4px;
                font-size: 12px;
                text-align: left;
                padding-left: 5px;
                word-wrap: break-word;
                overflow: hidden;
                overflow-y: auto;
                padding: 10px;
                display: flex;
                flex-direction: column;
                justify-content: space-between;
                // align-items: center;
                .bottom{
                    height: 18px;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    div{
                        font-size: 13px;
                        color: #999999;
                    }
                }
            }
            /* 标签 */
            .spanbox {
                display: flex;
                justify-content: space-between;
                align-items: center;
                font-size: 14px;
                margin: 4px 8px 4px 0;
                padding: 3px 6px;
                background-color: #f7f7f7;
                border: 1px solid #e8eaec;
                border-radius: 3px;
                .el-tag__close.el-icon-close {
                    color: #A7AFB8;
                    -ms-flex-negative: 0;
                    flex-shrink: 0;
                    cursor: pointer;
                    margin-left: 5px;
                }
                .el-tag__close.el-icon-close:hover{
                    color: #333;
                }
                .tagspan {
                    position: relative;
                    display: inline-block;
                    color: #333333;
                    font-size: 13px;
                    cursor: pointer;
                    opacity: 1;
                    vertical-align: middle;
                    overflow: hidden;
                    transition: 0.25s linear;
                }

            }

            /* input */
            .inputTag {
                font-size: 13px;
                border: none;
                box-shadow: none;
                outline: none;
                background-color: transparent;
                padding: 0;
                width: auto;
                min-width: 300px;
                vertical-align: top;
                height: 32px;
                color: #495060;
                line-height: 32px;
            }

        }

        .el-textarea__inner {
            margin-top: 0px !important;
        }
    }
    .blueInput{
        position: absolute;
    }
    /* 搜索下拉框*/
    #list {
        // list-style: none;
        // margin: 0;
        // padding: 0;
        width:300px;
        height: 115px;
        overflow-y: auto;
        overflow-x: hidden;
        display: inline-block;
        position: relative;
        top: 32px;
        left: 0;
    }
    ul#list li {
        margin: 0;
        padding: 10px;
        cursor: pointer;
    }

    ul#list li:hover {
        background-color: #e8eaec;
        width:300px;
    }
</style>

 

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值