将div的高度设置为行高的倍数

42
如何在纯CSS中将DIV的高度表达为其行高的倍数,而不硬编码行高或字体大小?
背景:
我有一个包含状态文本的框(DIV)。文本有时会跨越多行,通常只需要一行。状态每秒更新几次(五次),这会导致底部边框和框下面的所有内容上下“跳动”,因为框会更改大小以适应其内容。
解决跳动问题的一个方法是将DIV的最小高度设置为两行的高度。超过两行的状态文本很少见,因此那种程度的跳动是可以接受的。
或者,我可以将高度设置为恰好两行的高度,并使用overflow: hidden截断文本。这将消除所有跳动,但会牺牲一些信息。
这引出了一个问题:如何将高度表达为行高的倍数?状态文本从全局样式表中继承其外观,不知道字体大小、家族、行高或任何内容。
半个解决方案:
我可以使用em单位来表示高度,而不需要了解字体的任何信息,但这仍然涉及到影响最终行高度的行高属性。
理想情况下,我希望继承行高度,但如果我覆盖它到已知的倍数,则可以强制框变为恰好两行高。
.progressStatus
{
    line-height: 1.1;
    height: 2.2em;
}
这个解决方案对我的需求来说已经足够好了,但我很好奇是否有更好的解决方案,而且不需要使用javascript。

这个解决方案对我的需求来说已经足够好了,但我很好奇是否有更好的解决方案,而且不需要使用javascript。


2
不要以为你可以在没有像 LESS 这样的东西的情况下做到那一点。即使有,也可能不是100%动态的。 - James Montagne
7
对我来说,这看起来是一个很好的解决方案。如果你能找到更好的解决方案,我会感到惊讶。 - Brian Nickel
与 overflow: hidden; 相反,您可以尝试使用 ellipsis .truncate { width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } - sheriffderek
加入一些 CSS 过渡效果来缓解跳动问题怎么样?没有可视化界面,我很难理解这个问题。嗯嗯。 - sheriffderek
6个回答

12

在 CSS 中没有办法表达这个意思。

  

状态文本继承其外观自全局样式表,并不知道字体大小、字体族、行高或其他任何东西。

这并不完全正确。因为通过层叠,状态 div 中的文本继承了其 font-sizeline-height 等值,所以它“知道”这些值,并会相应地进行样式设置。问题在于 CSS 并没有提供一种使用这些值进行计算的方法。它们仅在声明新属性时被隐含考虑。

唯一实现你想要的结果的方法是通过 JavaScript。我在这里做了一个 fiddle (链接),使用了一些 jQuery。在我的示例中,简单的 body 声明充当祖先元素。在 body 的 CSS 中运行不同的 font-sizeline-height 值。

在实践中,我会将此与你的方法结合起来,作为以下情况的备选方案:

  1. JavaScript 被禁用
  2. 相关祖先元素的 line-height 给定为百分比值(后代元素继承了 计算出来的 值)并且你决定更改状态 font-size。例如:祖先元素的 font-size16pxline-height120%(约为 19px)。现在,如果你决定你的状态需要更多的关注,并声明 .progressStatus {font-size: 24px;},它将继承计算出来的 line-height(仍然是 19px)。因此,你会得到一个行高小于文本大小的行高。像你的“半解决方案”一样明确地声明一个 line-height 可以避免这种情况发生。

这个答案是不正确的。只需要使用CSS就可以完成。请参考这个答案 - petern0691

7
div {
  height: 1em; // that's one line, 2em for 2 lines, etc...
  line-height: 1em; // the height of one text line
  overflow: hidden;
}

4
这并没有回答问题,因为你强制行高为1em,而实际上它可以是1.1,就像上面的例子一样。 - Shachar Har-Shuv

4

或许使用“共享”变量能够为您提供足够接近的方案,以实现所期望的“继承行高”。声明一个变量可以避免两次定义行高。按照您的例子,这个解决方案设置了一个固定的高度为2.2em1.1 * 2em = 2.2em = 2行):

.progressStatus
{
    height: calc(var(--line-height) * 2em);
    line-height: var(--line-height);
    --line-height: 1.1;
}

2em 更改为 3em 可以使用 3 行高度 (1.1 * 3em = 3.3em)。

如果有帮助的话,变量 -line-height 也可以在 DOM 层次结构中更高的位置定义,因为变量在所有嵌套元素中都可见,直到被覆盖为止。


4
对于那些喜欢了解未来可能出现的情况的人,CSS Values and Units Module Level 4提出了lh相对长度单位

等于在其使用的元素上计算出的'line-height'属性值,通过仅使用第一个可用字体的度量将'normal'转换为绝对长度。

该文档的当前状态显示在页面标题下方。在撰写本文时,它处于“工作草案阶段”working draft stage。您可以在此处找到与css-values-4相关的所有开放GitHub问题的列表
示例代码:
.progressStatus {
    line-height: 1.1;
    height: 42lh;
}

由于动机是使 div 内部的文本不调整大小,您可能还想更改 overflow 属性的值,并确保 box-sizing 的计算值没有被其他规则无意地设置为 border-box

2

我来晚了,但如果我正确地理解了您的需求,完全可以使用内联HTML样式或CSS实现您想要的功能,但可能不是您首选的方式。我提供两个可能的解决方案。

正如您和其他人指出的那样,需要一些基本属性值是无法访问的。然而,它们显然对浏览器是可用的,因为它必须进行计算以使块足够大以适应内容等。

只需要少量CSS。

这里提供的HTML总长度相对较长,但这仅因为它包括两个解决方案和九个进度状态文本示例。任何解决方案都只需要三个DOM元素。

我认为我已经满足了您的请求,两种解决方案的方法中的技巧是以一种可以隐式或显式访问风格属性的方式让浏览器进行计算的结果。

在这种情况下,它是包含两行文本的块的高度。

所以:

  1. 创建两行文本,其中包含“&nbsp;<br>&nbsp;”
  2. 将其放入一个块中,该块在此处为两行高
  3. 将该块放入容器块中
  4. 容器块在此处为两行高,即其最小高度已被隐式设置为两行
  5. 在容器内放置另一个块,即进度状态块

第一种解决方案是“内联”进度状态块。

第二种解决方案是“定位”进度状态块。

在这两种解决方案中,进度状态块可以根据需要在一行或两行文本之间跳转,而不会影响容器外的任何内容。

这两种解决方案中的第一种“内联”允许三行或更多行的文本扩展容器。这是因为容器的计算高度取决于其内容的流动,最终取决于进度状态文本,因此无法将最大高度或高度设置为值,因为当容器的高度可访问时,浏览器已经从初始的两行更改了计算高度。如果偶尔出现额外的流量不是问题,则一切正常。但是,您无法修剪否则会溢出的内容,因为所需高度不可访问。

第一种解决方案使用了三个CSS类,后缀为“Inline”。

第二种解决方案是位置(Position),通过使用绝对定位将进度状态块的内容流与其容器分离,使容器高度可访问。这意味着进度状态块的内容不再影响其容器(即父级)的大小。因此,进度状态块的最大高度或高度可以设置为其父级的高度,一旦分离,就不会重新计算(在这种情况下),而在这种情况下是两行。但是,溢出必须被隐藏,否则它将跨越容器的边缘和其他任何阻挡它的东西。请注意,背景受高度设置的限制,不会跟随溢出文本。

第二种解决方案使用了三个CSS类,后缀为“Position”。

任何一种解决方案都可以轻松调整到超过两行;只需为每个额外的行添加“<br>&nbsp;”即可。

您还可以像位置(Position)解决方案一样,向内联(Inline)解决方案添加“visibility: hidden;”,以防止屏幕阅读器被无关紧要的内容分散注意力。

为了在位置解决方案中使用背景,同时填充一个文本行的容器,请将 CSS 类中的 max-height 更改为 height。对于内联解决方案,请将进度状态块中的背景移动到容器块 CSS 类中。

我已经在 Windows 上的 Chrome 和 Android 上的 Chrome 上尝试了这两种解决方案。这是非常基本的 CSS 和 HTML,所以应该可以普遍适用吧?

.progressStatusContainerInline {
  border: 3px solid black;
}

.setHeightTwoLinesInline {
  display: inline-block;
  width: 0%;
}

.progressStatusInline {
  display: inline-block;
  background: pink;
  width: 100%;
}

.progressStatusContainerPosition {
  position: relative;
  border: 3px solid black;
}

.setHeightTwoLinesPosition {
  visibility: hidden;
}

.progressStatusPosition {
  position: absolute;
  top: 0px;
  left: 0px;
  background: pink;
  width: 100%;
  max-height: 100%;
  overflow: hidden;
}
Inline examples with:
<ul>
<li>default line height</li>
<li>single line of text at bottom of status box</li>
<li>too much text expands status box</li>
</ul>
<div style="width: 200px;">
<div class="progressStatusContainerInline">
<div class="setHeightTwoLinesInline">&nbsp;<br>&nbsp;
</div><div class="progressStatusInline">
Progress status text line going to two lines.
</div>
</div>
<br>
<div class="progressStatusContainerInline">
<div class="setHeightTwoLinesInline">&nbsp;<br>&nbsp;
</div><div class="progressStatusInline">
Short status text.
</div>
</div>
<br>
<div class="progressStatusContainerInline">
<div class="setHeightTwoLinesInline">&nbsp;<br>&nbsp;
</div><div class="progressStatusInline">
Much much much much much much longer status text written here.
</div>
</div>
</div>
<br>

Position examples with:
<ul>
<li>line height 3.5</li>
<li>single line of text at top of status box</li>
<li>too much text is truncated</li>
</ul>
<div style="line-height: 3.5; width: 200px;">
<div class="progressStatusContainerPosition">
<div class="setHeightTwoLinesPosition">&nbsp;<br>&nbsp;
</div><div class="progressStatusPosition">
Progress status text line going to two lines.
</div>
</div>
<br>
<div class="progressStatusContainerPosition">
<div class="setHeightTwoLinesPosition">&nbsp;<br>&nbsp;
</div><div class="progressStatusPosition">
Short status text.
</div>
</div>
<br>
<div class="progressStatusContainerPosition">
<div class="setHeightTwoLinesPosition">&nbsp;<br>&nbsp;
</div><div class="progressStatusPosition">
Much much much much much much longer status text written here.
</div>
</div>
</div>
<br>

Position examples with:
<ul>
<li>line height 1.4</li>
<li>single line of text at top of status box</li>
<li>too much text is truncated</li>
</ul>
<div style="line-height: 1.4; width: 200px;">
<div class="progressStatusContainerPosition">
<div class="setHeightTwoLinesPosition">&nbsp;<br>&nbsp;
</div><div class="progressStatusPosition">
Progress status text line going to two lines.
</div>
</div>
<br>
<div class="progressStatusContainerPosition">
<div class="setHeightTwoLinesPosition">&nbsp;<br>&nbsp;
</div><div class="progressStatusPosition">
Short status text.
</div>
</div>
<br>
<div class="progressStatusContainerPosition">
<div class="setHeightTwoLinesPosition">&nbsp;<br>&nbsp;
</div><div class="progressStatusPosition">
Much much much much much much longer status text written here.
</div>
</div>
</div>


很棒的答案,在我的使用情况下有效。希望CSS本地支持这个,但总体来说,这是我见过/使用过的较少的把戏之一。 - Asad-ullah Khan

1
你可以在 div 上使用这些。
.your-div {
  line-height: 1.1em;
  max-height: 2.2em;
  overflow: hidden;
  white-space: normal;
  -webkit-line-clamp: 2;
  text-overflow: ellipsis;
}

当文本超过两行时,这将导致显示为三个点(...)的文本结束。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接