假设,DOM 被设置了 height:20px 和 overflow:hidden,如何计算它的真实高度呢?
一、问题背景
最近在优化折叠组件,需要在窗口宽度变化的时候重新判断展开收起状态。如果段落高度大于给定高度,则隐藏超高内容,展示【展开】按钮。如果段落高度小于给定高度,则不限制最大高度,隐藏【展开】按钮。
但如何计算【段落高度小于给定高度】呢?比如,段落被设置了 height="20px",如何计算它的真实高度呢?
二、干货,计算元素真实高度函数
方法如下,直接传入要计算的目标 dom 就好了。
function getHeightUnfold (dom) {
var fakeNode = dom.cloneNode(true);
fakeNode.style.position = 'absolute';
// 先插入再改样式,以防元素属性在createdCallback中被添加覆盖
dom.parentNode.insertBefore(fakeNode, dom);
fakeNode.style.height = 'auto';
fakeNode.style.visibility = 'hidden';
var height = fakeNode.getBoundingClientRect().height;
dom.parentNode.removeChild(fakeNode);
return height;
}
这个方法的核心是,创建一个不可见元素,摘除高度限制,最终计算它的高度。
三、发散思考
1. 复制元素的必要性
Jenny_L 给复制出来的元素增加了 postiion: absolute
属性,为了不触发后面元素的重拍重绘,节省浏览器资源。如果直接快速地给目标元素设置 height: auto
+ 获取高度 + height: 20px
,虽然能达到目的,但会造成所有后续元素的(不一定可见)抖动,尽量避免。
2. Node.cloneNode 与 document.createElement('div') 的选择
后者与innerHTML
配合使用,虽然能够模仿目标元素的内层内容,但不能继承目标元素的样式。即使使用document.createElement(dom.nodeName)
也会有问题,不能继承内联样式。而使用cloneNode
不但可以继承 class,css,还能触发 createdCallback(如果有的话),继承 js 中添加的内联样式。
3. fakeNode.getBoundingClientRect().height 与 getComputedStyle(fakeNode).height 的选择
都是计算高度的,但前者计算的是占位高度,包括 padding+border;后者计算的是单纯高度,经过多层 css 优先级竞争之后的 height 取值(px),获取纯数值还需要parseInt()
。本次情况,需要计算占位高度,所以选择getBoundingClientRect()
4. removeChild 的必要性
虽然 fakeNode 不可见,但终究在文档流中,display 不是 none,重拍的时候会参与计算。除此之外,如果原先 dom 带有 id="someID" 的话,删除 fakeNode 之前,文档中就会存在两个 id="someID" 的元素。未来浏览器再做选择的时候,就懵逼了。
四、自勉
好久不写文章了,草稿箱里存了好多代码片段,要加油了。
不得不说,这种小代码片段还是很有分享价值的,一次研究(竟然花了一个小时),未来处处复制,走向人生巅峰。