Obliczanie szerokości tekstu

101

Próbuję obliczyć szerokość tekstu za pomocą jQuery. Nie wiem co, ale zdecydowanie robię coś złego.

Oto kod:

var c = $('.calltoaction');

var cTxt = c.text();

var cWidth =  cTxt.outerWidth();

c.css('width' , cWidth);
Dom
źródło
Więc w jaki sposób ten kod nie działa? Co musi zrobić inaczej?
Sixten Otto

Odpowiedzi:

142

To działało lepiej dla mnie:

$.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
źródło
4
+1 - to naprawdę mierzy tekst, a nie tylko zawierający element blokowy, taki jak rozwiązanie brainreavis.
Ben
Przysiek. +1. Wygląda na to, że brakuje średnika w trzecim wierszu po '</span>', chociaż nie wydaje się to robić różnicy (działało z nim lub bez niego na FF9).
shmeeps
21
Uważaj jednak ... ta metoda usunie powiązania wszystkich zdarzeń na elementach potomnych.
brianreavis
Próbowałem użyć tej funkcji na wiele sposobów, ale zwraca ona wartość null. Czy ktoś mógłby opublikować składnię opisującą, jak ta funkcja jest faktycznie używana? Czy uruchamiam funkcję .textWidth () na obiekcie jQuery? Czy przekazuję tekst do tej funkcji? Czy uruchamiam tę funkcję jako funkcję jQuery (tj. $ .TextWidth (jqObject)? Ponieważ żadna z tych opcji nie wydaje się działać. Dzięki ...
moosefetcher
2
@moosefetcher zrobiłbyś $ (selector) .textWidth (); Zajrzyj tutaj learn.jquery.com/plugins/basic-plugin-creation/…
uesports135
89

Oto funkcja, która jest lepsza niż inne opublikowane, ponieważ

  1. jest krótszy
  2. To działa, gdy przepuszczanie <input>, <span>lub "string".
  3. jest szybszy w przypadku częstych zastosowań, ponieważ ponownie wykorzystuje istniejący element DOM.

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
źródło
1
Łał! Czysta niesamowitość!
Michel Triana,
3
Świetne rozwiązanie, myślę, że należy przyjąć odpowiedź! Dzięki kolego!
Ilia Rostovtsev
Fantastico! Proste i czyste.
Carey Estes,
Chociaż dobrze, to nie obsługuje spacji. Zobacz odpowiedź @ edsioufi, aby uzyskać informacje na temat ulepszenia tego rozwiązania.
Badgerspot,
Jeśli wewnątrz elementu znajduje się ukryty tekst, spowoduje to odchylenie szerokości. Aby to uwzględnić, pamiętaj o usunięciu ukrytych węzłów przed obliczeniem szerokości tekstu. Aby bezpiecznie usunąć ukryte węzły, prawdopodobnie powinieneś najpierw utworzyć klon i tam usunąć.
thdoan
36

Moje rozwiązanie

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

Zasadniczo ulepszenie w stosunku do Rune, które nie działa .htmltak lekko

bevacqua
źródło
Autor: Ajarn Gerhard, który opublikował to jako odpowiedź: To nie działa w IE8, ponieważ brakuje ci /przed nawiasem zamykającym <span>tagu:calculator = $('<span style="display: inline-block;" />'),
Petr R.
1
Wydaje się, że zwraca wartość null, jeśli pod elementem, który mierzysz, znajduje się element potomny. Czy może działać bez elementu podrzędnego? Zobacz jsfiddle.net/h22tj/41
Casper
+1 - Nigdy nie wiedziałem, że było rozpakowanie (), które wywołało u mnie niekończący się smutek.
Orwellophile
W Chrome zawsze zwraca wartość null.
emeraldhieu
12

Te textWidthfunkcje przewidziane w odpowiedziach i że akceptują stringjako argument nie będą stanowić dla początkowych i końcowych spacji (te, które nie są świadczone w pojemniku manekina). Ponadto nie będą działać, jeśli tekst zawiera jakiekolwiek znaczniki HTML (podłańcuch <br>nie wygeneruje żadnych danych wyjściowych i &nbsp;zwróci długość jednej spacji).

Jest to problem tylko dla textWidthfunkcji, które akceptują a string, ponieważ jeśli podano element DOM i .html()jest on wywoływany dla elementu, to prawdopodobnie nie ma potrzeby naprawiania tego w takim przypadku użycia.

Ale jeśli, na przykład, obliczasz szerokość tekstu, aby dynamicznie modyfikować szerokość inputelementu tekstowego w trakcie pisania przez użytkownika (mój obecny przypadek użycia), prawdopodobnie będziesz chciał zamienić spacje wiodące i końcowe na &nbsp;i zakodować ciąg do html.

Użyłem rozwiązania philfreo, więc tutaj jest wersja, która rozwiązuje ten problem (z komentarzami do dodatków):

$.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
źródło
Dzięki. Twoja odpowiedź jest najcenniejsza.
Vishal
1
Co dziwne, kiedy wypróbowałem ten, nie skopiował poprawnie pliku font-size. Musiałem dodać css('font-size'), this.css('font-size')), żeby zadziałało. Jakieś pomysły, dlaczego fontsam nie kopiuje tej wartości?
Gone Coding,
@GoneCoding Gdzie dodałeś te dodatki?
webmagnets
@webmagnets: To samo miejsce co dotychczasowa .cssmetoda, ale widzę, że moje nawiasy są nieprawidłowe. Powinien być css('font-size', this.css('font-size')).
Gone Coding
11

Funkcje szerokości jQuery mogą być nieco niewyraźne podczas próby określenia szerokości tekstu z powodu niespójnych modeli ramek. Pewnym sposobem byłoby wstrzyknięcie div wewnątrz elementu, aby określić rzeczywistą szerokość tekstu:

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

Aby użyć tej mini wtyczki, po prostu:

$('.calltoaction').textWidth();
brianreavis
źródło
1
Czy nie dałoby to raczej szerokości bloku niż tekstu?
Josh Rickard,
-1 To rzeczywiście wydaje się mierzyć szerokość ramki, a nie szerokość tekstu.
Nathan Arthur
1
Czy użycie rozpiętości zamiast elementu div nie rozwiązałoby problemu z szerokością? Jeśli tak, to ta funkcja jest nieco lepsza niż zaakceptowana odpowiedź.
Josh
@Josh: Jeśli zastąpisz to a span, po prostu otrzymasz zero. Wypróbowałem to rozwiązanie w przypadku H2, gdy elementy nadrzędne mają ukryte przepełnienia i otrzymuję tylko obciętą długość tekstu. Być może wyjaśnienie, jak to ma działać , pomogłoby to poprawić?
Gone Coding
8

Okazało się, że to rozwiązanie działa dobrze i dziedziczy czcionkę pochodzenia przed dopasowaniem:

$.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
źródło
1
Skończyło się na tym, że użyłem tego, ale dodałem org.css("font-weight"). Powiedziałbym też, że ta if(!text)część jest nieintuicyjna. Gdybym użył np. jQuery("#someContainer").textWidth("Lorem ipsum")Chciałbym poznać szerokość tekstu "Lorem ipsum" po zastosowaniu w tym konkretnym kontenerze.
Urocze
8

Ani Rune, ani Brain nie działały dla mnie w przypadku, gdy element trzymający tekst miał stałą szerokość. Zrobiłem coś podobnego do Okamera. Używa mniej selektorów.

EDYCJA: Prawdopodobnie nie zadziała dla elementów, które używają względnych font-size, ponieważ poniższy kod wstawia htmlCalcelement do, w bodyten sposób tracąc informacje o relacji rodziców.

$.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
źródło
To było dla mnie najlepsze rozwiązanie z obsługą IE8 i nie zerwie żadnych powiązań z elementem w procesie! Dzięki.
ouija
Uwaga: w metodzie rozszerzenia jQuery thisjest już obiektem jQuery, więc $(this)jest nadmiarowy.
Gone Coding
W moim przypadku działało to źle, ponieważ obliczona szerokość jest za mała. Zaakceptowana odpowiedź zadziałała poprawnie
PandaWood
5

Jeśli próbujesz to zrobić z tekstem w polu wyboru lub jeśli te dwa nie działają, spróbuj zamiast tego:

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

lub

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

jeśli chcesz najpierw pobrać tekst

okamera
źródło
4

chodzi o to, że robisz źle, że wywołujesz metodę na cTxt, która jest prostym ciągiem znaków, a nie obiektem jQuery. cTxt to naprawdę zawarty tekst.

Juraj Blahunka
źródło
2

Niewielka zmiana na wartość Nico, ponieważ .children () zwróci pusty zestaw, jeśli odwołujemy się do elementu tekstowego, takiego jak h1 lub p . Więc zamiast tego użyjemy .contents () i tego zamiast $ (this), ponieważ tworzymy metodę na obiekcie 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
źródło
1

po dwóch dniach ścigania ducha, próbując dowiedzieć się, dlaczego szerokość tekstu jest nieprawidłowa, zdałem sobie sprawę, że jest to spowodowane białymi spacjami w ciągu tekstowym, które zatrzymywały obliczanie szerokości.

więc kolejną wskazówką jest sprawdzenie, czy spacje powodują problemy. posługiwać się

&nbsp;

nie działającą przestrzeń i zobacz, czy to naprawi.

inne funkcje, które ludzie sugerowali, również działają dobrze, ale to były białe spacje powodujące problemy.

zonabi
źródło
Po prostu tymczasowo dodaj white-space: nowrapdo wbudowanego CSS, aby tego uniknąć.
Gone Coding
1

Jeśli próbujesz określić szerokość kombinacji węzłów tekstowych i elementów wewnątrz danego elementu, musisz zawinąć całą zawartość metodą wrapInner () , obliczyć szerokość, a następnie rozpakować zawartość.

* Uwaga: Będziesz także musiał rozszerzyć jQuery, aby dodać funkcję unrapInner (), ponieważ nie jest ona dostarczana domyślnie.

$.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
źródło
+1 dla .wrapInner. Jak zapewne wiesz, zajmowanie się czystymi elementami tekstowymi w jQuery to piekło. Jeśli istnieją inne metody jQuery, które mają zastosowanie, daj mi znać. Musiałem uciec się do .get (0) .children i nie jest to ładne.
Orwellophile
1

Rozwinięcie odpowiedzi @ philfreo:

Dodałem możliwość sprawdzania text-transform, ponieważ rzeczy takie jak text-transform: uppercasezwykle powodują, że tekst jest szerszy.

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

Wywołaj getColumnWidth (), aby uzyskać z tekstu. Działa to doskonale.

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

Uwaga: - Jeśli chcesz, aby wbudowane pliki .css przekazywały szczegóły czcionki tylko w stylu.

Arun Pratap Singh
źródło
0

Zmodyfikowałem kod Nico, aby działał na moje potrzeby.

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

Używam .contents (), ponieważ .children () nie zwraca węzłów tekstowych, których potrzebowałem. Odkryłem również, że na zwróconą szerokość wpłynęła szerokość widocznego obszaru, która powodowała zawijanie, więc używam białych znaków: nowrap; aby uzyskać prawidłową szerokość niezależnie od szerokości rzutni.

non_carbondated
źródło
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;
    };

prawie jedna wkładka. Daje ci pierwszy znak

ceed
źródło
0

Nie mogłem sprawić, aby żadne z wymienionych rozwiązań działało w 100%, więc wymyśliłem tę hybrydę , opartą na pomysłach z @chmurson (który z kolei był oparty na @Okamera), a także z @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);

Uwagi:

  • this wewnątrz metody rozszerzenia jQuery znajduje się już obiekt jQuery, więc nie ma potrzeby stosowania wszystkich dodatkowych $(this) które ma wiele przykładów.
  • Dodaje element atrapy do ciała tylko raz i używa go ponownie.
  • Powinieneś również określić white-space: nowrap, aby upewnić się, że mierzy go jako pojedynczą linię (a nie zawijanie wierszy w oparciu o inne style strony).
  • Nie mogłem zmusić tego do pracy, używając fontsamego i musiałem również wyraźnie skopiować font-size. Jeszcze nie wiem, dlaczego (nadal badam).
  • To nie obsługuje pól wejściowych w ten sposób @philfreo.
Gone Coding
źródło
0

Czasami trzeba również zmierzyć dodatkowo wysokość i nie tylko tekst , ale także szerokość HTML . Wziąłem odpowiedź @philfreo i uczyniłem ją bardziej elastyczną i użyteczną:

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
źródło
0

szerokość tekstu może być różna dla różnych rodziców, na przykład jeśli dodasz tekst do tagu h1, będzie szerszy niż div lub label, więc moje rozwiązanie wygląda następująco:

<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
źródło
0

Miałem problem z rozwiązaniami takimi jak @ rune-kaagaard dla dużych ilości tekstu. Odkryłem to:

$.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
źródło
-1

myślę, że powinieneś użyć $('.calltoaction').val;

również użyłbym do tego id (#) zamiast klasy .. jeśli masz więcej niż jedną z takich klas, jak by sobie z tym poradził?

Noam Smadja
źródło
Tak, w dokumencie jest więcej niż jeden. Dlaczego sugerowałbyś id?
Dom