Skip to content

Commit be23d82

Browse files
committed
fix(clamp): measure real line height so -webkit-line-clamp works without line-height
usedLineHeightPx guessed the line box as font-size*1.2 (or the raw CSS value). But `line-height: normal` is font-metric dependent, and inside a -webkit-box the line box never shrinks below the font strut, so e.g. line-height:18px on 20px text still lays out at ~20px. The guessed targetH under-counted the lines and the clamp truncated to too few lines (or dropped the ellipsis entirely). Measure the real per-line height from the live element (scrollHeight of a single line) instead of guessing. Fixes #443
1 parent 0a818bf commit be23d82

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

__tests__/module.lineClamp.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,26 @@ describe('lineClamp', () => {
5252
expect(div.textContent).toBe(longText)
5353
})
5454

55+
it('clamps to the right line count when line-height is below the font strut (#443)', () => {
56+
// In a -webkit-box the line box never shrinks below the font metrics, so a
57+
// line-height smaller than the glyph height must not over-truncate. The clamp
58+
// must keep as much text as a comfortably-sized line-height with the same
59+
// width/font would (both fit the same 2 lines) — the old fs-based guess cut
60+
// this down to a single line.
61+
const make = (lineHeight) => {
62+
const div = document.createElement('div')
63+
div.style.cssText = `width:200px;font-size:20px;line-height:${lineHeight};word-break:break-word;display:-webkit-box;overflow:hidden;-webkit-line-clamp:2;-webkit-box-orient:vertical`
64+
div.textContent = 'Long Long Long Long Long Long Long Long Long Text.'
65+
document.body.appendChild(div)
66+
lineClamp(div)
67+
return div.textContent
68+
}
69+
const small = make('18px')
70+
const normal = make('24px')
71+
expect(small).toContain('…')
72+
expect(small).toBe(normal)
73+
})
74+
5575
it('returns no-op when content fits in N lines', () => {
5676
const div = document.createElement('div')
5777
div.style.webkitLineClamp = '5'

src/modules/lineClamp.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,23 @@ export function lineClamp(el) {
3737
if (!isPlainTextContainer(el)) return () => {}
3838

3939
const cs = getComputedStyle(el)
40-
const targetH = Math.round(usedLineHeightPx(cs) * lines + vpad(cs))
4140

4241
const original = el.textContent ?? ''
4342
// Guarda para restaurar
4443
const prevText = original
4544

45+
// Measure the REAL rendered line height instead of guessing from CSS.
46+
// `line-height: normal` is font-metric dependent, and inside a -webkit-box the
47+
// line box never shrinks below the font strut, so a value smaller than the
48+
// glyph height (e.g. line-height:18px on 20px text) still lays out taller. A
49+
// fs*1.2 / raw-CSS guess mis-sizes targetH and clamps to the wrong line count (#443).
50+
const pad = vpad(cs)
51+
el.textContent = 'X'
52+
const perLine = el.scrollHeight - pad
53+
el.textContent = original
54+
const lineH = perLine > 0 ? perLine : usedLineHeightPx(cs)
55+
const targetH = Math.round(lineH * lines + pad)
56+
4657
// Si ya entra completo en N líneas, no hacemos nada (igual que el clamp nativo)
4758
if (el.scrollHeight <= targetH + 0.5) {
4859
return () => {}

0 commit comments

Comments
 (0)