input 超出maxlength限制后,输入框变红

一、前言

最近收到产品的一个需求:输入框限制了maxlength=“11”,需要在输入第12位时,输入框变红;当然,第12位是不能真正输入到输入框中的。

二、实现难点

其实,单纯的要监听 字母和数字以及字符 还是比较容易实现的,通过监听 keyup、input 基本都能做到。但是,有一个问题就是:在输入中文时,还是拼写,这时输入框也需要变红,上面的事件就监听不到。需要单独的事件 compositionstart、compositionend
实现难点有以下两个:

1. input 怎么区分输入的是中文 和 (英文或者数字)

方法一:监听 compositionstart、compositionend 和 input或者keyup 这三个事件

方法二:InputEvent:isComposing 属性 ==> MDN解释表示事件是否是在 compositionstart 之后且在 compositionend 之前触发的。

解析compositionstart、compositionend:

  • compositionstart 开始输入中文拼音,但没确定输入到输入框内
  • compositionend 介绍中午拼写,对于文字已录入到输入框
  • 输入英文和数字,特殊字符不会触发
2. vue自定义指令,在同时绑定多个事件时,怎么在多个事件中传参(可能用不到)

在绑定的事件中,对事件源进行处理;将参数放到 e.target.参数名 = '参数值' => 这样在每个事件源中都能获取到参数

三、自定义指令实现限制输入框最大长度,达到限制长度再次输入时,输入框变红
/**
 * 限制输入框最大长度,达到限制长度再次输入时,输入框变红
 * 用法:v-input-maxlength="6" 不能在标签上配置 maxlength 属性
 * v-input-maxlength.limit="6" 可实现 show-word-limit 效果
 */
Vue.directive('input-maxlength', {
    bind(el, binding, vnode) {
        const limit = binding.modifiers?.limit || false
        const ele = el.tagName === 'INPUT' ? el : el.querySelector('input') || el.querySelector('textarea')
        
        ele.classList.add('el-input__maxlength') // 给el-input添加class, 解决验证不通过时 border 变红的问题(因为下面会通过style修改input的样式,导致验证不通过时边框不能变红)

        if (limit) {
            const suffix = document.createElement('span')
            suffix.className = 'custom-input-suffix'
            suffix.innerText = `${ele.value.length}/${binding.value}`
            suffix.style = `position: absolute; bottom: 0px; right: 5px; font-size: 12px;color: #909399;line-height: 40px;`
            el.appendChild(suffix)

            ele.parentNode.classList.add('input-maxlength-limit'); // 给input的父元素添加class,解决clearable与limit重叠问题
        }

        ele.addEventListener('compositionend', maxInputCallback(el, ele, binding.value, limit, vnode), false)
        ele.addEventListener('input', maxInputCallback(el, ele, binding.value, limit, vnode), false)
        ele.addEventListener('focus', maxInputFocus(el, ele), false)
        ele.addEventListener('blur', maxInputBlur(el, ele), false)
    },
    unbind(el) {
        const ele = el.tagName === 'INPUT' ? el : el.querySelector('input')
        ele.removeEventListener('compositionend', maxInputCallback(el, ele), false)
        ele.removeEventListener('input', maxInputCallback(el, ele), false)
        ele.removeEventListener('focus', maxInputFocus(el, ele), false)
        ele.removeEventListener('blur', maxInputBlur(el, ele), false)
    },
})

function maxInputCallback(el, ele, maxLength, isLimit = false, vnode) {
    function handler(e) {
        if (maxLength === undefined) return

        let val = ele.value
        if (val.length > maxLength && !e.isComposing) {
            // 这个条件有一个小问题,当输入达到限制后,正在拼中文时(e.isComposing = true)边框是蓝色的
            ele.value = val.substr(0, maxLength)
            ele.style.border = '1px solid #F00'
        } else {
            ele.style.border = '1px solid #409EFF'
        }

        // 触发数据的双向绑定。
        if (vnode.componentInstance) {
            vnode.componentInstance.$emit('input', ele.value)
        } 

        if (isLimit && !e.isComposing) {
            if (el.children[1] && el.children[1].className === 'custom-input-suffix') {
                el.children[1].innerText = `${ele.value.length}/${maxLength}`
            }
        }
    }

    return handler
}

function maxInputFocus(el, ele) {
    function handler(e) {
        ele.style.border = '1px solid #409EFF'
    }
    return handler
}
function maxInputBlur(el, ele) {
    function handler(e) {
        ele.style.border = '1px solid #DCDFE6'
    }
    return handler
}


------------ 然后再全局样式添加一下样式 --------------
// v-input-maxlength => 解决验证不通过时 border 变红的问题
.el-form-item.is-error .el-input__maxlength {
    border-color: #F56C6C !important;
}
// v-input-maxlength.limit => 解决clearable与limit重叠问题 
.input-maxlength-limit {
    .el-input__inner {
        padding-right: 50px;
    }
    .el-input__suffix {
        right: 30px;
    }
}
--------------- 以上代码就能实现完整的效果了 ---------------


------------分割线:如果监听keyup事件,可以参考下面代码,但会有很多问题。最好不要使用------------
function maxInputKeyup(el, ele ) {
    const maxLength = ele.getAttribute('maxlength');
    const regex = /^[a-zA-Z0-9~!@#$%^&*()-=_+{}<>?:"',./\[\]]$/;  // 不支持空格
    let oldLimitLength = false; // 上一次输入是否达到限制长度
    function handler(e) {
        let val = ele.value;

        /**
         * val.length >= maxLength 是当前输入是否达到限制长度
         * oldLimitLength 是上一次输入是否达到限制长度
         * 如果当前输入达到限制长度,并且上一次输入没有达到限制长度,则表示为正好输入到限制长度(只要一个条件无法判断这种情况)
         * 如果当前输入长度小于限制长度,但上一次输入达到限制长度,则表示为删除操作=>删除了限制字符的最后一个字符
         */
        console.log('------>>> 333333333',e, regex.test(e.key), e.isComposing);
       // 在输入限制的最后一个字符并且是输入中文时会有问题 
        if( regex.test(e.key) && val.length >= maxLength && oldLimitLength ) {
            ele.style.border = '1px solid #F00';
        } else {
            ele.style.border = '1px solid #409EFF';
        }

        if (val.length >= maxLength) {
            oldLimitLength = true;
        } else {
            oldLimitLength = false;
        }
    }

    return handler;
}

文章仅为本人学习过程的一个记录,仅供参考,如有问题,欢迎指出!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值