It’s been a long struggle for the designers at Behance. Every time there is a long block of text, the struggle is to balance the amount you display versus the amount of screen real estate you’re willing to give up. Often their designs will call for the text to be truncated to fill a certain number of lines. If you’ve ever tried to do this in CSS you’ll know that you can’t.
CSS is unfortunately limited in the truncation department. i.e. You can only
truncate a single line of content with
text-overflow: ellipsis;. There have
been experiments with ellipses, but sadly they’re either all
browser-specific, or in the best case I’ve found, require precise
measurement of line heights or else the text gets cut off mid-way. (Or my
peeve, when descenders get cut off or ascenders peek through.)
Much chagrin was had
Our solution accepted the reality that we’re never going to have a perfect multi-line ellpsis, and focused on the bit we deemed important:
- Can’t be ultra slow
- Should work for arbitrary amount of text
- As orthogonal to CSS styling as possible
The last point is probably most important, since the whole idea is to get pretty-looking truncated text; which means any font face at any size with any line height. The naive solution was just to take a block of text, then character-by-character lop off the end, attach the ellipsis, and measure how much space that took. Rinse and repeat as necessary.
This approach is definitely valid and would work, but isn’t very scalable as long blocks of text will spend forever in the loop of cut-and-measure when perhaps all you want is the first three lines. We want to reduce where we start cutting from with a binary search with the help of HTML5 Range.
At the end of the binary search, we end up with a
length equal to the exact
character beyond which the text overflows. From then on, the naive approach is
necessary in order to add an extra ellipsis character and still not break text
There we have it, JS aided multi-line truncated ellipses. Try saying that 5
times fast. It’s not a perfect solution, since it’s a content-enforced
truncation. Container resizes will never give you back more of the text that
was cut. In addition, it only operates on
TextNodes. Even still it is useful
enough to be relied on.
In our usages, each truncation causes only 1 or 2 reflows as the
ellipsis is added. The majority of the JS does not modify the DOM at all to
measure the text height, thanks to
Range. The full code lives in our
front-end framework library, BeFF (Behance Front-end
Framework) as dom/truncate.
Try it out yourself!