Calculando a largura do texto

101

Estou tentando calcular a largura do texto usando jQuery. Não tenho certeza do quê, mas definitivamente estou fazendo algo errado.

Então, aqui está o código:

var c = $('.calltoaction');

var cTxt = c.text();

var cWidth =  cTxt.outerWidth();

c.css('width' , cWidth);
Dom
fonte
Então, de que maneira esse código não está funcionando? O que precisa ser diferente?
Sixten Otto,

Respostas:

142

Isso funcionou melhor para mim:

$.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
fonte
4
+1 - mede verdadeiramente o texto, não apenas o elemento de bloco que o contém, como a solução do Brainreavis.
Ben
Liso. +1. Parece que falta um ponto e vírgula na terceira linha depois '</span>', embora não pareça fazer diferença (funcionou com ou sem ele no FF9).
shmeeps
21
Porém, tenha cuidado com isso ... este método desvinculará quaisquer eventos em elementos filho.
brianreavis
Tentei usar essa função de várias maneiras, mas ela continua retornando nulo. Alguém poderia postar a sintaxe de como essa função é realmente usada? Devo executar a função .textWidth () em um objeto jQuery? Devo passar texto para esta função? Devo executar a função como uma função jQuery (ou seja, $ .textWidth (jqObject)? Porque nenhuma dessas opções parece funcionar. Obrigado ...
moosefetcher
2
@moosefetcher você faria $ (selector) .textWidth (); Olhe aqui learn.jquery.com/plugins/basic-plugin-creation/…
uesports135
89

Esta é uma função melhor do que outras postadas porque

  1. é mais curto
  2. ele funciona quando passar um <input>, <span>ou "string".
  3. é mais rápido para usos frequentes porque reutiliza um elemento DOM existente.

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
fonte
1
Uau! Pura grandiosidade!
Michel Triana
3
Ótima solução, deve ser aceita resposta, eu acho! Valeu cara!
Ilia Rostovtsev
Fantastico! Simples e limpo.
Carey Estes
Embora seja bom, isso não controla espaços. Consulte a resposta de @edsioufi para ver um aprimoramento dessa solução.
Badgerspot
Se houver texto oculto dentro do elemento, ele distorcerá sua largura. Para explicar isso, certifique-se de remover os nós ocultos antes de calcular a largura do texto. Para remover com segurança os nós ocultos, você provavelmente deve primeiro fazer um clone e fazer a remoção lá.
thdoan
36

Minha solução

$.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;
};

Basicamente, uma melhoria em relação a Rune, que não usa .htmltão levianamente

bevacqua
fonte
Por Ajarn Gerhard que postou isso como uma resposta: Isso não está funcionando no IE8 porque está faltando um /antes do colchete de fechamento da <span>tag:calculator = $('<span style="display: inline-block;" />'),
Petr R.
1
Isso parece retornar nulo se houver um elemento filho abaixo do elemento que você está medindo. Ele pode funcionar sem o elemento filho? Consulte jsfiddle.net/h22tj/41
Casper
+1 - Eu nunca soube que havia um desembrulhar (), que me causou uma dor sem fim.
Orwellophile
Ele sempre retorna nulo no Chrome.
emeraldhieu
12

As textWidthfunções fornecidas nas respostas e que aceitam a stringcomo argumento não levarão em conta espaços em branco à esquerda e à direita (aqueles não são renderizados no contêiner fictício). Além disso, eles não funcionarão se o texto contiver qualquer marcação html (uma sequência secundária <br>não produzirá nenhuma saída e &nbsp;retornará o comprimento de um espaço).

Este é apenas um problema para as textWidthfunções que aceitam um string, porque se um elemento DOM é fornecido e .html()é chamado sobre o elemento, então provavelmente não há necessidade de corrigir isso para esse caso de uso.

Mas se, por exemplo, você está calculando a largura do texto para modificar dinamicamente a largura de um inputelemento de texto conforme o usuário digita (meu caso de uso atual), você provavelmente desejará substituir os espaços à esquerda e à direita por &nbsp;e codificar a string para html.

Usei a solução de philfreo, então aqui está uma versão dela que corrige isso (com comentários sobre adições):

$.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
fonte
Obrigado. Sua resposta é muito valiosa.
Vishal
1
Estranhamente, quando tentei este, não copiou corretamente o font-size. Tive que adicionar css('font-size'), this.css('font-size'))para que funcionasse. Alguma ideia por que fontsozinho não copia esse valor?
Gone Coding
@GoneCoding Onde você adicionou esses extras?
webmagnets
@webmagnets: Mesmo lugar do .cssmétodo existente , mas vejo que meus colchetes estão errados. Deve ser css('font-size', this.css('font-size')).
Gone Coding
11

As funções de largura do jQuery podem ser um pouco confusas ao tentar determinar a largura do texto devido a modelos de caixa inconsistentes. A maneira certa seria injetar div dentro do seu elemento para determinar a largura real do texto:

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

Para usar este mini plug-in, basta:

$('.calltoaction').textWidth();
Brianreavis
fonte
1
Isso não daria a você a largura do bloco em vez do texto?
Josh Rickard
-1 Isso realmente parece medir a largura da caixa em vez da largura do texto.
Nathan Arthur
1
Simplesmente usar um span em vez de um div não resolveria o problema de largura? Se sim, então esta função é um pouco melhor do que a resposta aceita.
Josh
@Josh: Se você substituí-lo por um, spanvocê obtém zero. Tentei essa solução em um H2, em que os elementos pai têm overflow oculto e só consigo o comprimento truncado do texto. Talvez uma explicação de como isso deveria funcionar ajudasse a fazer com que funcionasse melhor?
Gone Coding
8

Achei que essa solução funciona bem e herda a fonte de origem antes do tamanho:

$.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
fonte
1
Acabei usando isso, mas acrescentei org.css("font-weight"). Além disso, eu diria que a if(!text)parte não é intuitiva. Se eu usar, por exemplo jQuery("#someContainer").textWidth("Lorem ipsum"), gostaria de saber a largura do texto de "Lorem ipsum" quando aplicado naquele contêiner específico.
Sleavelly
8

Nem Rune nem Brain estavam funcionando para mim no caso de o elemento que estava segurando o texto ter largura fixa. Fiz algo semelhante ao Okamera. Ele usa menos seletores.

EDITAR: Provavelmente não funcionará para elementos que usam relativo font-size, já que o código a seguir insere o htmlCalcelemento em, bodyassim, perde a informação sobre a relação dos pais.

$.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
fonte
Esta foi a melhor solução para mim com suporte ao IE8 e não quebrará nenhum vínculo com o elemento no processo! THX.
ouija
nota: em um método de extensão jQuery, thisjá é um objeto jQuery, portanto, $(this)é redundante.
Gone Coding
Isso funcionou incorretamente para mim, pois a largura calculada é muito pequena. A resposta aceita funcionou corretamente
PandaWood
5

Se você está tentando fazer isso com texto em uma caixa de seleção ou se os dois não estão funcionando, tente este:

$.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;
};

ou

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

se você quiser pegar o texto primeiro

okamera
fonte
4

a coisa, você está fazendo errado, que você está chamando um método em cTxt, que é uma string simples e não um objeto jQuery. cTxt é realmente o texto contido.

Juraj Blahunka
fonte
2

Ligeira mudança no de Nico, já que .children () retornará um conjunto vazio se estivermos fazendo referência a um elemento de texto como h1 ou p . Portanto, usaremos .contents () em vez disso, e usaremos isso em vez de $ (this), pois estamos criando um método em um objeto jQuery.

$.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;
    };
309designer
fonte
1

depois de perseguir um fantasma por dois dias, tentando descobrir por que a largura de um texto estava incorreta, percebi que eram os espaços em branco na string de texto que impediriam o cálculo da largura.

então, outra dica é verificar se os espaços em branco estão causando problemas. usar

&nbsp;

espaço ininterrupto e veja se isso o conserta.

as outras funções que as pessoas sugeriram também funcionam bem, mas foram os espaços em branco que causaram problemas.

zonabi
fonte
Apenas adicione temporariamente white-space: nowrapao CSS embutido para evitar isso.
Gone Coding
1

Se você está tentando determinar a largura de uma mistura de nós de texto e elementos dentro de um determinado elemento, você precisa envolver todo o conteúdo com wrapInner () , calcular a largura e, em seguida, desembrulhar o conteúdo.

* Nota: Você também precisará estender o jQuery para adicionar uma função unbrapInner (), pois ela não é fornecida por padrão.

$.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;
  }
});
viagem 41
fonte
1 para .wrapInner. Como você provavelmente sabe, lidar com elementos de texto puro em jQuery é um inferno. Se houver qualquer outro método jQuery aplicável, por favor me avise. Eu tive que recorrer a .get (0) .children e não é bonito.
Orwellophile
1

Expandindo a resposta de @philfreo:

Eu adicionei a capacidade de verificar text-transform, já que coisas como text-transform: uppercasegeralmente tendem a tornar o texto mais amplo.

$.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
fonte
0
var calc = '<span style="display:none; margin:0 0 0 -999px">' + $('.move').text() + '</span>';
yura
fonte
0

Chame getColumnWidth () para obter o com do texto. Isso funciona perfeitamente bem.

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

Nota: - Se você quiser .css embutidos, passe os detalhes da fonte apenas no estilo.

Arun Pratap Singh
fonte
0

Modifiquei o código de Nico para atender às minhas necessidades.

$.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;
};

Estou usando .contents () porque .children () não retorna nós de texto que eu precisava. Também descobri que a largura retornada foi impactada pela largura da janela de visualização, o que estava causando quebra de linha, então estou usando espaço em branco: nowrap; para obter a largura correta, independentemente da largura da janela de visualização.

não_carbondado
fonte
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;
    };

quase um forro. Dá a você o com do primeiro personagem

ceder
fonte
0

Não consegui fazer com que nenhuma das soluções listadas funcionasse 100%, então criei este híbrido , baseado em ideias de @chmurson (que por sua vez era baseado em @Okamera) e também de @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);

Notas:

  • this dentro de um método de extensão jQuery já é um objeto jQuery, então não há necessidade de todos os extras $(this) que muitos exemplos têm.
  • Ele apenas adiciona o elemento fictício ao corpo uma vez e o reutiliza.
  • Você também deve especificar white-space: nowrap, apenas para garantir que mede como uma única linha (e não quebra de linha com base em outro estilo de página).
  • Não consegui fazer isso funcionar fontsozinho e tive que copiar explicitamente font-sizetambém. Não tenho certeza do porquê ainda (ainda investigando).
  • Isso não suporta campos de entrada dessa forma @philfreo.
Gone Coding
fonte
0

Às vezes, você também precisa medir a altura adicional e não apenas o texto , mas também a largura do HTML . Peguei a resposta @philfreo e tornei-a mais flexível e útil:

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
fonte
0

a largura do texto pode ser diferente para pais diferentes, por exemplo, se você adicionar um texto na tag h1, ele será mais largo do que div ou rótulo, então minha solução é esta:

<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
fonte
0

Tive problemas com soluções como @ rune-kaagaard para grandes quantidades de texto. Eu descobri isso:

$.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

Peeto
fonte
-1

eu acho que você deveria usar $('.calltoaction').val;

Além disso, eu estaria usando id (#) em vez de uma classe para isso .. se vc tiver mais de uma dessas classes, como isso lidaria?

Noam Smadja
fonte
Sim, há mais de um no documento. Por que você sugeriria id?
Dom