I'd like to use JavaScript to calculate the width of a string. Is this possible without having to use a monospace typeface?

If it's not built-in, my only idea is to create a table of widths for each character, but this is pretty unreasonable especially supporting Unicode and different type sizes (and all browsers for that matter).

Beware that if you're using external fonts, you'll have to use the techniques below after they've loaded. You may not realize this if you have them cached or if you have local versions installed.
– Seth W. KleinDec 13 '15 at 20:35

22 Answers
22

Create a DIV styled with the following styles. In your JavaScript, set the font size and attributes that you are trying to measure, put your string in the DIV, then read the current width and height of the DIV. It will stretch to fit the contents and the size will be within a few pixels of the string rendered size.

The only thing I'd add is that this may give the wrong dimensions depending on which styles are used. Remember you may have styles like p { letter-spacing: 0.1em; } that a DIV element would not reflect. You must ensure that the styles in place are appropriate for where you will use the text.
– JimSep 23 '08 at 0:26

4

Ditto Jim's comment - double-check to make sure the container, div in this case, does not have any other styles applied to it via css selection rules that you may not be cognizant of at the time. Strip all relevant styles from the container before applying the ones you care about before measuring.
– Jason BuntingSep 23 '08 at 0:33

41

You should also put in white-space:nowrap if you think the text will exceed the browser width.
– Herb CaudillSep 23 '08 at 1:41

4

@BBog all that hacking around is why I suggest a different, less hacky, approach downstairs. Take a look at it here.
– DomiJan 9 '14 at 8:54

6

Just curious what the +1 is for on each dimension?
– KipMar 28 '14 at 3:18

NOTE 2: On some browsers, this method yields sub-pixel accuracy (result is a floating point number), on others it does not (result is only an int). You might want to run Math.floor (or Math.ceil) on the result, to avoid inconsistencies. Since the DOM-based method is never sub-pixel accurate, this method has even higher precision than the other methods here.

According to this jsperf (thanks to the contributors in comments), the Canvas method and the DOM-based method are about equally fast, if caching is added to the DOM-based method and you are not using Firefox. In Firefox, for some reason, this Canvas method is much much faster than the DOM-based method (as of September 2014).

@Martin Huh? What does "many HTML elements" have to do with anything here? The canvas object in this example isn't even attached to the DOM. And even aside from that, changing the font in a canvas drawing context doesn't even affect the canvas at all until you strokeText() or fillText(). Perhaps you meant to comment on a different answer?
– Ajedi32Sep 18 '15 at 16:43

1

Nice solution. I've wrapped a couple of other methods around Domi's answer so that I can - Get a (potentially) truncated string with ellipsis (...) at the end if it won't fit in a given space (as much of the string as possible will be used) - Pass in a JQuery Element that is to contain the (possibly truncated) string, and determine the Font attributes dynamically so that they don't have to be hard-coded, allowing CSS font attributes to change without breaking the layout. JSFiddle Here: jsfiddle.net/brebey/1q94gLsu/6/embed
– BRebeyMay 18 '16 at 19:37

Thanks JordanC. One thing I would add is, if you are calling this a lot of times on the same page, and performance is an issue, you could persist the DIV, and just change the contents and check width. I just did it this way so once everything was said and done, the DOM would be the same as when you started
– Bob MonteverdeJan 21 '12 at 1:53

In my version of this i used an object instead of the font argument so you can supply css arguments to the div for bold and italics etc.
– IainAug 31 '12 at 2:21

3

I would not add this method to String since it diminishes your program's separation of concerns (separate core code vs. UI code). Imagine you want to port your core code to a platform with a very different UI framework...
– DomiJan 9 '14 at 8:26

17

This is bad design, not only does it couple your model with your view but also couples it directly with jQuery. On top of that, this is reflow hell, this would definitely not scale well.
– JamesJul 17 '14 at 9:40

The ExtJS javascript library has a great class called Ext.util.TextMetrics that "provides precise pixel measurements for blocks of text so that you can determine exactly how high and wide, in pixels, a given block of text will be". You can either use it directly or view its source to code to see how this is done.

ExtJS has an odd license. Either you pay the current maintainers, the Sencha Company to use it, or you must open-source all related code in your application. This is a show stopper for most companies. jQuery, on the other hand, uses the highly permissive MIT license.
– devdankeFeb 18 '12 at 17:00

1

Doesn't javascript automatically meet the requirements of open source? You serve the source to anyone viewing the page.
– PatrickOApr 24 '12 at 16:09

8

There's a difference between being able to view the source code and open source, which is defined by licensing.
– Parker AultApr 26 '12 at 21:59

Thanks from those of us still using ExtJS :-)
– Monarch WadiaMar 6 '17 at 17:00

I like your "only idea" of just doing a static character width map! It actually works well for my purposes. Sometimes, for performance reasons or because you don't have easy access to a DOM, you may just want a quick hacky standalone calculator calibrated to a single font. So here's one calibrated to Helvetica; pass a string and (optionally) a font size:

That giant ugly array is ASCII character widths indexed by character code. So this just supports ASCII (otherwise it assumes an average character width). Fortunately, width basically scales linearly with font size, so it works pretty well at any font size. It's noticeably lacking any awareness of kerning or ligatures or whatever.

that is a smart solution, nice one :) I am always using the same font/size, so it suits me well. I found the performance of the DOM/Canvas solutions a real problem, this is really clean, cheers!
– mikeapr4Dec 31 '18 at 15:58

That is more or less what make upper solutions, but as your text may be splited into several lines, they add some CSS styles to the text to get the real full text width.
– Adrian MaireOct 28 '14 at 16:25

The code-snips below, "calculate" the width of the span-tag, appends "..." to it if its too long and reduces the text-length, until it fits in its parent (or until it has tried more than a thousand times)

Building off of Deepak Nadar's answer, I changed the functions parameter's to accept text and font styles. You do not need to reference an element. Also, the fontOptions have defaults, so you to not need to supply all of them.

when you set a number doesnt work, because theres a space no necesary, if you remove it, it will works fine: fontSize + " px arial" -> fontSize + "px arial", just that.
– Alberto AcuñaJan 26 '17 at 8:57

The Element.getClientRects() method returns a collection of DOMRect objects that indicate the bounding rectangles for each CSS border box in a client. The returned value is a collection of DOMRect objects, one for each CSS border box associated with the element. Each DOMRect object contains read-only left, top, right and bottom properties describing the border box, in pixels, with the top-left relative to the top-left of the viewport.

In case anyone else got here looking both for a way to measure the width of a string and a way to know what's the largest font size that will fit in a particular width, here is a function that builds on @Domi's solution with a binary search:

Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).