前言
最开始要做这个需求的时候我还在想, 这不是都快烂大街的功能吗, 直接在网上抄一段代码下来就完事了. 搞完就走, 今天绝对能早点下班.
第一次尝试
简单搜了一圈, 感觉使用 css 自动超出省略, 然后用当前文本节点的 offsetWidth
和 scrollWidth
来判断是否文本超出并依此展示全量文本的 tooltip 挺靠谱的. 下面是示例代码:
文本样式
css
.span{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
js 判断当前节点是否超出省略代码 (VUE版本)
javascript
const span = this.$refs.span // 根据 ref 获得当前节点
this.showTooltip = span.clientWidth < span.scrollWidth // 将是否展示 tooltip 放到页面变量里控制
简单试了一下, 妥, 再多测试测试应该就能上线下班了. 后面随即又找了一下实际的案例, 但没想到这么简单的代码这么快就翻了车.
问题案例: 当时有一个测试是一个订单号, css 上已经自动展示了省略号, 但是本该自动展示的 tooltip 却没有动静. 打印看了下 offsetWidth
和 scrollWidth
, 发现都是 140. 感觉事情不对, 又赶紧用检查元素看了下实际的 DOM 节点, 好家伙, 原来真实的宽度是 140.17. 当时的我还没有意识到问题的严重性, 想着应该是 scrollWidth
被自动四舍五入了, 那就把节点信息都打印出来看看有没有能保留小数的信息. 直到我把属性从头看到尾这才眉头一皱, 完蛋没有.
赶紧翻了下 MDN 上关于 scrollWidth
的定义( 快速跳转 ), 发现确实有这么条备注:
- 这个属性会进行四舍五入并返回整数,如果你需要小数形式的值,使用
element.getBoundingClientRect()
. - 在实际测试过程中,谷歌获取的
Element.scrollWidth
和 IE,火狐下获取的Element.scrollWidth
并不相同
看来这个班没那么容易下.
第二次尝试
还好当时网上找方案的时候多翻了几条, 记得之前有个被我 pass 的有一个嫌费劲的使用 getBoundingClientRect
的方案, 原来费劲是有原因的(大雾).但我还是有些疑惑, 按理说基本组件库应该都有这个需求, 那他们是用的什么方案, 赶紧看看免得又走弯路. 后来翻了两个知名的组件库, 发现一个就是用的 getBoundingClientRect
, 还有一个是直接用的 scrollWidth
... 好吧, 那就改.
根据网上资料修改了下方案, 修改后的 js 代码如下:
javascript
const span = this.$refs.span
const el = span.$el
this.showTooltip = span.clientWidth < el.getBoundingClientRect().width
本地跑了下, 不错测试用例全都能通过. 看来今晚只比预想中要晚走那么一点, 还是可以的. 想罢就把代码打包一份, 用本地代理的方式测了下上线后的效果. 测完之后蒙了, 随便挑几个测试都不行, 这效果怎么跟本地跑的区别这么大. 后来赶紧上 MDN 查了下 element.getBoundingClientRect()
这个 api(快速跳转), 发现好像也没什么蛛丝马迹, 然后又多方搜集了一下, 发现下面这些信息:
- CSS 变换 :如果元素应用了 CSS 变换(如
scale
,rotate
,skew
等),这些变换可能会影响元素的实际显示大小,而getBoundingClientRect()
返回的是应用变换后的元素尺寸。 - 子像素渲染 :浏览器在渲染时可能会使用子像素来优化显示效果,这意味着实际的渲染可能涉及到像素的小数部分。而
getBoundingClientRect()
返回的是四舍五入后的整数值,这可能导致轻微的不准确。 - 视口缩放 :如果用户缩放了网页(如通过手势或浏览器控制),
getBoundingClientRect()
返回的尺寸值将是缩放后的值,可能不反映原始的像素值。 - 滚动条宽度 :在某些情况下,如果元素内部有滚动条,滚动条的宽度可能不会被包含在
getBoundingClientRect()
返回的宽度中。 - 边框宽度 :
getBoundingClientRect()
返回的宽度包括了元素的边框(border),如果你的计算没有考虑到边框宽度,可能会认为测量结果不准确。
好吧, 我就知道这个班没那么好下! 赶紧又去找是否有解决方案, 网上搜了一圈, 又回温了下几个知名组件库的源码, 发现大家都选择性无视这个问题了, 这可难办了, 冥思苦想了半天还是没有头绪, 突然想起来刚才找资料时候发现的一个很离谱的方案.
第三次尝试(绝对是最后一次了!)
这个方案我看第一眼就把它 pass 了, 没想到最后兜兜转转还是只有它. 核心的意思是将目标文本节点超出省略后, 再放一个隐藏的同样文本节点用于计算它的实际宽度. 这样之前取不到高精度宽度值, 和模拟计算出来的宽度会被其他很多条件影响的问题就能迎刃而解了, 但是确实感觉很 low. 犹豫了很久, 又找了一些其他文章寻找思路, 但是最后仍然没有头绪. 看了下时钟, 罢了, 就它吧.
隐藏文本节点样式
css
.copy_span_node{
display: inline-block;
position: fixed; // 用于避免隐藏文本节点的宽度撑开了父容器的宽度, 导致父容器多一大截空白
overflow: visible;
whitespace: nowrap;
visibility: hidden;
}
js 判断当前节点是否超出省略代码 (VUE版本)
javascript
const span = this.$refs.span
const span_copy = this.$refs.span_copy
this.showTooltip = span.clientWidth < Number.parseFloat(window.getComputedStyle(span_copy).width, 10)
本地和线上测试全部通过, 下班!
感谢
主要参考了两篇文章, 非常感谢.