我想使用JavaScript来计算字符串的宽度。如果不使用单行字体,这可能吗?

如果它不是内置的,我唯一的想法是为每个字符创建一个宽度表,但这是非常不合理的,特别是支持Unicode和不同的类型大小(以及所有浏览器)。


用下面的样式创建一个DIV。在JavaScript中,设置你要测量的字体大小和属性,将字符串放入DIV中,然后读取DIV的当前宽度和高度,它将拉伸以适应内容,大小将在字符串渲染大小的几个像素内。

var fontSize = 12; var test = document.getElementById(" test "); test.style.fontSize = fontSize; Var高度= (test。clientHeight + 1) + "px"; Var宽度=(测试。clientWidth + 1) + "px" console.log(高度、宽度); #测试 { 位置:绝对的; 可见性:隐藏; 高度:汽车; 宽度:汽车; 空白:nowrap;}/*感谢Herb Caudill的评论*/ } < div id = "测试" > abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ < / div >


<span id="text">Text</span>

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

只要<span>标记没有应用其他样式,这就可以工作。 offsetWidth将包括任何边框的宽度,水平填充,垂直滚动条宽度等。


ExtJS javascript库中有一个很棒的类,叫做Ext.util.TextMetrics,它“为文本块提供精确的像素测量,这样你就可以准确地确定给定文本块的高和宽(以像素为单位)”。您可以直接使用它,也可以查看它的源代码以了解如何完成此操作。

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


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

下面的代码片段,“计算”span标签的宽度,如果它太长,就添加“…”,并减少文本长度,直到它适合它的父标签(或者直到它尝试了超过1000次)

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

这对我很有用……

// 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.textContent = pText;

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

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

    return lResult;
}

这是我随手拼凑的一个例子。看来我们都达成共识了。

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

使用它很简单:"a string".width()

**增加了空格:nowrap,因此宽度大于窗口宽度的字符串可以被计算。


你可以使用画布,这样你就不必处理这么多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;

试试下面的代码:

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

文本的宽度和高度可以通过clientWidth和clienttheight获得

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

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

确保style position属性被设置为absolute

element.style.position = "absolute";

不需要在div中,可以在p或span中


更好的方法是在显示元素之前检测文本是否合适。你可以使用这个函数它不需要元素在屏幕上。

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

用法:

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

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

在HTML 5中,你可以只使用Canvas。measureText方法(此处进一步解释)。

试试这把小提琴:

/**
  * 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
  const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas"));
  const context = canvas.getContext("2d");
  context.font = font;
  const metrics = context.measureText(text);
  return metrics.width;
}

function getCssStyle(element, prop) {
    return window.getComputedStyle(element, null).getPropertyValue(prop);
}

function getCanvasFont(el = document.body) {
  const fontWeight = getCssStyle(el, 'font-weight') || 'normal';
  const fontSize = getCssStyle(el, 'font-size') || '16px';
  const fontFamily = getCssStyle(el, 'font-family') || 'Times New Roman';
  
  return `${fontWeight} ${fontSize} ${fontFamily}`;
}

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

如果你想使用特定元素myl的字体大小,你可以使用getCanvasFont实用函数:

const fontSize = getTextWidth(text, getCanvasFont(myEl));
// do something with fontSize here...

解释:getCanvasFontSize函数接受某些元素的字体(默认为正文),并将其转换为与上下文兼容的格式。字体属性。当然,任何元素在使用之前都必须首先添加到DOM中,否则会给您虚假的值。

更多的笔记

这种方法有几个优点,包括:

比其他(基于DOM的)方法更简洁和更安全,因为它不改变全局状态,比如DOM。 进一步的定制可以通过修改更多的画布文本属性,如textAlign和textBaseline。

注意:当你在DOM中添加文本时,记住还要考虑到填充、边距和边框。

注2:在某些浏览器上,该方法产生亚像素精度(结果是浮点数),在其他浏览器上则不是(结果只是一个int)。你可能想运行数学。floor(或Math.ceil),以避免不一致。由于基于dom的方法从来没有亚像素精度,因此该方法比这里的其他方法具有更高的精度。

根据这个jsperf(感谢评论中的贡献者),如果将缓存添加到基于dom的方法中,并且您没有使用Firefox,那么Canvas方法和基于dom的方法几乎一样快。在Firefox中,由于某种原因,这种Canvas方法要比基于dom的方法快得多(截至2014年9月)。

性能

本文将此Canvas方法与Bob Monteverde基于dom的方法的变体进行了比较,因此您可以分析和比较结果的准确性。


我为此写了一个小工具。也许对某些人有用。它不需要jQuery也能工作。

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

用法:

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

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

小提琴:http://jsfiddle.net/PEvL8/


我猜这和Depak的条目很相似,但这是基于Louis Lazaris在impressiveweb页面上发表的一篇文章

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

fit事件用于在函数关联到控件之后立即执行函数调用。

例如:$(“输入”).autofit () .trigger(“适合”);


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

工作示例: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>

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

根据Deepak Nadar的回答,我修改了函数参数,以接受文本和字体样式。您不需要引用元素。此外,fontOptions有默认值,所以您不需要提供所有的选项。

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


我喜欢你的“唯一的想法”只是做一个静态字符宽度地图!它实际上很适合我的目的。有时,出于性能原因,或者因为不容易访问DOM,您可能只想要一个快速的、独立的、校准为单一字体的计算器。这是一个用Helvetica字体校准的;传递一个字符串和字体大小:

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

function measureText(str, fontSize) {
  return Array.from(str).reduce(
    (acc, cur) => acc + (widths[cur.charCodeAt(0)] ?? avg), 0
  ) * fontSize
}

这个巨大的丑陋数组是由字符代码索引的ASCII字符宽度。所以这只支持ASCII(否则它假设平均字符宽度)。幸运的是,宽度基本上是随字体大小线性缩放的,所以它在任何字体大小下都能很好地工作。它明显缺乏对字距或结扎之类的意识。

为了“校准”,我只是在svg上渲染了charCode 126(强大的波浪号)的每个字符,并获得了边界框并将其保存到这个数组中;这里有更多的代码、解释和演示。


getclientrects()方法返回一个DOMRect对象的集合,用于指示客户端中每个CSS边界框的包围矩形。返回值是一个DOMRect对象的集合,对应于与元素关联的每个CSS边框框。每个DOMRect对象都包含以像素为单位的描述边界框的只读左、上、右和下属性,其中左上角相对于视口的左上角。

Mozilla contributor的Element.getClientRects()是CC-BY-SA 2.5授权的。

将所有返回的矩形宽度相加,就得到了文本的总宽度(以像素为单位)。

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>


如果有人在这里寻找一种测量字符串宽度的方法,以及一种知道适合特定宽度的最大字体大小的方法,这里有一个基于@Domi的解决方案的函数,使用二进制搜索:

/**
 * 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 {Number} width - The width in pixels the string must fit in
 * @param {Number} minFontPx - The smallest acceptable font size in pixels
 * @param {Number} 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;
    }
  }
}

从头重写了我的答案(谢谢你的减分)。 现在函数接受一个文本和css规则被应用(不再使用jQuery)。所以它也会尊重填充。结果值被四舍五入(您可以看到Math。四舍五入,如果你想要更精确的值,可以去掉)

function getSpan(){ const span = document.createElement('span') span.style.position = 'fixed'; span.style.visibility = 'hidden'; document.body.appendChild(span); return span; } function textWidth(str, css) { const span = getSpan(); Object.assign(span.style, css || {}); span.innerText = str; const w = Math.round(span.getBoundingClientRect().width); span.remove(); return w; } const testStyles = [ {fontSize: '10px'}, {fontSize: '12px'}, {fontSize: '60px'}, {fontSize: '120px'}, {fontSize: '120px', padding: '10px'}, {fontSize: '120px', fontFamily: 'arial'}, {fontSize: '120px', fontFamily: 'tahoma'}, {fontSize: '120px', fontFamily: 'tahoma', padding: '5px'}, ]; const ul = document.getElementById('output'); testStyles.forEach(style => { const li = document.createElement('li'); li.innerText = `${JSON.stringify(style)} > ${textWidth('abc', style)}`; ul.appendChild(li); }); <ul id="output"></ul>


你也可以使用createRange,这比文本克隆技术更准确:

function getNodeTextWidth(nodeWithText) {
    var textNode = $(nodeWithText).contents().filter(function () {
        return this.nodeType == Node.TEXT_NODE;
    })[0];
    var range = document.createRange();
    range.selectNode(textNode);
    return range.getBoundingClientRect().width;
}

我使用文本度量包。工作真的很好,我尝试了这个解决方案,但在某些原因,它计算错误。

textMetrics.init(document.querySelector('h1'), { fontSize: '20px' });

textMetrics.init({
  fontSize: '14px',
  lineHeight: '20px',
  fontFamily: 'Helvetica, Arial, sans-serif',
  fontWeight: 400,
  width: 100,
});

您可以使用max-content来测量文本的像素宽度。

这是一个效用函数。它可以选择将任何节点作为上下文来计算宽度,考虑到任何CSS,如字体大小、字母间距等。

function measureTextPxWidth( text, template = document.createElement("span") ) { const measurer = template.cloneNode(); measurer.style.setProperty("all", "revert", "important"); measurer.style.setProperty("position", "position", "important"); measurer.style.setProperty("visibility", "hidden", "important"); measurer.style.setProperty("width", "max-content", "important"); measurer.innerText = text; document.body.appendChild(measurer); const { width } = measurer.getBoundingClientRect(); document.body.removeChild(measurer); return width; } document.querySelector('.spanTextWidth').innerText = `${measureTextPxWidth('one two three')}px` document.querySelector('.h1TextWidth').innerText = `${measureTextPxWidth('one two three', document.querySelector('h1'))}px` h1 { letter-spacing: 3px; } <span>one two three</span> <div class="spanTextWidth"></div> <h1>one two three</h1> <div class="h1TextWidth"></div>


如果你可以安装一个包,并且你想要一个更权威或更精确的答案,你可以使用opentype.js(奇怪的是没有人提到这一点):

import { load } from "opentype.js";

const getWidth = async (text = "Hello World") => {
  const font = await load("path/to/some/font");
  const { x1, x2 } = font.getPath(text, 0, 0, 12).getBoundingBox();
  return x2 - x1;
};

当然,您希望每个字体只调用load一次,因此应该根据您的情况将该行拉出到更高的范围。

下面是一个代码沙盒,将OpenType方法与Canvas和DOM方法进行比较: https://codesandbox.io/s/measure-width-of-text-in-javascript-vctst2

在我的机器上,每个100个样品,典型的结果是:

女士的OpenType: 5 女士帆布:3 女士DOM: 4

我发现的另一个软件包是:https://github.com/sffc/word-wrappr


对于任何一个在那里使用React和/或Typescript…

试试这个Codepen!

export default function App() {
  const spanRef = useRef<HTMLSpanElement>(null);
  const [textWidth, setTextWidth] = useState(0);

  const getTextWidthInPixels = (ref: HTMLSpanElement) =>
    ref.getBoundingClientRect().width;

  useEffect(() => {
    setTextWidth(getTextWidthInPixels(spanRef.current!));
  }, [spanRef]);

  return (
    <div className="App">
      <span
        ref={spanRef}
        contentEditable
        suppressContentEditableWarning
        onInput={() => setTextWidth(getTextWidthInPixels(spanRef.current!))}
      >
        Edit Me!!!
      </span>
      {`textWidth: ${textWidth}px`}
    </div>
  );
}

将文本包装在内联定位的元素(如<span>)中是个好主意。 useRef是React访问DOM元素的方式,在我们的例子中是<span> getBoundingClientRect可以获得任何DOM元素的总宽度。 contentteditable允许用户更改元素的内容…这有点不安全(React会抛出警告!) suppresscontentteditablewarning将帮助我们防止这些警告


嘿,大家,我知道我来晚了一点,但我们开始吧

window.addEventListener("error",function(e){ alert(e.message); });
var canvas = new OffscreenCanvas(400, 50);
var ctx = canvas.getContext("2d");
ctx.font = "16px Ariel"; //this can be dynamic using getComputedStyle
const chars = ["a","b","c","d","e","f"," ","    "];
const charWidths = new Map();
while(chars.length > 0){
  var char = chars.shift();
  var wide = ctx.measureText(char).width;
  charWidths.set(char,wide);
}

然后你可以用它来做如下的事情:

var pixelWidth = charWidths.get("0");
//fyi css properties like letter-spacing need to be accounted for

在文本的包含元素上使用scrollWidth来获得元素的最小宽度,包括由于溢出而隐藏的部分。更多信息请访问https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollWidth

如果元素不在DOM中,将其添加到某个隐藏区域以进行测量。例如:

function measureText(text) {
  let div = document.createElement("div");
  div.innerText = text;
  div.style.whiteSpace = 'nowrap';
  body.appendChild(div);
  let width = div.scrollWidth;
  body.removeChild(div);
  return width;
}

样式(字体大小、重量等)将被元素继承,因此计入宽度。您还可以使用scrollWidth和scrollHeight来测量更复杂内容的大小。