75

Is there any way to determine the pixel length of a string in jQuery/JavaScript?

GSto
  • 41,512
  • 37
  • 133
  • 184

8 Answers8

62

The contexts used for HTML Canvases have a built-in method for checking the size of a font. This method returns a TextMetrics object, which has a width property that contains the width of the text.

function getWidthOfText(txt, fontname, fontsize){
    if(getWidthOfText.c === undefined){
        getWidthOfText.c=document.createElement('canvas');
        getWidthOfText.ctx=getWidthOfText.c.getContext('2d');
    }
    var fontspec = fontsize + ' ' + fontname;
    if(getWidthOfText.ctx.font !== fontspec)
        getWidthOfText.ctx.font = fontspec;
    return getWidthOfText.ctx.measureText(txt).width;
}

Or, as some of the other users have suggested, you can wrap it in a span element:

function getWidthOfText(txt, fontname, fontsize){
    if(getWidthOfText.e === undefined){
        getWidthOfText.e = document.createElement('span');
        getWidthOfText.e.style.display = "none";
        document.body.appendChild(getWidthOfText.e);
    }
    if(getWidthOfText.e.style.fontSize !== fontsize)
        getWidthOfText.e.style.fontSize = fontsize;
    if(getWidthOfText.e.style.fontFamily !== fontname)
        getWidthOfText.e.style.fontFamily = fontname;
    getWidthOfText.e.innerText = txt;
    return getWidthOfText.e.offsetWidth;
}

EDIT 2020: added font name+size caching at Igor Okorokov's suggestion.

dE_
  • 63
  • 1
  • 4
Lux
  • 1,540
  • 1
  • 22
  • 28
  • Some reason this is not working in Chrome 54 or Firefox 48. e.innerWidth keeps coming up as undefined. – Donny V. Oct 10 '16 at 20:34
  • You have to add this to the DOM for the width or height to be calculated. document.body.appendChild(e) – Donny V. Oct 11 '16 at 12:31
  • @DonnyV. Thank you for the information, it was of great aid to me! I fixed up the DOM example, for which your help was greatly appreciated. – Lux Oct 14 '16 at 19:46
  • 1
    you're missing a space after the `px` when setting fontSize/face on the canvas – dwelle Nov 22 '16 at 21:25
  • @Dwelle The unit is left up to the user to determine. – Lux Dec 01 '16 at 00:05
  • 1
    @Xenon I mean, if you just copy this snippet, it won't work since the `'px' + fontname` is missing space after `px`. – dwelle Dec 01 '16 at 09:38
  • Whoop... Thanks. Corrected – Lux Dec 03 '16 at 18:20
  • 3
    canvas idea is awesome (especially if you alredy have one)! its pure js approach, its better than span, and does not require jquery – xakepp35 Oct 20 '17 at 16:05
  • @xakepp35 And it's 2-3 times faster than the DOM approach. – Lux Oct 20 '17 at 23:55
  • @Xenon ty. i need to draw simple dialogs, buttons, scrollbars.. Can you advice to me some small,simple,lightweight gui drawer for js canvas? or do i have to roll my own? – xakepp35 Oct 22 '17 at 02:53
  • @xakepp35 Honestly, I've never really done much canvas programming, so I don't really know the libraries that are available. – Lux Oct 22 '17 at 03:00
  • In my testing, "getWidthOfText.ctx.font = fontsize + ' ' + fontname;" slows down performance significantly, suggest to put under condition `var font = fontsize + ' ' + fontname; if (font !== lastFont) { getWidthOfText.ctx.font = font; lastFont = font; }` – Igor Okorokov Mar 13 '20 at 08:55
  • Ty, implemented – Lux Apr 23 '20 at 19:17
61

Wrap text in a span and use jquery width()

EDIT: jQuery is probably not the best solution anymore. Using plain JavaScript is an option, like seen on https://youmightnotneedjquery.com/#get_width:

el.getBoundingClientRect().width;
Natrium
  • 30,772
  • 17
  • 59
  • 73
  • 3
    Don't forget: placement and styling of the div/span makes a difference, because wrapping can come into play. – Andy E Jan 13 '10 at 15:24
  • 5
    This does only return the width of the containing element but not the width of the text. – Gumbo Jan 13 '10 at 15:33
  • 5
    Using a `` wrapper works the best, because a `
    ` is automatically set to 100% browser width.
    – Mottie Jan 13 '10 at 15:42
  • 11
    You should probably provide some sample code. I'm having a hard time figuring out exactly what you mean by "wrap text in a span". Should the span be in the original html? Can I create a dummy span that isn't seen? How at runtime? I tried: `$('').html('hi').width()` but it output 0 – Cruncher Aug 27 '14 at 17:00
  • @Cruncher: Try `$(document.createElement('span')).html('hi').width()`, which creates an empty element for jQuery to use, and does NOT affect the page at all. Or put an empty span in the HTML, but use `display: none` on it, and use `$('.my_span').html('hi').width()`. – Lux Jun 20 '15 at 15:51
  • I think the element should be appended or is already in the DOM to get the `width`. – leonard.javiniar Feb 11 '16 at 09:35
  • 2
    I would add white-space: nowrap to the span element, otherwise if the text is wrapped because the container is too small you'll have a wrong result – tocqueville Aug 09 '16 at 14:05
  • If you're trying to match some other element, don't forget to also add css (as needed) for font-size, font-family, font-weight, letter-spacing, and text-transform. – HK1 Sep 02 '16 at 14:16
  • 1
    just my two cents, you can't get the string's width if the string hasn't appended to the document/html. when you get the width pixel, you are actually getting the container that wraps around the string. using `span`, it wraps itself around the string instead of `block` element with `div` tag. html tags can influence string's width, spaces between letters and style, even we use same font-family. – roger Mar 20 '17 at 21:46
11

I don't believe you can do just a string, but if you put the string inside of a <span> with the correct attributes (size, font-weight, etc); you should then be able to use jQuery to get the width of the span.

<span id='string_span' style='font-weight: bold; font-size: 12'>Here is my string</span>
<script>
  $('#string_span').width();
</script>
Topher Fangio
  • 20,372
  • 15
  • 61
  • 94
6

Put it in an absolutely-positioned div then use clientWidth to get the displayed width of the tag. You can even set the visibility to "hidden" to hide the div:

<div id="text" style="position:absolute;visibility:hidden" >This is some text</div>
<input type="button" onclick="getWidth()" value="Go" />
<script type="text/javascript" >
    function getWidth() {
        var width = document.getElementById("text").clientWidth;
        alert(" Width :"+  width);
    }
</script>
Grinn
  • 5,370
  • 38
  • 51
6

Based on vSync's answer, the pure javascript method is lightning fast for large amount of objects. Here is the Fiddle: https://jsfiddle.net/xeyq2d5r/8/

[1]: https://jsfiddle.net/xeyq2d5r/8/ "JSFiddle"

I received favorable tests for the 3rd method proposed, that uses the native javascript vs HTML Canvas

Google was pretty competive for option 1 and 3, 2 bombed.

FireFox 48:
Method 1 took 938.895 milliseconds.
Method 2 took 1536.355 milliseconds.
Method 3 took 135.91499999999996 milliseconds.

Edge 11 Method 1 took 4895.262839793865 milliseconds.
Method 2 took 6746.622271896686 milliseconds.
Method 3 took 1020.0315412885484 milliseconds.

Google Chrome: 52
Method 1 took 336.4399999999998 milliseconds.
Method 2 took 2271.71 milliseconds.
Method 3 took 333.30499999999984 milliseconds.

RJ Kelly
  • 179
  • 1
  • 7
  • 1
    This does not provide an answer to the question. Once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](http://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](http://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/low-quality-posts/13425031) – Rohit Vipin Mathews Aug 23 '16 at 08:17
  • 2
    But it does provide further testing of the answers provided to clearly show how they perform. Ive always benefit from the comments and additional information like this in other posts. Thats why I added it, felt like the work I did should benefit others that see this post. Not like it doesnt have 56812 views in 6 years.... – RJ Kelly Sep 01 '16 at 15:23
  • 1
    Sorry if it sounded rude, but that is not the way SO works. You may go through FAQ and understand further. We try to improve the quality of the posts, that is why certain posts are flagged. – Rohit Vipin Mathews Sep 01 '16 at 15:45
  • 2
    This helped a lot. Checking the perf. of an implementation is very important when having to choose from multiple options, especially across browsers. – Donny V. Oct 10 '16 at 12:51
  • Method 3 returns undefined when logging the innerWidth. – Michael Kire Hansen Oct 17 '16 at 12:09
  • @Michael fixed the error in the fiddle and updated link, thanks – RJ Kelly Oct 18 '16 at 21:55
  • All methods returns undefined or null – Žilvinas May 18 '17 at 11:14
  • If that's my code you're performance testing: I have changed my answer to optimize and fix my code. Updated performance results for Safari and Google Chrome have been added to my answer. – Lux Aug 08 '17 at 23:44
3

First replicate the location and styling of the text and then use Jquery width() function. This will make the measurements accurate. For example you have css styling with a selector of:

.style-head span
{
  //Some style set
}

You would need to do this with Jquery already included above this script:

var measuringSpan = document.createElement("span");
measuringSpan.innerText = 'text to measure';
measuringSpan.style.display = 'none'; /*so you don't show that you are measuring*/
$('.style-head')[0].appendChild(measuringSpan);
var theWidthYouWant = $(measuringSpan).width();

Needless to say

theWidthYouWant

will hold the pixel length. Then remove the created elements after you are done or you will get several if this is done a several times. Or add an ID to reference instead.

Andrew Marais
  • 925
  • 9
  • 13
0

Maybe it will useful for some

const getMaxPixelsOfStrings = ({ strings, styles = {} }) => {
  const spans = strings.map(str => {
    const span = document.createElement('span')
    span.append(str)
    Object.assign(span.style, {
      position: 'absolute',
      ...styles,
    })

    return span
  })

  document.querySelector('html').prepend(...spans)
  const maxPixels = Math.max(...spans.map(span => span.getBoundingClientRect().width))

  spans.forEach(span => span.remove())

  return maxPixels
}

usage

getMaxPixelsOfStrings({
            strings: ['One', 'Two', 'Three', 'Four', 'Five'],
            styles: {
              fontSize: '18px',
              letterSpacing: '1px',
            },
          })
Dandgerson
  • 81
  • 1
  • 8
-2

If you use Snap.svg, the following works:

var tPaper = Snap(300, 300);
var tLabelText = tPaper.text(100, 100, "label text");
var tWidth = tLabelText.getBBox().width;  // the width of the text in pixels.
tLabelText.attr({ x : 150 - (tWidth/2)});   // now it's centered in x
Tim Erickson
  • 582
  • 5
  • 15
  • Being relatively inexperienced, I'd love to know why this was downvoted. Is it because it is a solution using Snap? It's what I use in order to avoid coping with canvas in JavaScript. – Tim Erickson Dec 09 '16 at 08:47
  • 1
    This is just what I was looking for but I'm getting zero in the width of the text... – shinzou Aug 09 '17 at 07:16
  • The original answer used `tPaper.node.clientWidth`. Kuhaku couldn't get that to work, and dang! Now neither can I. I have altered the example to use `.getBBox()`. I _think_ that you can use `clientWidth` on a Snap.svg "paper" object, the drawing surface (and `getBBox()` does not work); but for elements (circles, shapes, and importantly, text) you can use `getBBox()`. [I have a demo in an answer to another post](https://stackoverflow.com/questions/34424041/getting-svg-container-size-in-snapsvg/45767291#45767291) – Tim Erickson Aug 19 '17 at 03:24