渲染前计算 HTML 元素宽度
我想向你展示一个小技巧,让你在元素渲染到屏幕上之前就能知道它的大小。
这个技巧有很多用处。在过去,开发者不得不更多地使用这个技巧,那时我们还没有 flex 和 CSS grid 之类的东西,元素也无法按照我们想要的方式自行调整大小。
有时你必须计算元素的大小并手动设置宽度。
我已经很久没用过这个技巧了。但最近在一个项目中偶然发现的一个小故事让我想起了这一点。我尝试了所有其他的 CSS 技巧,也问过同事,但都没有找到可行的解决方案。
在经历了一番折腾之后,我想到了这个办法。
那么问题是什么呢?
我需要构建一个可以显示两行文本的小组件。这样,当用户点击“阅读更多”按钮时,它就会展开并显示所有文本。
这听起来一点也不复杂,不是吗?只需显示 X 个单词或 Y 个字符即可。你在抱怨什么?
设计师想要显示两行文本,并在第二行末尾显示“阅读更多”按钮。就像这样:
我们以为这只是个小任务,我想我们甚至懒得去玩弄它。它只是个大故事里的一件小事。
如果我们把按钮放在下一行,我就不会写这篇文章了。我只需要检查一下行高,把文本元素的溢出设置为隐藏,就完事了。
但按钮真的、真的必须放在第二行的末尾。设计师,对吧?对吧?
你无法用CSS解决这个问题。我最初想到的是使用 float: right; ,
但我仍然需要知道元素应该放在哪里才能浮动。把它放在文本末尾会隐藏按钮元素。
不管怎样,我们需要找到一种方法来知道这一行可以容纳多少个单词,并且还要有足够的空间来容纳按钮。
好的,那么解决方案是什么?
要想知道这两行能写多少个单词,最简单的方法就是一次写一个单词,看看是否合适。写到两行之后就停下来。很简单。
我们创建一个临时元素,并将其附加到将包含实际文本的元素上。由于它是一个子元素,它将继承原始文本元素的所有样式,因此所有文本都将具有正确的字体大小、行高等。
我们逐字填充该元素,看看这些单词是否适合我们的两行(以及按钮)。当超过两行时,我们停止填充。一旦获得正确的文本量,我们就可以移除临时元素。
现在我们已经有了合适的文本量,我们将这部分文本复制到屏幕上可见的原始文本元素中。按钮将位于其后,正如我们计划的那样。
我们的函数看起来是这样的:
const createMaxLines = () => {
// create the temporary Element
const ruler = document.createElement('div');
ruler.style.width = 'auto';
ruler.style.position = 'absolute';
ruler.style.whiteSpace = 'nowrap';
// Adding the element as a child to myElement.
// it will be added to the DOM
myElement.appendChild(ruler);
/**
* Do the calculations you need to do
*/
// clean up after yourself
myElement.removeChild(ruler);
};
这不会导致你的屏幕出现奇怪的闪烁吗?
你可能会这么想。我们正在创建一个元素。我们正在将它添加到 DOM 中。这就是为什么我在第一个版本中用 CSS 将临时元素隐藏的原因。
但是……检查哪些文本应该在屏幕上显示的整个函数是同步的。并且发生了一些事情。
在我解释之前,我们先来了解一下浏览器中渲染引擎的工作流程。
元素显示在屏幕上之前,需要经过几个步骤。
我不会在这里详尽地讲解,因为这个话题太宏大了。但如果你想更深入地了解渲染过程,你一定要读一读Tali Garsiel 和 Paul Irish 的这篇文章。虽然有些老了,但仍然很棒。
首先会创建 DOM 树,其中包含所有 HTML 标签。此外,CSS 也会在这样的树中进行解析。
这两者在渲染树中组合,其中样式和元素被组合在一起。
下一步是布局或重排,所有元素都会在此获得其位置。
最后是绘制阶段,元素将在此显示在屏幕上。
现在,每次将元素添加到 DOM 中(例如在我们的函数中),都需要在布局/重排阶段重新计算所有元素的位置。当该阶段完成后,屏幕将被重新绘制。
正如我所说,请阅读上面提到的文章以了解详细信息,我在这里描述的只是一种过于简化的描述。
一旦我们的临时元素添加到 DOM,它就会触发渲染引擎的重排。
现在,每当一个单词添加到元素中,就会触发另一次重排。但是……不是重绘。重绘将在函数末尾所有计算完成后进行。这是重要的部分,因为正是重绘让所有内容显示在屏幕上。但是在函数末尾,我们将从 DOM 中移除临时元素,再次引发重排。只有在重排之后,渲染引擎的绘制部分才会运行。由于我们的临时元素不再位于 DOM 中,因此它将不会显示在屏幕上。
性能怎么样?
你不应该尝试用《战争与和平》的全部内容,但这种方法通常只适用于几行文本,应该没问题。
你可以使用更好的算法来确定需要多少个单词才能匹配,这样性能可能会有所提升。
结论
如果您需要在元素显示到屏幕上之前计算其大小,这是一个不错的小技巧。
您不需要太多时间,因为现在大多数情况下,您都可以用 CSS 来解决。但在 CSS 无法帮到您的少数情况下,这个技巧或许能帮上忙。
如果您在自己的项目中使用了它,希望能够收到您的反馈。
我为它创建了一个小型的反应组件,所以如果你好奇的话,你可以在这里找到代码和示例
文章来源:https://dev.to/sstraatemans/calculate-html-element-width-before-render-4ii7