Let’s imagine we have some some user provided text that we want to clip and render a show more button when it’s longer than some length n.

Solutions

.slice(0, n)

The first thought might be to slice the string, but this doesn’t play well with unicode:

"The Black Pearl sailed the mighty seas 🏴‍☠️".slice(0, 40)
> 'The Black Pearl sailed the mighty seas \ud83c'

So it’s out.

max-height

We could set the max-height on our text to some px value that looks good, but then we’d need to make sure we work in a ratio of our font height. Anyways, we could make it work, but there’s an easier way!

-webkit-line-clamp

A nifty property that, despite its prefix, is supported by most browsers.

Usage is pretty straight forward:

.trimmed-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}
<div class="trimmed-text">Lorem ipsum dolor etc.</div>

Which looks something like this:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas magna purus, sollicitudin ut pellentesque vitae, posuere nec lectus. Sed tempus diam massa, vel luctus libero venenatis at. Integer convallis aliquet mauris. Sed nulla justo, ultrices id purus eu, hendrerit rhoncus nulla. Praesent semper eros vitae diam facilisis efficitur.

Looks great but we’re missing the show more button.

Straightfoward enough, a few extra ids and an onclick and we’re on our way:

function handleClick() {
  const button = document.getElementById("toggle-button")
  const text = document.getElementById("text")
  if (showMoreButton.textContent === "show more") {
    text.classList.remove("trimmed-text")
    button.textContent = "show less"
  } else {
    text.classList.add("trimmed-text")
    button.textContent = "show more"
  }
}

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas magna purus, sollicitudin ut pellentesque vitae, posuere nec lectus. Sed tempus diam massa, vel luctus libero venenatis at. Integer convallis aliquet mauris. Sed nulla justo, ultrices id purus eu, hendrerit rhoncus nulla. Praesent semper eros vitae diam facilisis efficitur.

Also, we only want to show the show more button if the text is actually being clipped. With some extra JS we can hide/show the button appropriately:

const hasOverflow = (el: HTMLElement) => el.scrollHeight > el.clientHeight

// in some on load function & whenever the width of the window changes
const button = document.getElementById("toggle-button")
const text = document.getElementById("text")
if (hasOverflow(text)) {
  button.style.display = "block"
} else {
  button.style.display = "none"
}

Prior Art

YouTube uses -webkit-line-clamp for their comments, but they don’t recalculate the clipping when the window size changes.

This causes a couple of bugs.

  1. If you open a YouTube video at a desktop size viewport, so clipping doesn’t occur, then shrink the width of the page, the text will clip without the show more button appearing, meaning there is no way to see the rest of the comment.

  2. The reverse can happen when opening at a small viewport and expanding to a large viewport. The show more button is visible on the desktop size, but the text isn’t being clipped.

NYT Cooking uses the max-height setup for the recipe descriptions in a mobile browser. They also use some JS & media queries to ensure the More + button doesn’t show on wide viewports.