33

Is there any way to get an estimate for text width without rendering the actual elements? Something like canvas TextMetrics?

Case: I need to estimate element heights for ReactList. To do that I'd need to know roughly how much space the text elements will need (or how many lines will they span).

eg.

render(){
    return <div><SomeComponentWithKnownDims/><p>{this.props.someText}</p></div>;
}

If I knew how wide someText would be rendered into one line and how long the line would be, I could easily come up with a decent estimate for the components height.

EDIT: Note that this is quite performance critical and DOM should not be touched

0xc0de
  • 8,028
  • 5
  • 49
  • 75
Seppo420
  • 2,041
  • 2
  • 18
  • 37

1 Answers1

35

Please check this. is a solution using canvas

function get_tex_width(txt, font) {
        this.element = document.createElement('canvas');
        this.context = this.element.getContext("2d");
        this.context.font = font;
        return this.context.measureText(txt).width;
    }
    alert('Calculated width ' + get_tex_width("Hello World", "30px Arial"));
    alert("Span text width "+$("span").width());

Demo using

EDIT

The solution using canvas is not the best, each browser deal different canvas size.

Here is a nice solution to get size of text using a temporary element. Demo

EDIT

The canvas spec doesn't give us a method for measuring the height of a string, so for this we can use parseInt(context.font). TO get width and height. This trick work only with px size.

function get_tex_size(txt, font) {
    this.element = document.createElement('canvas');
    this.context = this.element.getContext("2d");
    this.context.font = font;
    var tsize = {'width':this.context.measureText(txt).width, 'height':parseInt(this.context.font)};
    return tsize;
}
var tsize = get_tex_size("Hello World", "30px Arial");

alert('Calculated width ' + tsize['width'] + '; Calculated height ' + tsize['height']);
vasilenicusor
  • 2,023
  • 1
  • 21
  • 37
  • 1
    will fail with `em`, don't know why though since it does work with `%` and viewport units. Also be carefull, in this context `this` is the `window` object, so you are actually creating global variables `element` and `context`. – Kaiido Jul 09 '15 at 03:03
  • @Kaiido Thank you for comment. Yes, canvas deals different the % and em size. So this will work only with px dimensions. So the only way to get the width as will be in DOM is to create e temporary element, add-it to DOM, apply styles and the get size. – vasilenicusor Jul 09 '15 at 06:06
  • 1
    no actually **it does work with `%` and viewport units (vh, vw...)** but it doesn't with `em`, which is strange – Kaiido Jul 09 '15 at 06:08
  • 1
    but the main issue is still that you do play with globals – Kaiido Jul 09 '15 at 06:13
  • here is without globals http://jsfiddle.net/r491oe7z/3/, doesnt matter. The main issue is how big will be the text in a specific element? So for this, Ben Ripkens has a solution - see my edit – vasilenicusor Jul 09 '15 at 06:19
  • 2
    Temporary elements don't really work due to performance requirements. The canvas thingy is my top candidate atm though. I think of making some zero size canvas with position:fixed and x-index:-1000 and then reusing the context for measureText. – Seppo420 Jul 09 '15 at 08:48
  • @Seppo420 there are some issues regarding browser behavior with canvas and units like % and viewport. Another aspect is the styles defined on the target element where text will be displayed, this must be applied to canvas – vasilenicusor Jul 09 '15 at 08:54
  • @vasiIenicusor can do just fine without % and providing font style to TextMetrics is not a major issue, but could you elaborate on the viewport issues? – Seppo420 Jul 09 '15 at 09:14
  • Yeah, this seems to work well enough. Follow up question: how do I get row height? – Seppo420 Jul 09 '15 at 11:10
  • given a maximum canvas width, is there a similar solution to calculating the number of lines the text will take? – Yossi Shasho Nov 15 '15 at 12:47