当前位置: 首页
前端开发
从零实现懂分寸的文本省略组件

从零实现懂分寸的文本省略组件

热心网友 时间:2026-07-09
转载

一、问题:tooltip 的“狼来了”困境

先说一个随处可见的场景。很多前端项目里,代码大概长这样:

一个



  

{{ text }}

这行看似简单的代码隐藏着一个容易被忽略的问题:无论文本是否溢出,tooltip 都会出现。鼠标划过“项目管理”四个字,弹出的提示也是“项目管理”——这不仅是冗余信息,更像是在一点点消磨用户的注意力。一个页面上几十个这样的 tooltip,每次 hover 都像是一场微小的“狼来了”。

更令人深思的是,这个问题在大多数项目中从未被认真修复,原因在于:

  • 产品经理不会主动提“去掉不必要的 tooltip”这类需求
  • 设计稿的 mockup 里看不出 hover 交互态
  • 测试用例中没有“验证短文本不弹 tooltip”的检查项
  • 功能上似乎也没什么大问题,大家就默认略过了

结果就是,这个细节一直潜伏在页面中,成为无数微小用户体验摩擦中的一个。

一个真正体贴的文本省略组件,应该在文本不需要省略时安安静静地闭嘴。


二、核心判断:浏览器已经算好了,我们只需要读出来

溢出检测这件事,本质上不是复杂的算法问题,而是一个简单的读值操作。浏览器在渲染文本时已经精确计算了每个元素的尺寸,我们要做的只有一件事:比较两个关键属性。

溢出条件:scrollWidth > clientWidth  或  scrollHeight > clientHeight
  • scrollWidth:元素内容实际占据的宽度(包括被 overflow hidden 裁掉的部分)
  • clientWidth:元素可视区的宽度(CSS 盒模型去掉滚动条后的内容宽度)

当内容没有溢出时,scrollWidth === clientWidth,差值恰好为零。

当内容溢出时,scrollWidth 会超出 clientWidth,浏览器裁掉了超出的部分——同时也诚实地保留了真实宽度的数值。

所以检测溢出不需要任何复杂计算,只需要问浏览器一句:你的内容到底有多宽?

const checkOverflow = () => {
  const el = overEllipsisRef.value
  if (!el) return
  isOverflow.value = el.scrollWidth > el.clientWidth
                  || el.scrollHeight > el.clientHeight
}

多行省略的原理也类似,不过比较维度从宽度变成了高度:

单行省略 → 比较 scrollWidth vs clientWidth
多行省略 → 比较 scrollHeight vs clientHeight(因为 -webkit-line-clamp 裁掉的是高度)

三、不仅是“判断一次”:什么时候需要重新判断?

溢出状态不是一次判定就结束的。以下任何情况发生,都可能让状态说变就变:

触发场景例子
内容变化数据从接口返回,文本从空变成了一段话
容器尺寸变化用户拖拽侧边栏,表格列宽变了
窗口缩放浏览器从全屏变成半屏
字体/主题切换字体大小变了,或者换了个语言包导致文字变长
行数限制变化从单行省略切换到三行省略

组件对这些场景的覆盖非常细致:

// 场景 1:内容变化 → watch content
watch(() => props.content, () => {
  refreshEllipsisState()
})// 场景 2:行数限制变化 → watch line
watch(() => props.line, () => {
  refreshEllipsisState()
})// 场景 3:容器尺寸变化 → ResizeObserver
const initObserver = () => {
  resizeObserver = new ResizeObserver(() => {
    refreshEllipsisState()
  })
  resizeObserver.observe(overEllipsisRef.value)
}// 场景 4:slot 内容更新 → onUpdated
onUpdated(() => {
  refreshEllipsisState()
})

每一种可能导致溢出状态变化的场景,都有一条对应的监听路径。不遗漏触发源,是这个组件可靠性的基础。


四、ResizeObserver 的正确用法

ResizeObserver 是这里最容易用错的 API。两点值得展开聊聊:

1. 不是监听 window,而是监听元素本身

很多开发者习惯用 window.addEventListener('resize', ...) 来做响应式布局,但这有两个问题:

  • 粒度太粗:窗口 resize 不一定影响当前元素(弹窗里的元素、固定宽度的侧边栏不受影响)
  • 漏掉场景:元素尺寸变化不一定伴随窗口 resize——CSS flex/grid 布局中,兄弟元素的增删会导致当前元素尺寸变化,但窗口大小没变

ResizeObserver 直接监听目标元素的尺寸变化,颗粒度精确到像素。

2. 有兜底降级方案

代码里有一个值得注意的判断:

if (!window.ResizeObserver || !overEllipsisRef.value) return

对于不支持 ResizeObserver 的古董浏览器,组件降级为只在 mount 和 content/watch 变化时检测,不报错、不崩溃。这是一个成本极低的渐进增强——支持了就体验更好,不支持也不影响核心功能。


五、把判断结果驱动到 tooltip 的 disabled 属性

这是整个组件最巧妙的一句模板代码:


  

{{ props.content }}

disabled 属性在 Element Plus 中控制 tooltip 是否生效。传入 true 时,tooltip 完全不渲染弹出层,不会有 hover 事件监听,不会有 DOM 节点创建。

这意味着:

  • 文本短 → isOverflow = falsedisabled = truetooltip 完全不存在,零开销
  • 文本长 → isOverflow = truedisabled = false → tooltip 正常工作

不是“有 tooltip 但内容为空”,而是 tooltip 根本不存在。 这比用 v-if 更优雅——v-if 会销毁重建 DOM,而 disabled 只影响 tooltip 的行为逻辑。Tooltip 的 DOM 结构和事件绑定保持不变,切换开销更小。


六、多行省略的 CSS 配合

单行省略是 CSS 基本功:

white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

多行省略需要 -webkit-line-clamp,但注意它要求配合 display: -webkit-box-webkit-box-orient: vertical,这三者缺一不可。组件在这里做了一个干净的属性分离:

const isMultiLine = computed(() => Number(props.line) > 1)const ellipsisStyle = computed(() =>
  isMultiLine.value
    ? { WebkitLineClamp: String(props.line) }
    : {}
)

.over-ellipsis--single-line {
  display: block;
  white-space: nowrap;
}.over-ellipsis--multi-line {
  word-break: break-all;
}

单行用 white-space: nowrap,多行用 -webkit-line-clamp(配合外部的 display: -webkit-box + -webkit-box-orient: vertical),两种省略模式通过 isMultiLine 干净切换。line prop 默认值设为 1,这是一个合理的默认——大多数场景确实是单行省略。


七、完整的心智模型

把这个组件的设计哲学抽象出来,是一条可以迁移的通用原则:

┌─────────────────────────────────────────────────┐
│                                                  │
│   状态 X 是否需要展示?                            │
│       ↓                                          │
│   能否在运行时检测?                               │
│       ↓                                          │
│   用浏览器原生 API 读值(不自己算)                  │
│       ↓                                          │
│   识别所有会导致值变化的触发源,逐一监听              │
│       ↓                                          │
│   用 disabled / v-if 控制功能的启用,而非事后补救     │
│                                                  │
└─────────────────────────────────────────────────┘

映射到 OverEllipsis:

步骤实现
状态 Xtooltip 是否需要出现
运行时检测scrollWidth > clientWidth
原生 API 读值直接用 DOM 属性,不自己算字符宽度
触发源覆盖watch content, watch line, ResizeObserver, onUpdated
控制功能启用:disabled="!isOverflow"

这个模式可以复用到很多场景:

  • 截断标记:“查看更多”按钮只在真正截断时才显示
  • 滚动阴影:只在可滚动时在边缘显示渐变阴影
  • 虚拟列表:只在数据量超过阈值时才启用虚拟化
  • 分页器:只在超过一页时才显示分页组件
  • 拖拽手柄:只在列表项 > 1 时才可拖拽排序

八、这个组件也有不完美的地方

实事求是地说,120 行代码不可能完美:

  1. 多行省略的 CSS 依赖外部-webkit-box-orient: vertical 等样式需要在使用侧定义,组件自身没有完整包含
  2. innerText 取 tooltip 内容innerText.trim() 在某些情况下可能和视觉显示的文本有差异(例如包含 ::before 伪元素内容时)
  3. onUpdated 可能触发过于频繁:在频繁更新的场景下(例如动画),每次 VNode 更新都会触发溢出检测,可以加 debounce 防抖优化

但这些都不影响它成为一个设计思路出色的组件。好的组件不是完美的组件,而是在核心问题上给出了清晰且正确的答案的组件。


九、总结

OverEllipsis 的 120 行代码,核心在做一件事:让 UI 的每一个元素都“说实话”

  • 文本真的溢出了,tooltip 才出现
  • 每一条可能导致溢出的变化,都有一条对应的监听路径
  • 不需要 tooltip 的时候,它彻底不存在,不是“存在但无内容”

这不是炫技,这是一种对用户注意力的尊重。每一个不必要的 tooltip、多余的滚动条阴影、不该出现的“展开更多”,都在微量地消耗用户的认知资源。消除这些“微小的不诚实”,是前端工程师能够给予产品的最细腻的关怀。

来源:https://juejin.cn/post/7656380711865155636

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
如何用拖放在Flask看板应用中更新数据库状态

如何用拖放在Flask看板应用中更新数据库状态

在Flask看板应用中,通过HTML5拖放API结合前端fetch异步POST请求,将拖拽任务的task_id和target_state发送至后端路由;后端接收JSON后更新数据库并提交事务,实现跨列拖拽时实时更新状态。需注意将事件绑定到容器而非子元素,并在ondragover中阻止默认行为以确保drop触发。

时间:2026-07-09 07:01
如何用CSS :has()选择器快速实现悬停span显示相邻div

如何用CSS :has()选择器快速实现悬停span显示相邻div

利用CSS的:has()伪类选择器,通过` bar:has(> baz:hover)+ qux`实现悬停内联元素时显示相邻块级容器,解决了传统相邻兄弟选择器无法跨层级匹配的局限,可用于悬停图标显示详情框等交互场景。需注意浏览器兼容性和性能优化,避免过度使用影响渲染效率。

时间:2026-07-09 07:01
display:block居中段落文本的响应式实现方法

display:block居中段落文本的响应式实现方法

通过`display:block`与`max-width`限制宽度,配合`margin:auto`使段落块水平居中并实现响应式布局;同时用CSS统一控制段落间距,避免脱离文档流,提升可读性与维护性。

时间:2026-07-09 07:01
JavaScript代码去重:高效避免重复编写的技巧

JavaScript代码去重:高效避免重复编写的技巧

将重复代码中的变化点提取为参数,利用ES6模板字符串动态生成选择器,可消除重复、保持一致性。注意参数安全性,必要时进行白名单校验;批量化场景可扩展为数组形式;现代框架中更推荐数据驱动或组件化封装替代手动选择器。

时间:2026-07-09 07:00
使用容器查询单位cqw实现表单输入框相对于祖父容器百分比宽度

使用容器查询单位cqw实现表单输入框相对于祖父容器百分比宽度

利用CSS容器查询单位cqw,表单输入框宽度可直接基于祖父容器计算,绕过中间包装器尺寸干扰,实现简洁响应式布局,无需硬编码或JavaScript,显著降低维护成本,提升嵌套结构下的布局灵活性。

时间:2026-07-09 07:00
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜