欧易备用下载地址
289.17MB · 2025-09-19
Vue3 + Element Plus 输入框省略号插件:零侵入式全局解决方案
在日常开发中,我们经常会遇到输入框内容过长需要显示省略号的需求。传统的做法是在每个组件中手动添加样式和逻辑,但这种方式存在以下问题:
今天我将分享一个零侵入式的全局解决方案,通过 Vue3 插件的方式,自动为所有 `el-input` 输入框添加省略号显示和悬浮提示功能。
我们的解决方案基于以下几个核心技术:
/**
* el-input 省略号全局插件
* 自动为所有 el-input 输入框添加省略号显示和悬浮提示功能
* 不包含 textarea 类型
*/
class InputEllipsisManager {
private observer: MutationObserver | null = null
private processedElements = new WeakSet<HTMLElement>()
constructor() {
this.init()
}
init() {
// 等待 DOM 加载完成后开始处理
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => this.startObserving())
} else {
this.startObserving()
}
}
private startObserving() {
// 处理已存在的元素
this.processExistingElements()
// 创建 MutationObserver 监听 DOM 变化
this.observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
this.processElement(node as HTMLElement)
}
})
}
})
})
// 开始观察
this.observer.observe(document.body, {
childList: true,
subtree: true
})
}
private processExistingElements() {
// 处理页面中已存在的所有 el-input
const inputs = document.querySelectorAll('.el-input:not(.el-textarea)')
inputs.forEach(input => this.processElement(input as HTMLElement))
}
private processElement(element: HTMLElement) {
// 如果已经处理过,跳过
if (this.processedElements.has(element)) {
return
}
// 查找 el-input 元素
const inputs = element.classList?.contains('el-input') && !element.classList?.contains('el-textarea')
? [element]
: Array.from(element.querySelectorAll?.('.el-input:not(.el-textarea)') || [])
inputs.forEach(inputEl => {
if (this.processedElements.has(inputEl)) {
return
}
this.processedElements.add(inputEl)
this.addEllipsisToInput(inputEl)
})
}
private addEllipsisToInput(inputEl: HTMLElement) {
const inputInner = inputEl.querySelector('.el-input__inner') as HTMLInputElement
if (!inputInner || inputInner.tagName.toLowerCase() === 'textarea') {
return
}
// 添加省略号样式
inputInner.style.textOverflow = 'ellipsis'
inputInner.style.whiteSpace = 'nowrap'
inputInner.style.overflow = 'hidden'
// 创建更新提示的函数
const updateTooltip = () => {
const text = inputInner.value || inputInner.placeholder || ''
if (text && inputInner.scrollWidth > inputInner.clientWidth) {
inputInner.title = text
} else {
inputInner.removeAttribute('title')
}
}
// 添加事件监听器
const events = ['input', 'focus', 'blur', 'change']
events.forEach(eventType => {
inputInner.addEventListener(eventType, updateTooltip)
})
// 初始检查
updateTooltip()
// 监听窗口大小变化
const resizeHandler = () => {
setTimeout(updateTooltip, 100)
}
window.addEventListener('resize', resizeHandler)
// 保存清理函数
;(inputEl as any)._ellipsisCleanup = () => {
events.forEach(eventType => {
inputInner.removeEventListener(eventType, updateTooltip)
})
window.removeEventListener('resize', resizeHandler)
}
}
// 公共方法:手动刷新所有输入框的省略号状态
public refreshInputEllipsis() {
this.processExistingElements()
}
// 销毁方法
public destroy() {
if (this.observer) {
this.observer.disconnect()
}
// 清理所有已处理元素的事件监听器
document.querySelectorAll('.el-input').forEach(inputEl => {
if ((inputEl as any)._ellipsisCleanup) {
;(inputEl as any)._ellipsisCleanup()
delete (inputEl as any)._ellipsisCleanup
}
})
}
}
// 创建全局实例
let ellipsisManager: InputEllipsisManager | null = null
// Vue 插件定义
export default {
install(app: any) {
// 在应用挂载后启动
app.mixin({
mounted() {
if (!ellipsisManager) {
ellipsisManager = new InputEllipsisManager()
}
}
})
// 提供全局方法
app.config.globalProperties.$refreshInputEllipsis = () => {
if (ellipsisManager) {
ellipsisManager.refreshInputEllipsis()
}
}
}
}
// 导出管理器类(可选,用于高级用法)
export { InputEllipsisManager }
this.observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
this.processElement(node as HTMLElement)
}
})
}
})
})
作用:自动监听页面中新增的 DOM 元素,确保动态添加的输入框也能被处理。
private processedElements = new WeakSet<HTMLElement>()
if (this.processedElements.has(element)) {
return
}
this.processedElements.add(element)
作用:使用 WeakSet 记录已处理的元素,避免重复处理同一个输入框,提高性能。
const updateTooltip = () => {
const text = inputInner.value || inputInner.placeholder || ''
if (text && inputInner.scrollWidth > inputInner.clientWidth) {
inputInner.title = text
} else {
inputInner.removeAttribute('title')
}
}
作用:通过比较 `scrollWidth` 和 `clientWidth` 来判断内容是否超出,只有超出时才显示悬浮提示。
将上述代码保存为 `src/plugins/inputEllipsis.ts`
import { createApp } from 'vue'
import App from '@/App.vue'
import inputEllipsisPlugin from '@/plugins/inputEllipsis'
const app = createApp(App)
app
.use(inputEllipsisPlugin) // 注册输入框省略号插件
.mount('#app')
// src/styles/element-plus.scss
// el-input 省略号全局样式
.el-input:not(.el-textarea) {
.el-input__inner {
// 确保省略号正确显示
&[style*="text-overflow: ellipsis"] {
display: block;
width: 100%;
box-sizing: border-box;
}
}
// 为只读状态的输入框也支持省略号
&.is-disabled .el-input__inner {
&[style*="text-overflow: ellipsis"] {
cursor: default;
}
}
}
// 确保输入框容器支持省略号
.el-input__wrapper {
overflow: hidden;
}
安装插件后,所有的 `el-input` 都会自动添加省略号功能:
<template>
<!-- 这些输入框会自动添加省略号功能 -->
<el-input v-model="value1" placeholder="自动添加省略号" />
<el-input v-model="value2" placeholder="这个也会自动处理" />
<!-- textarea 不会被影响 -->
<el-input type="textarea" v-model="value3" placeholder="这是文本域,不会被处理" />
<!-- 动态添加的输入框也会被自动处理 -->
<el-input v-if="showInput" v-model="value4" placeholder="动态输入框也会被处理" />
</template>
// 在任何组件中
this.$refreshInputEllipsis()
import { InputEllipsisManager } from '@/plugins/inputEllipsis'
// 创建自定义实例
const customManager = new InputEllipsisManager()
const resizeHandler = () => {
setTimeout(updateTooltip, 100)
}
窗口大小变化时使用防抖,避免频繁计算。
;(inputEl as any)._ellipsisCleanup = () => {
events.forEach(eventType => {
inputInner.removeEventListener(eventType, updateTooltip)
})
window.removeEventListener('resize', resizeHandler)
}
每个输入框都保存清理函数,避免内存泄漏。
使用 WeakSet 而不是 Set,让垃圾回收器自动清理不再使用的元素引用。
这个输入框省略号插件通过 Vue3 插件系统、MutationObserver 和 WeakSet 等技术,实现了一个完全自动化的解决方案。它不仅解决了传统方案的痛点,还提供了更好的性能和用户体验。
核心优势:
如果您觉得这个方案有用,欢迎点赞收藏!也欢迎在评论区分享您的使用心得和改进建议。
作者简介:专注于前端技术分享,Vue3 + TypeScript 实践者
技术栈:Vue3, TypeScript, Element Plus, 前端工程化