103

I'm trying to calculate text width using jQuery. I'm not sure what, but I am definitely doing something wrong.

So, here is the code:

var c = $('.calltoaction');

var cTxt = c.text();

var cWidth =  cTxt.outerWidth();

c.css('width' , cWidth);
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Dom
  • 3,126
  • 14
  • 46
  • 68

22 Answers22

142

This worked better for me:

$.fn.textWidth = function(){
  var html_org = $(this).html();
  var html_calc = '<span>' + html_org + '</span>';
  $(this).html(html_calc);
  var width = $(this).find('span:first').width();
  $(this).html(html_org);
  return width;
};
Rune Kaagaard
  • 6,643
  • 2
  • 38
  • 29
  • 4
    +1 - this truly measures the text, not just the containing block element like brainreavis' solution. – Ben Mar 23 '11 at 16:44
  • Slick. +1. It looks like you are missing a semi colon on the third line after `''`, though it doesn't seem to make a difference (worked with or without it on FF9). – shmeeps Jan 12 '12 at 18:33
  • 22
    Be careful with this though... this method will unbind any events on child elements. – brianreavis Feb 03 '12 at 21:05
  • I have tried to use this function in a number of ways, but it keeps returning null. Could someone please post the syntax for how this function is actually used? Do I run the .textWidth() function on a jQuery object? Do I pass text to this function? Do I run the function as a jQuery function (ie $.textWidth(jqObject)? Because none of those options seems to work. Thanks... – moosefetcher Oct 31 '13 at 14:37
  • 2
    @moosefetcher you would do $(selector).textWidth(); Look here https://learn.jquery.com/plugins/basic-plugin-creation/#basic-plugin-authoring – uesports135 Aug 21 '15 at 16:48
  • 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
  • What if there is a css rule that causes the nested span to have a very different font size? But I think this should work well if you know how it works. Btw, I would rather clone it or create a new temp element rather than modifying the original one. –  Mar 05 '18 at 05:45
  • This didn't work for text that's larger than the container. I came up with a solution: https://stackoverflow.com/questions/1582534/calculating-text-width/53291346#53291346 – Chris Petersn Nov 14 '18 at 00:34
90

Here's a function that's better than others posted because

  1. it's shorter
  2. it works when passing an <input>, <span>, or "string".
  3. it's faster for frequent uses because it reuses an existing DOM element.

Demo: http://jsfiddle.net/philfreo/MqM76/

// Calculate width of text from DOM element or string. By Phil Freo <http://philfreo.com>
$.fn.textWidth = function(text, font) {
    if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl = $('<span>').hide().appendTo(document.body);
    $.fn.textWidth.fakeEl.text(text || this.val() || this.text()).css('font', font || this.css('font'));
    return $.fn.textWidth.fakeEl.width();
};
philfreo
  • 41,941
  • 26
  • 128
  • 141
  • 1
    Wow! Pure awesomeness! – Michel Triana Feb 19 '14 at 22:34
  • Fantastico! Simple and clean. – Carey Estes Jul 02 '14 at 14:50
  • Whilst good, this doesn't handle spaces. See @edsioufi's answer for an enhancement to this solution. – Badgerspot Sep 24 '14 at 07:56
  • If there's hidden text inside the element, then it will skew your width. To account for this, be sure to do remove the hidden nodes before calculating the text width. To safely remove the hidden nodes, you should probably make a clone first and do the removal there. – thdoan Dec 12 '14 at 08:10
  • Magic! handles different font size elegantly. thanks. – geevee Nov 19 '15 at 11:13
  • Great solution! I modified it a little bit. I just use the element from which the function is called and get that font to pass on to the span. This saves having to pass in the font itself. $.fn.widthOfTextInElement = function () { if (!$.fn.widthOfTextInElement.fakeEl) $.fn.widthOfTextInElement.fakeEl = $('').hide().appendTo(document.body); $.fn.widthOfTextInElement.fakeEl.text($(this).text() || $(this).val()) .css('font', $(this).css("font")); return $.fn.widthOfTextInElement.fakeEl.width(); } – John Pasquet May 04 '17 at 21:26
  • Nice solution, but it will take time to append and all – Priyanka Ahire Jun 04 '19 at 06:56
  • Warning! This thrashes the body-layout! It cost me a day to find it :( – Rob Audenaerde May 26 '20 at 11:48
36

My solution

$.fn.textWidth = function(){
    var self = $(this),
        children = self.children(),
        calculator = $('<span style="display: inline-block;" />'),
        width;

    children.wrap(calculator);
    width = children.parent().width(); // parent = the calculator wrapper
    children.unwrap();
    return width;
};

Basically an improvement over Rune's, that doesn't use .html so lightly

bevacqua
  • 47,502
  • 56
  • 171
  • 285
  • **By [Ajarn Gerhard](http://stackoverflow.com/users/1996729/ajarn-gerhard) who posted this as an answer:** This not working in IE8 because you're missing a `/` before the closing bracket of the `` tag: `calculator = $(''),` – Petr R. Aug 17 '13 at 19:31
  • 1
    This seems to return null if there is child element below the element you are measuring. Can it be made to work without the child element? See http://jsfiddle.net/h22tj/41/ – Casper Nov 09 '13 at 23:26
  • +1 - I never knew there was an unwrap(), that has caused me endless grief. – Orwellophile Dec 19 '13 at 23:25
  • It always returns null on Chrome. – emeraldhieu Mar 12 '14 at 09:49
12

The textWidth functions provided in the answers and that accept a string as an argument will not account for leading and trailing white spaces (those are not rendered in the dummy container). Also, they will not work if the text contains any html markup (a sub-string <br> won't produce any output and &nbsp; will return the length of one space).

This is only a problem for the textWidth functions which accept a string, because if a DOM element is given, and .html() is called upon the element, then there is probably no need to fix this for such use case.

But if, for example, you are calculating the width of the text to dynamically modify the width of an text input element as the user types (my current use case), you'll probably want to replace leading and trailing spaces with &nbsp; and encode the string to html.

I used philfreo's solution so here is a version of it that fixes this (with comments on additions):

$.fn.textWidth = function(text, font) {
    if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl = $('<span>').appendTo(document.body);
    var htmlText = text || this.val() || this.text();
    htmlText = $.fn.textWidth.fakeEl.text(htmlText).html(); //encode to Html
    htmlText = htmlText.replace(/\s/g, "&nbsp;"); //replace trailing and leading spaces
    $.fn.textWidth.fakeEl.html(htmlText).css('font', font || this.css('font'));
    return $.fn.textWidth.fakeEl.width();
};
edsioufi
  • 8,297
  • 3
  • 36
  • 42
11

jQuery's width functions can be a bit shady when trying to determine the text width due to inconsistent box models. The sure way would be to inject div inside your element to determine the actual text width:

$.fn.textWidth = function(){
  var sensor = $('<div />').css({margin: 0, padding: 0});
  $(this).append(sensor);
  var width = sensor.width();
  sensor.remove();
  return width;
};

To use this mini plugin, simply:

$('.calltoaction').textWidth();
brianreavis
  • 11,562
  • 3
  • 43
  • 50
  • 1
    Wouldn't this give you the block width rather than the text? – Josh Rickard Jun 12 '10 at 16:59
  • -1 This does indeed seem to measure the width of the box rather than the text width. – Nathan Arthur Oct 31 '11 at 21:43
  • 1
    Wouldn't simply using a span instead of a div fix the width problem? If so, then this function is a bit better than the accepted answer. – Josh Apr 18 '13 at 19:56
  • @Josh: If you replace it with a `span` you just get zero. Tried this solution on an `H2`, where parent elements have overflow hidden, and I only get the truncated length of the text. Perhaps an explanation of how this is *supposed to work* would help make it work better? – iCollect.it Ltd Sep 02 '15 at 13:32
8

Neither Rune's nor Brain's was working for me in case when the element that was holding the text had fixed width. I did something similar to Okamera. It uses less selectors.

EDIT: It won't probably work for elements that uses relative font-size, as following code inserts htmlCalc element into body thus looses the information about parents relation.

$.fn.textWidth = function() {
    var htmlCalc = $('<span>' + this.html() + '</span>');
    htmlCalc.css('font-size', this.css('font-size'))
            .hide()
            .prependTo('body');
    var width = htmlCalc.width();
    htmlCalc.remove();
    return width;
};
chmurson
  • 619
  • 6
  • 11
8

I found this solution works well and it inherits the origin font before sizing:

$.fn.textWidth = function(text){
  var org = $(this)
  var html = $('<span style="postion:absolute;width:auto;left:-9999px">' + (text || org.html()) + '</span>');
  if (!text) {
    html.css("font-family", org.css("font-family"));
    html.css("font-size", org.css("font-size"));
  }
  $('body').append(html);
  var width = html.width();
  html.remove();
  return width;
}
Vince Spicer
  • 4,325
  • 3
  • 21
  • 11
  • 1
    I ended up using this, but added `org.css("font-weight")`. Also, I would say that the `if(!text)` part is unintuitive. If I use e.g. `jQuery("#someContainer").textWidth("Lorem ipsum")` I would want to know the text width of "Lorem ipsum" when applied in that particular container. – Sleavely Jun 20 '12 at 13:49
5

If your trying to do this with text in a select box or if those two arent working try this one instead:

$.fn.textWidth = function(){
 var calc = '<span style="display:none">' + $(this).text() + '</span>';
 $('body').append(calc);
 var width = $('body').find('span:last').width();
 $('body').find('span:last').remove();
 return width;
};

or

function textWidth(text){
 var calc = '<span style="display:none">' + text + '</span>';
 $('body').append(calc);
 var width = $('body').find('span:last').width();
 $('body').find('span:last').remove();
 return width;
};

if you want to grab the text first

okamera
  • 93
  • 1
  • 5
4

the thing, you are doing wrong, that you are calling a method on cTxt, which is a simple string and not a jQuery object. cTxt is really the contained text.

Juraj Blahunka
  • 17,913
  • 6
  • 34
  • 52
2

Slight change to Nico's, since .children() will return an empty set if we're referencing a text element like an h1 or p. So we'll use .contents() instead, and use this instead of $(this) since we're creating a method on a jQuery object.

$.fn.textWidth = function(){
    var contents = this.contents(),
        wrapper  = '<span style="display: inline-block;" />',
        width    = '';

    contents.wrapAll(wrapper);
    width = contents.parent().width(); // parent is now the wrapper
    contents.unwrap();
    return width;
    };
1

after chasing a ghost for two days, trying to figure out why the width of a text was incorrect, i realized it was because of white spaces in the text string that would stop the width calculation.

so, another tip is to check if the whitespaces are causing problems. use

&nbsp;

non-breaking space and see if that fixes it up.

the other functions people suggested work well too, but it was the whitespaces causing trouble.

zonabi
  • 746
  • 6
  • 20
1

If you are trying to determine the width of a mix of text nodes and elements inside a given element, you need to wrap all the contents with wrapInner(), calculate the width, and then unwrap the contents.

*Note: You will also need to extend jQuery to add an unwrapInner() function since it is not provided by default.

$.fn.extend({
  unwrapInner: function(selector) {
      return this.each(function() {
          var t = this,
              c = $(t).children(selector);
          if (c.length === 1) {
              c.contents().appendTo(t);
              c.remove();
          }
      });
  },
  textWidth: function() {
    var self = $(this);
    $(this).wrapInner('<span id="text-width-calc"></span>');
    var width = $(this).find('#text-width-calc').width();
    $(this).unwrapInner();
    return width;
  }
});
trip41
  • 1,201
  • 1
  • 8
  • 9
  • +1 for .wrapInner. As you probably know, dealing with pure text elements in jQuery is hell. If there are any other jQuery methods that are applicable, please let me know. I have had to resort to .get(0).children and it's not pretty. – Orwellophile Dec 19 '13 at 23:28
1

Expanding on @philfreo's answer:

I've added the ability to check for text-transform, as things like text-transform: uppercase usually tend to make the text wider.

$.fn.textWidth = function (text, font, transform) {
    if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl = $('<span>').hide().appendTo(document.body);
    $.fn.textWidth.fakeEl.text(text || this.val() || this.text())
        .css('font', font || this.css('font'))
        .css('text-transform', transform || this.css('text-transform'));
    return $.fn.textWidth.fakeEl.width();
};
shaneparsons
  • 1,489
  • 1
  • 20
  • 24
0

Call getColumnWidth() to get the with of the text. This works perfectly fine.

someFile.css
.columnClass { 
    font-family: Verdana;
    font-size: 11px;
    font-weight: normal;
}


function getColumnWidth(columnClass,text) { 
    tempSpan = $('<span id="tempColumnWidth" class="'+columnClass+'" style="display:none">' + text + '</span>')
          .appendTo($('body'));
    columnWidth = tempSpan.width();
    tempSpan.remove();
return columnWidth;
}

Note:- If you want inline .css pass the font-details in style only.

Arun Pratap Singh
  • 3,428
  • 30
  • 23
0

I modified Nico's code to work for my needs.

$.fn.textWidth = function(){
    var self = $(this),
        children = self.contents(),
        calculator = $('<span style="white-space:nowrap;" />'),
        width;

    children.wrap(calculator);
    width = children.parent().width(); // parent = the calculator wrapper
    children.unwrap();
    return width;
};

I'm using .contents() as .children() does not return text nodes which I needed. I also found that the returned width was impacted by the viewport width which was causing wrapping so I'm using white-space:nowrap; to get the correct width regardless of viewport width.

0
$.fn.textWidth = function(){
        var w = $('body').append($('<span stlye="display:none;" id="textWidth"/>')).find('#textWidth').html($(this).html()[0]).width();
        $('#textWidth').remove();
        console.log(w);
        return w;
    };

almost a one liner. Gives you the with of the first character

ceed
  • 689
  • 1
  • 6
  • 15
0

I could not get any of the solutions listed to work 100%, so came up with this hybrid, based on ideas from @chmurson (which was in turn based on @Okamera) and also from @philfreo:

(function ($)
{
    var calc;
    $.fn.textWidth = function ()
    {
        // Only create the dummy element once
        calc = calc || $('<span>').css('font', this.css('font')).css({'font-size': this.css('font-size'), display: 'none', 'white-space': 'nowrap' }).appendTo('body');
        var width = calc.html(this.html()).width();
        // Empty out the content until next time - not needed, but cleaner
        calc.empty();
        return width;
    };
})(jQuery);

Notes:

  • this inside a jQuery extension method is already a jQuery object, so no need for all the extra $(this) that many examples have.
  • It only adds the dummy element to the body once, and reuses it.
  • You should also specify white-space: nowrap, just to ensure that it measures it as a single line (and not line wrap based on other page styling).
  • I could not get this to work using font alone and had to explicitly copy font-size as well. Not sure why yet (still investigating).
  • This does not support input fields that way @philfreo does.
iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
0

Sometimes you also need to measure additionally height and not only text, but also HTML width. I took @philfreo answer and made it more flexbile and useful:

function htmlDimensions(html, font) {
  if (!htmlDimensions.dummyEl) {
    htmlDimensions.dummyEl = $('<div>').hide().appendTo(document.body);
  }
  htmlDimensions.dummyEl.html(html).css('font', font);
  return {
    height: htmlDimensions.dummyEl.height(),
    width: htmlDimensions.dummyEl.width()
  };
}
Daniel Kmak
  • 18,164
  • 7
  • 66
  • 89
0

text width can be different for different parents, for example if u add a text into h1 tag it will be wider than div or label, so my solution like this:

<h1 id="header1">

</h1>

alert(calcTextWidth("bir iki", $("#header1")));

function calcTextWidth(text, parentElem){
    var Elem = $("<label></label>").css("display", "none").text(text);
    parentElem.append(Elem);
  var width = Elem.width();
  Elem.remove();
    return width;
}
Saeed Taran
  • 376
  • 2
  • 14
0

I had trouble with solutions like @rune-kaagaard's for large amounts of text. I discovered this:

$.fn.textWidth = function() {
 var width = 0;
 var calc = '<span style="display: block; width: 100%; overflow-y: scroll; white-space: nowrap;" class="textwidth"><span>' + $(this).html() + '</span></span>';
 $('body').append(calc);
 var last = $('body').find('span.textwidth:last');
 if (last) {
   var lastcontent = last.find('span');
   width = lastcontent.width();
   last.remove();
 }
 return width;
};

JSFiddle GitHub

0
var calc = '<span style="display:none; margin:0 0 0 -999px">' + $('.move').text() + '</span>';
yura
  • 9
  • 1
0

If the field is a fixed-width input or contenteditable div, you can get the horizontal scroll width as scrollWidth

      $("input").on("input", function() {
        var width = el[0].scrollWidth;
        
        console.log(width);
      });