Pergunta

Eu gostaria de usar o JavaScript para calcular a largura de uma string. Isso é possível sem ter que usar um tipo de letra monospace?

Se não é built-in, a minha única ideia é criar uma tabela de larguras para cada personagem, mas isso é bastante razoável especialmente apoiando Unicode e diferentes tamanhos de tipo (e todos os navegadores para que o assunto).

Foi útil?

Solução

Criar um DIV denominada com os seguintes estilos. Em seu JavaScript, defina o tamanho da fonte e atributos que você está tentando medir, coloque o seu cadeia na DIV, em seguida, ler a largura atual ea altura do DIV. Ele vai esticar para caber o conteúdo eo tamanho será dentro de alguns pixels do tamanho processado string.

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>

Outras dicas

Em HTML 5 , você pode simplesmente usar o href="http://www.w3schools.com/tags/canvas_measuretext.asp" método Canvas.measureText (mais explicações aqui ).

Tente este violino:

/**
 * 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 violino compara este método Canvas com uma variação de Bob Monteverde método , para que possa analisar e comparar a precisão dos resultados.

Existem várias vantagens para esta abordagem, incluindo:

  • Mais conciso e mais seguro do que os outros métodos (baseados em DOM) porque não alterar o estado global, tal como o seu DOM.
  • Além disso personalização é possível por modificar mais as propriedades do texto da lona , como textAlign e textBaseline.

NOTA: Quando você adiciona o texto para o seu DOM, lembre-se também ter em conta preenchimento, margem e borda .

NOTA 2: Em alguns navegadores, esse método produz precisão sub-pixel (resultado é um número de ponto flutuante), em outros não (resultado é apenas um int). Você pode querer executar Math.floor (ou Math.ceil) sobre o resultado, a inconsistências Evitar. Desde o método baseado em DOM nunca é sub-pixel preciso, este método tem ainda maior precisão do que os outros métodos aqui.

De acordo com a este JSPerf (graças aos contribuintes nos comentários), o método Canvas e método baseado em DOM são igualmente rápido, se o cache é adicionada ao método baseado em DOM e você não está usando o Firefox. No Firefox, por algum motivo, este método Canvas é muito, muito mais rápido do que o método baseado em DOM (a partir de setembro de 2014).

Aqui está um que eu chicoteado juntos sem exemplo. Parece que estamos todos na mesma 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;
}

Usá-lo é simples: "a string".width()

** Adicionado white-space: nowrap assim cordas com largura maior do que a largura da janela pode ser calculado.

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);

Isso funciona para mim ...

// 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;
}

O ExtJS biblioteca javascript tem uma grande classe chamada Ext.util.TextMetrics que " fornece medições de pixel precisas para blocos de texto para que você possa determinar exatamente como altura e largura, em pixels, de um determinado bloco de texto será". Você pode usá-lo diretamente ou ver a sua fonte para o código para ver como isso é feito.

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

Eu escrevi uma pequena ferramenta para isso. Talvez seja útil para alguém. Ele funciona sem 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

Fiddle: http://jsfiddle.net/PEvL8/

Eu gosto de sua "única ideia" de apenas fazer um caráter estático largura mapa! Ele realmente funciona bem para os meus propósitos. Às vezes, por razões de desempenho ou porque você não tem acesso fácil a um DOM, você pode apenas querer uma rápida calculadora autônomo hacky calibrado para uma única fonte. Então aqui está um calibrado para Helvetica; passar uma cadeia e (opcionalmente) um tamanho de letra:

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
}

Essa variedade feio gigante é larguras de caracteres ASCII indexados pelo código de caracteres. Portanto, este suportes apenas ASCII (caso contrário, assume uma largura média de caracteres). Felizmente, largura basicamente escala linearmente com o tamanho da fonte, assim que ele funciona muito bem em qualquer tamanho da fonte. É visivelmente sem qualquer consciência de kerning ou ligaduras ou o que quer.

Para "calibrar" Eu só prestado cada personagem até charCode 126 (o poderoso til) em um SVG e tem a caixa delimitadora e salva-lo a essa matriz; mais código e explicação e demonstração aqui .

Você pode usar a tela para que você não tem que lidar tanto com propriedades 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>

Isso deve funcionar, desde que a tag não tem outros estilos aplicada a ele. offsetWidth irá incluir a largura de quaisquer fronteiras, estofamento horizontal, a largura da barra de rolagem vertical, etc.

O código-snips abaixo, "calcular" a largura do vão-tag, acrescenta "..." a ele se o seu muito tempo e reduz o texto de comprimento, até que ele se encaixa em seu pai (ou até que tenha tentaram mais de mil vezes)

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 (com 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)
        }
    }
});

Tente 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;
}

A largura ea ALTURA de um texto pode ser obtida com clientWidth e clientHeight

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

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

Certifique-se de que a propriedade posição estilo é definida como absoluta

element.style.position = "absolute";

Não é necessário estar dentro de um div, pode estar dentro de um p ou um span

O melhor é detectar se o texto vai se encaixa bem antes de exibir o elemento. Então você pode usar essa função que não exige que o elemento seja na tela.

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) {
    ...
}

Construção fora do Deepak Nadar resposta , eu mudei as funções de parâmetros do que aceitar texto e estilos de fonte. Você não precisa fazer referência a um elemento. Além disso, o fontOptions têm padrões, assim você não precisa fornecer todos eles.

(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>

Em caso de alguém chegar aqui olhando tanto para uma forma de medir a largura de uma seqüência de e uma maneira de saber qual é o maior tamanho de fonte que vai caber em uma largura particular, aqui é uma função que se baseia @ solução de Domi com uma pesquisa binária:

/**
 * 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;
        }
    }
}

Eu acho que isso é prety semelhante a entrada Depak, mas é baseado na obra de Louis Lazaris publicada em um artigo no impressivewebs página

(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);

O evento ajuste é usado para executar a chamada de função inmediatly após a função é asociated ao controlo.

.

g .: $ ( 'input') autofit () gatilho ( "fit");.

Sem 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);

Fiddle de exemplo de trabalho: 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");

O método Element.getClientRects() retorna uma coleção de objetos DOMRect que indicam os retângulos de limite para cada beira caixa CSS em um cliente. O valor retornado é uma coleção de objetos DOMRect, um para cada caixa de fronteira CSS associado ao elemento. Cada objecto DOMRect contém somente leitura left, top, right e propriedades bottom descrevem caixa da fronteira, em pixels, com a parte superior esquerda em relao ao canto superior esquerdo da janela de visualização.

Element.getClientRects () por < a href = "https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects$history" rel = "nofollow noreferrer"> Mozilla Contribuintes está licenciado sob CC-BY-SA 2.5 .

Resumindo todas as larguras retângulo retornado produz a largura total do texto em pixels.

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>

Eu fiz um módulo minúsculo ES6 (usos 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});

Cool coisa que você pode perceber - que permite ter em conta todos os atributos CSS, mesmo paddings

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top