Pregunta

Me gustaría usar JavaScript para calcular el ancho de una cadena.¿Es esto posible sin tener que utilizar un tipo de letra monoespaciado?

Si no está integrado, mi única idea es crear una tabla de anchos para cada carácter, pero esto es bastante irrazonable, especialmente si se admite Unicódigo y diferentes tamaños de letra (y todos los navegadores).

¿Fue útil?

Solución

Cree un DIV con los siguientes estilos.En su JavaScript, establezca el tamaño de fuente y los atributos que está intentando medir, coloque su cadena en el DIV y luego lea el ancho y alto actuales del DIV.Se estirará para ajustarse al contenido y el tamaño estará dentro de unos pocos píxeles del tamaño de la cadena representada.

var fontSize = 12;
var test = document.getElementById("Test");
test.style.fontSize = fontSize;
var height = (test.clientHeight + 1) + "px";
var width = (test.clientWidth + 1) + "px"

console.log(height, width);
#Test
{
    position: absolute;
    visibility: hidden;
    height: auto;
    width: auto;
    white-space: nowrap; /* Thanks to Herb Caudill comment */
}
<div id="Test">
    abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
</div>

Otros consejos

En HTML5, puedes usar simplemente el Método Canvas.measureText (explicación adicional aquí).

Prueba este violín:

/**
 * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
 * 
 * @param {String} text The text to be rendered.
 * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
 * 
 * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
 */
function getTextWidth(text, font) {
    // re-use canvas object for better performance
    var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
    var context = canvas.getContext("2d");
    context.font = font;
    var metrics = context.measureText(text);
    return metrics.width;
}

console.log(getTextWidth("hello there!", "bold 12pt arial"));  // close to 86

este violín compara este método Canvas con una variación de Método basado en DOM de Bob Monteverde, para que pueda analizar y comparar la precisión de los resultados.

Este enfoque tiene varias ventajas, entre ellas:

  • Más conciso y seguro que otros métodos (basados ​​en DOM) porque no cambia el estado global, como su DOM.
  • Es posible una mayor personalización mediante modificando más propiedades de texto del lienzo, como textAlign y textBaseline.

NOTA:Cuando agregues el texto a tu DOM, recuerda tener en cuenta también relleno, margen y borde.

NOTA 2:En algunos navegadores, este método produce una precisión de subpíxeles (el resultado es un número de punto flotante), en otros no (el resultado es solo un int).Quizás quieras correr Math.floor (o Math.ceil) sobre el resultado, para evitar inconsistencias.Dado que el método basado en DOM nunca tiene una precisión de subpíxeles, este método tiene una precisión incluso mayor que los otros métodos aquí.

De acuerdo a este jsperf (gracias a los contribuyentes en los comentarios), el Método de lienzo y el Método basado en DOM son casi igualmente rápidos, si se agrega el almacenamiento en caché al Método basado en DOM y no estás usando Firefox.En Firefox, por alguna razón, esto Método de lienzo es mucho más rápido que el Método basado en DOM (a septiembre de 2014).

Aquí hay uno que preparé sin ejemplo.Parece que todos estamos en la misma página.

String.prototype.width = function(font) {
  var f = font || '12px arial',
      o = $('<div></div>')
            .text(this)
            .css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
            .appendTo($('body')),
      w = o.width();

  o.remove();

  return w;
}

Usarlo es simple: "a string".width()

**Agregado white-space: nowrap por lo que se pueden calcular cadenas con un ancho mayor que el ancho de la ventana.

jQuery:

(function($) {

 $.textMetrics = function(el) {

  var h = 0, w = 0;

  var div = document.createElement('div');
  document.body.appendChild(div);
  $(div).css({
   position: 'absolute',
   left: -1000,
   top: -1000,
   display: 'none'
  });

  $(div).html($(el).html());
  var styles = ['font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing'];
  $(styles).each(function() {
   var s = this.toString();
   $(div).css(s, $(el).css(s));
  });

  h = $(div).outerHeight();
  w = $(div).outerWidth();

  $(div).remove();

  var ret = {
   height: h,
   width: w
  };

  return ret;
 }

})(jQuery);

Esto funciona para mí...

// Handy JavaScript to measure the size taken to render the supplied text;
// you can supply additional style information too if you have it.

function measureText(pText, pFontSize, pStyle) {
    var lDiv = document.createElement('div');

    document.body.appendChild(lDiv);

    if (pStyle != null) {
        lDiv.style = pStyle;
    }
    lDiv.style.fontSize = "" + pFontSize + "px";
    lDiv.style.position = "absolute";
    lDiv.style.left = -1000;
    lDiv.style.top = -1000;

    lDiv.innerHTML = pText;

    var lResult = {
        width: lDiv.clientWidth,
        height: lDiv.clientHeight
    };

    document.body.removeChild(lDiv);
    lDiv = null;

    return lResult;
}

El biblioteca javascript extjs tiene una gran clase llamada Ext.util.TextMetrics que "proporciona medidas precisas de píxeles para bloques de texto para que pueda determinar exactamente qué tan alto y ancho, en píxeles, será un bloque de texto determinado".Puede usarlo directamente o ver su código fuente para ver cómo se hace.

http://docs.sencha.com/extjs/6.5.3/modern/Ext.util.TextMetrics.html

Escribí una pequeña herramienta para eso.Quizás sea útil para alguien.Funciona sin jQuery.

https://github.com/schickling/calculate-size

Uso:

var size = calculateSize("Hello world!", {
   font: 'Arial',
   fontSize: '12px'
});

console.log(size.width); // 65
console.log(size.height); // 14

Violín: http://jsfiddle.net/PEvL8/

¡Me gusta tu "única idea" de hacer simplemente un mapa de ancho de caracteres estático!De hecho, funciona bien para mis propósitos.A veces, por razones de rendimiento o porque no tienes fácil acceso a un DOM, es posible que simplemente quieras una calculadora independiente y rápida calibrada para una sola fuente.Aquí hay uno calibrado para Helvetica;pase una cadena y (opcionalmente) un tamaño de fuente:

function measureText(str, fontSize = 10) {
  const widths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2796875,0.2765625,0.3546875,0.5546875,0.5546875,0.8890625,0.665625,0.190625,0.3328125,0.3328125,0.3890625,0.5828125,0.2765625,0.3328125,0.2765625,0.3015625,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.2765625,0.2765625,0.584375,0.5828125,0.584375,0.5546875,1.0140625,0.665625,0.665625,0.721875,0.721875,0.665625,0.609375,0.7765625,0.721875,0.2765625,0.5,0.665625,0.5546875,0.8328125,0.721875,0.7765625,0.665625,0.7765625,0.721875,0.665625,0.609375,0.721875,0.665625,0.94375,0.665625,0.665625,0.609375,0.2765625,0.3546875,0.2765625,0.4765625,0.5546875,0.3328125,0.5546875,0.5546875,0.5,0.5546875,0.5546875,0.2765625,0.5546875,0.5546875,0.221875,0.240625,0.5,0.221875,0.8328125,0.5546875,0.5546875,0.5546875,0.5546875,0.3328125,0.5,0.2765625,0.5546875,0.5,0.721875,0.5,0.5,0.5,0.3546875,0.259375,0.353125,0.5890625]
  const avg = 0.5279276315789471
  return str
    .split('')
    .map(c => c.charCodeAt(0) < widths.length ? widths[c.charCodeAt(0)] : avg)
    .reduce((cur, acc) => acc + cur) * fontSize
}

Esa fea matriz gigante son anchos de caracteres ASCII indexados por código de caracteres.Entonces esto solo admite ASCII (de lo contrario, asume un ancho de carácter promedio).Afortunadamente, el ancho básicamente escala linealmente con el tamaño de fuente, por lo que funciona bastante bien con cualquier tamaño de fuente.Es notablemente carente de conocimiento sobre el interletraje, las ligaduras o lo que sea.

Para "calibrar", simplemente representé cada carácter hasta charCode 126 (la poderosa tilde) en un svg, obtuve el cuadro delimitador y lo guardé en esta matriz; más código, explicación y demostración aquí.

Puedes usar el lienzo para no tener que lidiar tanto con las propiedades de CSS:

var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.font = "20pt Arial";  // This can be set programmaticly from the element's font-style if desired
var textWidth = ctx.measureText($("#myElement").text()).width;
<span id="text">Text</span>

<script>
var textWidth = document.getElementById("text").offsetWidth;
</script>

Esto debería funcionar siempre y cuando la etiqueta <span> no tenga otros estilos aplicados.offsetWidth incluirá el ancho de los bordes, el relleno horizontal, el ancho de la barra de desplazamiento vertical, etc.

Los recortes de código a continuación "calculan" el ancho de la etiqueta span, le agregan "..." si es demasiado largo y reducen la longitud del texto, hasta que quepa en su padre (o hasta que haya probado más de mil veces)

CSS

div.places {
  width : 100px;
}
div.places span {
  white-space:nowrap;
  overflow:hidden;
}

HTML

<div class="places">
  <span>This is my house</span>
</div>
<div class="places">
  <span>And my house are your house</span>
</div>
<div class="places">
  <span>This placename is most certainly too wide to fit</span>
</div>

JavaScript (con jQuery)

// loops elements classed "places" and checks if their child "span" is too long to fit
$(".places").each(function (index, item) {
    var obj = $(item).find("span");
    if (obj.length) {
        var placename = $(obj).text();
        if ($(obj).width() > $(item).width() && placename.trim().length > 0) {
            var limit = 0;
            do {
                limit++;
                                    placename = placename.substring(0, placename.length - 1);
                                    $(obj).text(placename + "...");
            } while ($(obj).width() > $(item).width() && limit < 1000)
        }
    }
});

Pruebe este código:

function GetTextRectToPixels(obj)
{
var tmpRect = obj.getBoundingClientRect();
obj.style.width = "auto"; 
obj.style.height = "auto"; 
var Ret = obj.getBoundingClientRect(); 
obj.style.width = (tmpRect.right - tmpRect.left).toString() + "px";
obj.style.height = (tmpRect.bottom - tmpRect.top).toString() + "px"; 
return Ret;
}

El ancho y alto de un texto se puede obtener con clientWidth y clientHeight

var element = document.getElementById ("mytext");

var width = element.clientWidth;
var height = element.clientHeight;

asegúrese de que la propiedad de posición del estilo esté establecida en absoluta

element.style.position = "absolute";

no es necesario estar dentro de un div, puede estar dentro de un p o un span

Lo mejor es detectar si el texto encajará correctamente antes de mostrar el elemento.Entonces puedes usar esta función que no requiere que el elemento esté en la pantalla.

function textWidth(text, fontProp) {
    var tag = document.createElement("div");
    tag.style.position = "absolute";
    tag.style.left = "-999em";
    tag.style.whiteSpace = "nowrap";
    tag.style.font = fontProp;
    tag.innerHTML = text;

    document.body.appendChild(tag);

    var result = tag.clientWidth;

    document.body.removeChild(tag);

    return result;
}

Uso:

if ( textWidth("Text", "bold 13px Verdana") > elementWidth) {
    ...
}

Construyendo a partir de La respuesta de Deepak Nadar, Cambié los parámetros de funciones para aceptar texto y estilos de fuente.No es necesario hacer referencia a un elemento.También el fontOptions tienen valores predeterminados, por lo que no es necesario proporcionarlos todos.

(function($) {
  $.format = function(format) {
    return (function(format, args) {
      return format.replace(/{(\d+)}/g, function(val, pos) {
        return typeof args[pos] !== 'undefined' ? args[pos] : val;
      });
    }(format, [].slice.call(arguments, 1)));
  };
  $.measureText = function(html, fontOptions) {
    fontOptions = $.extend({
      fontSize: '1em',
      fontStyle: 'normal',
      fontWeight: 'normal',
      fontFamily: 'arial'
    }, fontOptions);
    var $el = $('<div>', {
      html: html,
      css: {
        position: 'absolute',
        left: -1000,
        top: -1000,
        display: 'none'
      }
    }).appendTo('body');
    $(fontOptions).each(function(index, option) {
      $el.css(option, fontOptions[option]);
    });
    var h = $el.outerHeight(), w = $el.outerWidth();
    $el.remove();
    return { height: h, width: w };
  };
}(jQuery));

var dimensions = $.measureText("Hello World!", { fontWeight: 'bold', fontFamily: 'arial' });

// Font Dimensions: 94px x 18px
$('body').append('<p>').text($.format('Font Dimensions: {0}px x {1}px', dimensions.width, dimensions.height));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

En caso de que alguien más haya llegado aquí buscando una forma de medir el ancho de una cuerda. y una forma de saber cuál es el tamaño de fuente más grande que cabe en un ancho particular, aquí hay una función que se basa en la solución de @Domi con una búsqueda binaria:

/**
 * Find the largest font size (in pixels) that allows the string to fit in the given width.
 * 
 * @param {String} text The text to be rendered.
 * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold ?px verdana") -- note the use of ? in place of the font size.
 * @param {width} the width in pixels the string must fit in
 * @param {minFontPx} the smallest acceptable font size in pixels
 * @param {maxFontPx} the largest acceptable font size in pixels
**/
function GetTextSizeForWidth( text, font, width, minFontPx, maxFontPx )
{
    for ( ; ; )
    {
        var s = font.replace( "?", maxFontPx );
        var w = GetTextWidth( text, s );
        if ( w <= width )
        {
            return maxFontPx;
        }

        var g = ( minFontPx + maxFontPx ) / 2;

        if ( Math.round( g ) == Math.round( minFontPx ) || Math.round( g ) == Math.round( maxFontPx ) )
        {
            return g;
        }

        s = font.replace( "?", g );
        w = GetTextWidth( text, s );
        if ( w >= width )
        {
            maxFontPx = g;
        }
        else
        {
            minFontPx = g;
        }
    }
}

Supongo que esto es bastante similar a la entrada de Depak, pero se basa en el trabajo de Louis Lazaris publicado en un artículo en impresionante pagina web

(function($){

        $.fn.autofit = function() {             

            var hiddenDiv = $(document.createElement('div')),
            content = null;

            hiddenDiv.css('display','none');

            $('body').append(hiddenDiv);

            $(this).bind('fit keyup keydown blur update focus',function () {
                content = $(this).val();

                content = content.replace(/\n/g, '<br>');
                hiddenDiv.html(content);

                $(this).css('width', hiddenDiv.width());

            });

            return this;

        };
    })(jQuery);

El evento fit se utiliza para ejecutar la llamada a la función inmediatamente después de que la función se asocia al control.

p.ej.:$('entrada').autofit().trigger("ajuste");

Sin jQuery:

String.prototype.width = function (fontSize) {
    var el,
        f = fontSize + " px arial" || '12px arial';
    el = document.createElement('div');
    el.style.position = 'absolute';
    el.style.float = "left";
    el.style.whiteSpace = 'nowrap';
    el.style.visibility = 'hidden';
    el.style.font = f;
    el.innerHTML = this;
    el = document.body.appendChild(el);
    w = el.offsetWidth;
    el.parentNode.removeChild(el);
    return w;
}

// Usage
"MyString".width(12);

Violín de ejemplo práctico: http://jsfiddle.net/tdpLdqpo/1/

HTML:

<h1 id="test1">
    How wide is this text?
</h1>
<div id="result1"></div>
<hr/>
<p id="test2">
    How wide is this text?
</p>
<div id="result2"></div>
<hr/>
<p id="test3">
    How wide is this text?<br/><br/>
    f sdfj f sdlfj lfj lsdk jflsjd fljsd flj sflj sldfj lsdfjlsdjkf sfjoifoewj flsdjfl jofjlgjdlsfjsdofjisdojfsdmfnnfoisjfoi  ojfo dsjfo jdsofjsodnfo sjfoj ifjjfoewj fofew jfos fojo foew jofj s f j
</p>
<div id="result3"></div>

Código JavaScript:

function getTextWidth(text, font) {
    var canvas = getTextWidth.canvas ||
        (getTextWidth.canvas = document.createElement("canvas"));
    var context = canvas.getContext("2d");
    context.font = font;
    var metrics = context.measureText(text);
    return metrics.width;
};

$("#result1")
.text("answer: " +
    getTextWidth(
             $("#test1").text(),
             $("#test1").css("font")) + " px");

$("#result2")
    .text("answer: " +
        getTextWidth(
             $("#test2").text(),
             $("#test2").css("font")) + " px");

$("#result3")
    .text("answer: " +
        getTextWidth(
             $("#test3").text(),
             $("#test3").css("font")) + " px");

El Element.getClientRects() El método devuelve una colección de DOMRect objetos que indican los rectángulos delimitadores para cada cuadro de borde CSS en un cliente.El valor devuelto es una colección de DOMRect objetos, uno para cada cuadro de borde CSS asociado con el elemento.Cada DOMRect el objeto contiene solo lectura left, top, right y bottom propiedades que describen el cuadro de borde, en píxeles, con la parte superior izquierda relativa a la parte superior izquierda de la ventana gráfica.

Elemento.getClientRects() por Colaboradores de Mozilla tiene licencia bajo CC-BY-SA 2.5.

La suma de todos los anchos de rectángulo devueltos produce el ancho total del texto en píxeles.

document.getElementById('in').addEventListener('input', function (event) {
    var span = document.getElementById('text-render')
    span.innerText = event.target.value
    var rects = span.getClientRects()
    var widthSum = 0
    for (var i = 0; i < rects.length; i++) {
        widthSum += rects[i].right - rects[i].left
    }
    document.getElementById('width-sum').value = widthSum
})
<p><textarea id='in'></textarea></p>
<p><span id='text-render'></span></p>
<p>Sum of all widths: <output id='width-sum'>0</output>px</p>

Hice un pequeño módulo ES6 (usa jQuery):

import $ from 'jquery';

const $span=$('<span>');
$span.css({
    position: 'absolute',
    display: 'none'
}).appendTo('body');

export default function(str, css){
    $span[0].style = ''; // resetting the styles being previously set
    $span.text(str).css(css || {});
    return $span.innerWidth();
}

Fácil de usar:

import stringWidth from './string_width';
const w = stringWidth('1-3', {fontSize: 12, padding: 5});

Lo interesante que puedes notar: permite tener en cuenta cualquier atributo CSS, ¡incluso los rellenos!

var textWidth = (function (el) {
    el.style.position = 'absolute';
    el.style.top = '-1000px';
    document.body.appendChild(el);

    return function (text) {
        el.innerHTML = text;
        return el.clientWidth;
    };
})(document.createElement('div'));
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top