el-input显示字符数量

18 阅读2分钟

需求:特殊开发,需要给element plus的某些输入框计算字符数量。

环境:vue3+ vite+elementPlus

实际效果:

企业微信截图_17682118492206.png

过程:

1.创建自定义命令文件(inputCount.js)

/**
 * el-input字符统计自定义指令
 * 使用方式:v-input-count="{ max: 100 }"
 * 参数:
 * - max: 最大字符数(可选)
 * - showCount: 是否显示字符数(默认true)
 * - position: 位置,'inside'、'right'或'bottom'(默认'inside')
 */
export default {
  mounted(el, binding) {
    // 获取el-input组件实例
    const inputElement = el.querySelector('.el-input__inner') || el.querySelector('input') || el.querySelector('textarea');
    if (!inputElement) return;
    
    // 获取配置参数
    const config = binding.value || {};
    const max = config.max || null;
    const showCount = config.showCount !== false; // 默认为true
    const position = config.position || 'inside'; // 默认为内部显示
    
    if (!showCount) return;
    // 创建字符计数元素
    const countElement = document.createElement('span');
    countElement.className = 'input-count';
    countElement.style.fontSize = '12px';
    countElement.style.color = '#100aff';
    countElement.style.pointerEvents = 'none'; // 防止点击计数元素
    countElement.style.userSelect = 'none'; // 防止选择计数元素
    
    // 根据位置设置样式
    if (position === 'inside') {
      // 输入框内部靠右显示
      countElement.style.position = 'absolute';
      countElement.style.right = '2px';
      countElement.style.top = '-3px';
      countElement.style.transform = 'translateY(-50%)';
      countElement.style.zIndex = '1';
      countElement.style.backgroundColor = '#ffffff00';
      countElement.style.padding = '0 2px';
      
      // 确保父元素有相对定位
      el.style.position = 'relative';
      
      // 为输入框添加右侧内边距,避免文字与计数重叠
      // if (inputElement.tagName === 'INPUT') {
      //   inputElement.style.paddingRight = '10px';
      // } else if (inputElement.tagName === 'TEXTAREA') {
      //   inputElement.style.paddingRight = '10px';
      // }
      
      // 添加到输入框内部
      el.appendChild(countElement);
    } else if (position === 'right') {
      // 输入框右侧外部显示
      countElement.style.position = 'absolute';
      countElement.style.right = '-40px';
      countElement.style.top = '50%';
      countElement.style.transform = 'translateY(-50%)';
      el.style.position = 'relative';
      el.style.marginRight = '50px';
      el.appendChild(countElement);
    } else if (position === 'bottom') {
      // 输入框下方显示
      countElement.style.display = 'block';
      countElement.style.textAlign = 'right';
      countElement.style.marginTop = '4px';
      countElement.style.marginRight = '8px';
      el.parentNode.style.position = 'relative';
      el.parentNode.appendChild(countElement);
    }
    
    // 更新字符计数函数
    const updateCount = () => {
      // 使用多种方式获取输入框的值,确保准确性
      let value = '';
      
      // 尝试从Vue组件获取值
      if (el.__vueParentComponent && el.__vueParentComponent.props) {
        value = el.__vueParentComponent.props.modelValue || '';
      }
      
      // 尝试从input元素获取值
      if (!value && inputElement.value !== undefined) {
        value = inputElement.value;
      }
      
      // 尝试从属性获取值
      if (!value && inputElement.getAttribute) {
        value = inputElement.getAttribute('value') || '';
      }
      
      // 确保值是字符串类型
      value = String(value || '');
      
      const length = value.length;
      let text = `${length}`;
      
      if (max) {
        text += `/${max}`;
        // 超过限制时改变颜色
        if (length > max) {
          countElement.style.color = '#f56c6c';
        } else {
          countElement.style.color = '#909399';
        }
      }
      
      // 只有当长度大于0时才显示计数,否则隐藏计数元素
      if (length > 0) {
        countElement.textContent = text;
        countElement.style.display = '';
      } else {
        countElement.style.display = 'none';
      }
    };
    
    // 初始化计数
    updateCount();
    
    // 监听输入事件
    inputElement.addEventListener('input', updateCount);
    
    // 监听键盘事件,处理删除等操作
    inputElement.addEventListener('keydown', updateCount);
    
    // 监听粘贴和剪切事件
    inputElement.addEventListener('paste', () => {
      setTimeout(updateCount, 10);
    });
    inputElement.addEventListener('cut', () => {
      setTimeout(updateCount, 10);
    });
    
    // 定期检查值的变化(作为备用方案)
    const intervalId = setInterval(() => {
      updateCount();
    }, 1000);
    
    // 保存更新函数,以便后续使用
    el._updateCount = updateCount;
    el._countElement = countElement;
    el._inputElement = inputElement;
    el._intervalId = intervalId;
  },
  
  updated(el, binding) {
    // 如果配置发生变化,更新计数显示
    if (el._updateCount) {
      el._updateCount();
    }
  },
  
  beforeUnmount(el) {
    // 清理事件监听器
    if (el._inputElement && el._updateCount) {
      el._inputElement.removeEventListener('input', el._updateCount);
      el._inputElement.removeEventListener('keydown', el._updateCount);
      el._inputElement.removeEventListener('paste', el._updateCount);
      el._inputElement.removeEventListener('cut', el._updateCount);
    }
    
    // 清理定时器
    if (el._intervalId) {
      clearInterval(el._intervalId);
    }
    
    // 移除计数元素
    if (el._countElement && el._countElement.parentNode) {
      el._countElement.parentNode.removeChild(el._countElement);
    }
  }
};

2.在主文件导入该自定义指令

例如在main.js中

import  inputCountDirective(自定义)form 'xxxx.js'(文件位置)

并注册app.directive('input-count', inputCountDirective);

3.使用

在输入框添加属性v-input-count,更多使用方式看文件内头部示例。