Line-Based Truncation Methods
Tags: javascript, css, tutorial
Published: 2018-04-25 03:29:09

---
title: Line-Based Truncation Methods
published: true
description: A walkthrough of a few methods for truncating text to a certain number of lines for any device or viewport size
tags: javascript, css, tutorial
---

I frequently get the request to truncate content excerpts on websites I build to a certain number of lines. While it sounds like a straightforward task, it’s difficult to implement for two main reasons: firstly, since we build websites to scale responsively to a variety of device sizes, we can’t assume that a content box will be the same width at any given screen size, and secondly, unless we’re using a monospace font, the number of lines any text will take up is dependent on the content, since in most fonts characters have different widths.

Let’s take, for example, this snippet of code:

```html
<article>
	<h3>This is a really long heading that we’re going to need to truncate for the sake of the layout since it goes on for several lines.</h3>
	<div class=“excerpt”>
		<p>Cras dolor orci, mollis eget mi vel, faucibus malesuada velit. Phasellus egestas nec nisi non bibendum. Vestibulum faucibus mauris ac ipsum viverra volutpat. Sed lobortis justo sapien, eget ornare orci convallis ut. Nullam pulvinar, urna at faucibus dignissim, orci elit varius quam, non sodales lacus elit a nunc. Morbi eleifend velit quis tellus tempus, sed vehicula neque vulputate. Vestibulum sit amet tempus nibh, sit amet semper mi. Integer sit amet quam eget ligula luctus pulvinar at non ligula. Suspendisse a fringilla lorem, cursus sodales odio. Aliquam ac odio eget nulla consectetur dictum eu quis massa. Sed volutpat ante non felis condimentum vestibulum. In tempor tristique varius. Nunc est nunc, tincidunt quis metus et, semper molestie eros. <a href=“#” class=“readmore”>Read More</a>
	</div>
</article>
```

## What are some approaches we can take?

### CSS-Based Clipping

A very simple solution would be to use CSS to set a maximum height for the container that the text is inside. If we know the line-height, we can multiply that by the number of lines we want to show to get the height the box should be to clip it properly.

```css
h3 {
	line-height: 26px;
	height: 52px;
	overflow: hidden;
}

.content {
	line-height: 24px;
	height: 100%;
	overflow: hidden;
	max-height: 72px;
}

.content p {
	margin: 0;
}
```

This solution requires no javascript and therefore is very good for performance. You could also add a tiny amount of javascript to be able to reveal the hidden content by setting the max-height of the .content container to a height much longer than any content in it might be, such as 9999999px, which is also friendly to transition animations.

However, if you need to include a “More” or “Continue” link at the end, or want to add an ellipsis to indicate that the text has been truncated, you’ll need something a little more robust, since this solution will hide the end of a segment that happens to be over the specified number of lines long.

**Pros:** Minimum performance change, no modification of markup needed
**Cons:** Can’t use readmore links or ellipses at the end of the text, specific to certain CSS selectors

### Javascript-Based Clipping

By using Javascript (and in this example, jQuery, although I’m sure you could write it without) to manipulate the HTML document, we can achieve more flexible results.

In this case, if we know, the line-height of the element and it remains constant, we can separate out any readmore links, split the text by spaces, and then iterate over each word until we find that the content is now taller than the box we want to fit it to. We should also store the original text in an attribute so that we can update the excerpt when the container size changes.

```javascript
$(window).on(‘resize load’, function() {
    $(‘.content p’).each(function() {
        var lineHeight = 16; // content's line-height
        var lines = 3; // number of lines to show
        // only truncate if the text is longer than the desired size; if not, skip it
        if ($(this).height() > lineHeight * lines) {
            if ($(this).find('a.readmore').length > 0) {
                    $(this).attr('data-link', $(this).find('a.readmore')); // if there's a readmore link, separate it out and put it on a data attribute
                    $(this).find('a.readmore').detach(); // remove the link from the HTML
            }
            if ($(this).attr('title')) {
                    $(this).text($(this).attr('title'));
            }
            $(this).attr('title', $(this).text().trim()); // set the text as the title attribute to preserve it
            $(this).text(""); // empty the content
            var str = "";
            var prevstr = "";
            var words = text.split(" "); // split text into words
            var link = this.attr('data-link');
            for (var i = 0; i < words.length; i++) {
                if (this.height() > lines * lineHeight) {
                    // if we've spilled over onto the next line, roll it back a word and break the loop
                    this.html(prevstr.trim() + "&hellip; " + (typeof link !== 'undefined' ? ' ' + link : ''));
                    break;
                }
                prevstr = str;
                // update the working string with the next word
                str += words[i] + " ";
                // update the content in the document
                this.html(str.trim() + "&hellip;" + (typeof link !== 'undefined' ? ' ' + link : ''));
            }
            // if the last iteration caused us to spill over a line, roll back one word
            if (this.height() > lines * lineHeight) {
                this.html(prevstr.trim() + "&hellip; " + (typeof link !== 'undefined' ? ' ' + link : ''));
            }
        }
    });
});
```

**Pros:** Can maintain readmore links and ellipses
**Cons:** Specific to certain CSS selectors

### Javascript Truncation on Arbitrary Items

The above solution is pretty flexible, but needs to specify the line-height. What if we want to apply the solution to an arbitrary element, or what if this particular element has a different line-height specified at some CSS breakpoints?

With many attributes one could just get the property, either from vanilla Javascript or by using the $(elt).css(“line-height”) method, but many browsers return the line-height value slightly differently, and, furthermore, we can’t guarantee what kind of units the line-height will be in.

I wish I had a really easy brilliant solution that anyone can DIY, but I got very tired and just downloaded the [line-height](https://www.npmjs.com/package/line-height) module and included it ahead of my own scripting. (The line-height module was, unsurprisingly, *also* written to supplement a line-based truncation solution.)

Using that, we can replace the static number assigned to the lineHeight variable with `window.lineHeight(this[0]);`, which should return the line-height value in an easy-to-use way. At this point, this is pretty easy to turn into a custom function we can call with an element and a given number of lines, or even as a jQuery plugin we can use as a method on any element.

**Pros:** Maintains readmore links and ellipses, can be re-used easily
**Cons:** Uses an external library for line-height

## Demo!

And here's the whole thing put together in a Codepen demo, with everything wrapped into a jQuery plugin:

{% codepen https://codepen.io/cmho/pen/jZzQRg %}