Расчет ширины текста

101

Я пытаюсь рассчитать ширину текста с помощью jQuery. Не знаю, что, но я определенно делаю что-то не так.

Итак, вот код:

var c = $('.calltoaction');

var cTxt = c.text();

var cWidth =  cTxt.outerWidth();

c.css('width' , cWidth);
Дом
источник
Итак, каким образом этот код не работает? Что нужно сделать по-другому?
Сикстен Отто

Ответы:

142

У меня это сработало лучше:

$.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;
};
Руна Каагаард
источник
4
+1 - это действительно измеряет текст, а не только содержащий элемент блока, как решение brainreavis.
Бен
Slick. +1. Похоже, вам не хватает точки с запятой в третьей строке после '</span>', хотя это, похоже, не имеет значения (работает с ней или без нее на FF9).
shmeeps
21
Однако будьте осторожны ... этот метод отвяжет любые события дочерних элементов.
brianreavis 03
Я пытался использовать эту функцию разными способами, но она продолжает возвращать значение null. Может ли кто-нибудь опубликовать синтаксис того, как на самом деле используется эта функция? Могу ли я запустить функцию .textWidth () для объекта jQuery? Могу ли я передать текст в эту функцию? Могу ли я запускать эту функцию как функцию jQuery (например, $ .textWidth (jqObject)? Потому что ни один из этих параметров, похоже, не работает. Спасибо ...
moosefetcher
2
@moosefetcher вы бы сделали $ (selector) .textWidth (); Посмотрите здесь learn.jquery.com/plugins/basic-plugin-creation/…
uesports135
89

Вот функция, которая лучше, чем другие опубликованные, потому что

  1. это короче
  2. он работает при пропускании <input>, <span>или "string".
  3. это быстрее для частого использования, потому что он повторно использует существующий элемент DOM.

Демо: 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();
};
Филфрео
источник
1
Вот Это Да! Настоящее великолепие!
Мишель Триана
3
Отличное решение, я думаю, должен быть принят ответ! Спасибо, приятель!
Илья Ростовцев
Фантастико! Просто и чисто.
Кэри Эстес
Это хорошо, но не обрабатывает пробелы. См. Ответ @ edsioufi для улучшения этого решения.
Badgerpot
Если внутри элемента есть скрытый текст, он будет искажать вашу ширину. Чтобы учесть это, не забудьте удалить скрытые узлы перед вычислением ширины текста. Чтобы безопасно удалить скрытые узлы, вам, вероятно, следует сначала создать клон и выполнить удаление там.
thdoan
36

Мое решение

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

По сути, улучшение по сравнению с Рунами, которое не .htmlтак легко использовать

Bevacqua
источник
Автор Ajarn Gerhard, который разместил это в качестве ответа: Это не работает в IE8, потому что вам не хватает /перед закрывающей скобкой <span>тега:calculator = $('<span style="display: inline-block;" />'),
Петр Р.
1
Кажется, что это возвращает null, если под измеряемым элементом есть дочерний элемент. Можно ли заставить его работать без дочернего элемента? См. Jsfiddle.net/h22tj/41
Casper
+1 - Я никогда не знал, что есть разворачивание (), которое доставило мне бесконечное горе.
Orwellophile
Он всегда возвращает null в Chrome.
emeraldhieu
12

Эти textWidthфункции , предусмотренные в ответах , и которые принимают в stringкачестве аргумента , не будет составлять начальную и конечные пробела (те , которые не отображаются в контейнере фиктивного). Кроме того, они не будут работать, если текст содержит какую-либо разметку html (подстрока <br>не будет производить никакого вывода и &nbsp;вернет длину одного пробела).

Это проблема только для textWidthфункций, которые принимают a string, потому что, если элемент DOM задан и .html()вызывается элементом, то, вероятно, нет необходимости исправлять это для такого варианта использования.

Но если, например, вы вычисляете ширину текста для динамического изменения ширины текстового inputэлемента в зависимости от типов пользователей (мой текущий вариант использования), вы, вероятно, захотите заменить начальные и конечные пробелы на &nbsp;и закодировать строку в html.

Я использовал решение philfreo, поэтому вот его версия, которая это исправляет (с комментариями к дополнениям):

$.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();
};
Эдсиуфи
источник
Спасибо. Ваш ответ очень ценен.
Vishal
1
Как ни странно, когда я попробовал этот, он неправильно скопировал font-size. Пришлось добавить, css('font-size'), this.css('font-size'))чтобы он заработал. Любые идеи, почему fontодно не копирует это значение?
Gone Coding
@GoneCoding Где вы добавили эти дополнения?
webmagnets 05
@webmagnets: то же место, что и существующий .cssметод, но я вижу, что мои скобки неправильные. Должно быть css('font-size', this.css('font-size')).
Gone Coding
11

Функции ширины jQuery могут быть немного сомнительными при попытке определить ширину текста из-за несовместимых блочных моделей. Верным способом было бы вставить div внутри вашего элемента, чтобы определить фактическую ширину текста:

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

Чтобы использовать этот мини-плагин, просто:

$('.calltoaction').textWidth();
Brianreavis
источник
1
Разве это не даст вам ширину блока, а не текст?
Джош Рикард,
-1 Это действительно похоже на измерение ширины поля, а не ширины текста.
Натан Артур
1
Разве простое использование диапазона вместо div не решит проблему ширины? Если да, то эта функция немного лучше принятого ответа.
Джош
@Josh: Если вы замените его на, spanвы просто получите ноль. Пробовал это решение на H2, где у родительских элементов скрыто переполнение, и я получаю только усеченную длину текста. Возможно, объяснение того, как это должно работать , поможет улучшить его работу?
Gone Coding
8

Я обнаружил, что это решение работает хорошо, и оно наследует исходный шрифт перед изменением размера:

$.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;
}
Винс Спайсер
источник
1
В итоге я использовал это, но добавил org.css("font-weight"). Также я бы сказал, что эта if(!text)часть не интуитивно понятна. Если я использую, например, jQuery("#someContainer").textWidth("Lorem ipsum")я хотел бы знать ширину текста «Lorem ipsum» при применении в этом конкретном контейнере.
Sleavely
8

Ни Rune, ни Brain у меня не работали в том случае, если элемент, содержащий текст, имел фиксированную ширину. Я сделал что-то похожее на Окамеру. Он использует меньше селекторов.

РЕДАКТИРОВАТЬ: это, вероятно, не сработает для элементов, которые используют относительные font-size, поскольку следующий код вставляет htmlCalcэлемент в, bodyтаким образом, теряет информацию о родительских отношениях.

$.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
источник
Для меня это было лучшее решение с поддержкой IE8, и оно не нарушит никаких привязок к элементу в процессе! Спасибо.
ouija
Примечание. В методе расширения jQuery thisуже является объектом jQuery, поэтому $(this)он избыточен.
Gone Coding
У меня это сработало неправильно, потому что рассчитанная ширина слишком мала. Принятый ответ сработал правильно
PandaWood
5

Если вы пытаетесь сделать это с текстом в поле выбора или если эти два не работают, попробуйте это вместо этого:

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

или

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

если вы хотите сначала схватить текст

окамера
источник
4

дело в том, что вы делаете неправильно, что вы вызываете метод cTxt, который представляет собой простую строку, а не объект jQuery. cTxt - это действительно содержащийся текст.

Юрай Блахунка
источник
2

Небольшое изменение для Нико, поскольку .children () вернет пустой набор, если мы ссылаемся на текстовый элемент, такой как h1 или p . Поэтому мы будем использовать .contents () вместо этого и использовать this вместо $ (this), поскольку мы создаем метод для объекта 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;
    };
309дизайнер
источник
1

после двух дней погони за призраком, пытаясь понять, почему ширина текста была неправильной, я понял, что это было из-за пробелов в текстовой строке, которые остановили вычисление ширины.

Итак, еще один совет - проверить, не вызывают ли пробелы проблемы. использовать

&nbsp;

неразрывное пространство и посмотрите, исправит ли это его.

другие функции, которые люди предлагали, тоже работают хорошо, но проблемы вызывают пробелы.

дзонаби
источник
Просто временно добавьте white-space: nowrapвстроенный CSS, чтобы избежать этого.
Gone Coding
1

Если вы пытаетесь определить ширину комбинации текстовых узлов и элементов внутри данного элемента, вам необходимо обернуть все содержимое с помощью wrapInner () , вычислить ширину, а затем развернуть содержимое.

* Примечание. Вам также потребуется расширить jQuery, чтобы добавить функцию unwrapInner (), поскольку она не предоставляется по умолчанию.

$.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;
  }
});
поездка41
источник
+1 за .wrapInner. Как вы, наверное, знаете, работать с чистыми текстовыми элементами в jQuery - ад. Если есть какие-либо другие подходящие методы jQuery, сообщите мне. Мне пришлось прибегнуть к .get (0) .children, и это некрасиво.
Orwellophile
1

Расширение ответа @ philfreo:

Я добавил возможность проверки text-transform, так как такие вещи text-transform: uppercaseобычно делают текст шире.

$.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
источник
0
var calc = '<span style="display:none; margin:0 0 0 -999px">' + $('.move').text() + '</span>';
Юра
источник
0

Вызовите getColumnWidth (), чтобы получить содержание текста. Это прекрасно работает.

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

Примечание: - Если вы хотите, чтобы встроенный .css передавал детали шрифта только в стиле.

Арун Пратап Сингх
источник
0

Я изменил код Нико для своих нужд.

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

Я использую .contents (), поскольку .children () не возвращает текстовые узлы, которые мне нужны. Я также обнаружил, что на возвращаемую ширину влияет ширина области просмотра, которая вызвала перенос, поэтому я использую white-space: nowrap; чтобы получить правильную ширину независимо от ширины области просмотра.

non_carbondated
источник
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;
    };

почти один лайнер. Дает вам знак первого символа

ceed
источник
0

Я не смог заставить ни одно из перечисленных решений работать на 100%, поэтому придумал этот гибрид , основанный на идеях от @chmurson (который, в свою очередь, был основан на @Okamera), а также от @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);

Ноты:

  • this внутри метода расширения jQuery уже есть объект jQuery, поэтому нет необходимости во всех дополнительных $(this) которые есть во многих примерах.
  • Он только один раз добавляет фиктивный элемент в тело и использует его повторно.
  • Вы также должны указать white-space: nowrap, чтобы убедиться, что он измеряется как одна строка (а не перенос строки на основе других стилей страницы).
  • Я не мог заставить это работать, используя fontодин, и мне также пришлось явно копировать font-size. Пока не уверен, почему (все еще исследую).
  • Это не поддерживает поля ввода таким образом @philfreo.
Исчезло кодирование
источник
0

Иногда нужно дополнительно измерить высоту и не только текст , но и ширину HTML . Я взял ответ @philfreo и сделал его более гибким и полезным:

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()
  };
}
Даниэль Кмак
источник
0

ширина текста может быть разной для разных родителей, например, если вы добавляете текст в тег h1, он будет шире, чем div или label, поэтому мое решение вроде этого:

<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;
}
Саид Таран
источник
0

У меня были проблемы с решениями типа @ rune-kaagaard для больших объемов текста. Я обнаружил это:

$.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
источник
-1

я думаю, тебе стоит использовать $('.calltoaction').val;

Кроме того, я бы использовал id (#) вместо класса для этого ... если у вас есть несколько таких классов, как бы он с этим справился?

Ноам Смаджа
источник
Да, в документе их несколько. Почему вы предлагаете идентификатор?
Dom