Joshua Bitton
Joshua Bitton

Reputation: 403

Problems with Replace overflowing string characters with three dots

So ive seen some solutions on stackoverflow how to do this, but the solutions are for a static amount of characters, for example

if (string.length > 20) {
var shortstring= name.substring(0, 20) + " ...";
}

But this is for only 20 characters, I'd like to be able to do it if an element is overflowing out of a container, then replace those characters with three dots.

For example,

asadasddadasadsssssssssssssssssssssssssssssssssssdddddddddddddddddddddddd

Upvotes: 2

Views: 1748

Answers (2)

FZs
FZs

Reputation: 18639

As it is already mentioned in the comments, it's a much easier task to do in CSS.

It has a text-overflow property, that's invented just for that. When it is set to ellipsis, it displays a character at the end of the visible area.

The spec also allows specifying a custom overflow string (e.g. ' ...'), however, this is only supported by Firefox.

It can be used like this:

.overflow{
  border: 1px solid black;
  width: 300px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
<div class="overflow" >Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque at enim eget purus tincidunt tincidunt non at orci. Pellentesque urna.</div>

Upvotes: 0

Tom
Tom

Reputation: 9167

The only way to determine whether a piece of text will overflow a layout container is to actually render the text in that container and then measure its geometry. (This is not true when drawing text in <canvas>, but I believe that's because canvas text does not care about cascading styles.)

The reason you see so many solutions driven by character-count is because it's simpler, much less computationally expensive (and thus faster), and because the dimensions of any text element are very obviously a function of the amount of text, (among other things -- and there's the rub), so people generally treat it as a quick-and-dirty proxy for actual render dimensions. There's a saying: "sometimes a little inaccuracy can save a ton of explanation." That's what's going on here.

But this is StackOverflow, so let's splurge on explanation. Buckle up. If you want to do this right, here is how you would do it:

  1. Construct a text node with the desired text, and wrap it in some element that you know will have no impact on its layout within the target container.

This requires a deep knowledge of the styling context on your web page; maybe in your page <span> within .Column2 is styled such that it will auto-fit to its content, but on my website every element within the target zone receives 2px padding because that's what my layout requires. These details impact the render size.

  1. Additionally, set the opacity to 0 on your new text element.

Do this before you add the element to the target container, or you could get flicker.

  1. Insert the text element into the target container.
  2. Measure the text element, and the container, using element.getBoundingClientRect.
  3. Do math on those two rects.

If the text element's width or height are bigger than the container, you've got overflow.

  1. If the text overflows, chop off part of the string and re-measure.

This can repeat many, many times, and DOM manipulation is one of the most-expensive things a browser can do (relatively speaking). This means it's not wise to simply remove one character at a time -- if the text is very much longer than the space allows, your loop could repeat thousands of times, which users will notice, especially if you apply this to more than one chunk of text on the page; if triggered at page load, this would be happening simultaneously to many elements, aka DOM thrashing: RIP your webpage.

That means you need a more-efficient search algorithm. I think the one that's best for this is binary search.

  1. Once you have figured out exactly which characters fit, remove the custom opacity:0 so the text becomes visible. Now you are done.

You can probably add a few clever optimizations to that algorithm. For example, to protect against huge chunks of text being crammed into tiny containers, you could create an empty 1em square element and use that to come up with an upper-bound for the number of characters that will fit. So that 10k sample might get cut down to just 175 as the first step, and then use the guess-and-test process to get from there to whatever is the final value. You could put this special short-circuiting logic inside a guard that only executes if the text is very long.

Or you might cache the final character counts in the browser's localStorage such that refreshing the page with the same content could just read the already-determined counts and render the right amount of text in one step. I suspect the major challenge there would be devising an organizing scheme for the cache data, because the "coordinates" of a cache entry need to include the full starting text and the target container, and you'd want to invalidate the cache if the text or page styling changes.


Many years ago, I used a library called three-dots.js to handle this. I always thought it used binary search, but skimming the source now, that doesn't seem to be the case. Still, it illustrates how complex this is. That is why most people go with character counts: a proper solution is surprisingly complex and much less performant.

You may be able to find a good library on NPM, or use three-dots.js. You can write your own, but unless you have an unlimited amount of time for this, you'd do well to ask yourself if it's really that important to get this exactly perfectly right in every single case. It's a lot easier, and probably fine, to just chop the text conservatively based on character count and manual measurements you take using DevTools.

Upvotes: 1

Related Questions