Detecting how many times some text wraps with JavaScript
Tags: javascript, layout, overflow
Published: 2023-11-27 13:25:08

---
title: Detecting how many times some text wraps with JavaScript
published: true
description: Description of a simple function which works out how many times some text will wrap, within its current context on a web page.
tags: JavaScript, Layout, Overflow
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5okqmzqh7tcirhw30l2i.jpg
# published_at: 2023-11-27 11:16 +0000
---

<small>Image by [Till Daling](https://www.pexels.com/photo/a-man-holding-a-teapot-12642987/)</small>

I lost an argument the other day. I'd built a design which included these circles with text and an icon inside them. The element would appear inside a site which was content managed so naturally, I put Too Much Content inside them, just like a mischievous content author would.

I can't remember how I handled the overflow - I probably just allowed the circles to distort into ovals. It wasn't pretty, but that wasn't my goal. My goal was to ensure that all of the text would be legible.

The designer understandably didn't like this. The obvious solution is to restrict how long a string the content author can add, but neither of us controlled that part of the application, so we needed to work with what we had.

> Why can't you just make the text smaller, if it wraps onto two lines?

This made my eye twitch[^1], but I had to admit, it was a better solution than adding a scroll bar inside the circle. The way I anticipated it working was that an attribute on the element would indicate how many times the text was wrapping and a CSS selector would pick up on this and make the text size smaller.

There's only so many times you can do this, of course, before the text becomes a string of pixels. But hopefully by this point, this would become Someone Else's Problem.

[^1]: In case anyone doesn't understand this reaction, using JavaScript to do a CSS's job is usually a terrible idea. As a rule of thumb, if you find yourself doing this, it suggests that the problem you're trying to solve might be flawed.

I looked to the usual suspects to a solution to this problem, but I didn't like the code. So I wrote some JavaScript which changes a `data` attribute, depending upon how many times the text wraps. I could use this to target specific instances in the CSS, and change the font-size as a result. Here's a CodePen:

{% codepen https://codepen.io/ross-angus/pen/qBgKaXe %}

## How it works

The main work takes place in a function called `lineNumbers`:

1. The function measures the height of the passed element
1. It replaces all of the content inside the element with `i`
1. Then it measures the height of the element a second time
1. The original content is replaced (steps 2-4 happen so quickly that you won't notice the DOM updating)
1. It takes the original height and divides it by the small height, to return the number of times the content wraps
1. This is added to the element on an attribute called `data-wrap`

`lineNumbers()` is called every time the page resizes, and will be run against every element which has the attribute `data-js="detect-wrap"`. That's (potentially) a lot of work! So I debounced the function which means that it only runs if it hasn't been called for ⅒ of a second. You should think about debouncing any code which runs when the user is doing something which reflows the content or is susceptible to lag, such as resizing the browser or scrolling the content. But I'm not your dad. You do you[^2].

[^2]: The other reason it's useful to debounce this code is that if you *do* make the text size smaller using CSS, then this causes another reflow of the DOM which might cause a `resize` event to fire. The debounce helps to stop the code getting caught in a loop. No guarantees, though!

I've commented the code fairly intensively so the only thing I'd like to mention is that the `clientHeight` method will give you the height of an element *including* its padding. So the top and bottom padding is worked out and stored, to ensure that the sums work out correctly.