使用JavaScript计算文本宽度

Jac*_*cob 435 javascript textbox

我想用JavaScript来计算字符串的宽度.这是否可以在不使用等宽字体的情况下实现?

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

Dom*_*omi 350

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
    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
Run Code Online (Sandbox Code Playgroud)

这个小提琴将这种Canvas方法与Bob Monteverde基于DOM的方法的变体进行比较,因此您可以分析和比较结果的准确性.

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

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

注意:当您将文本添加到DOM时,请记住还要考虑填充,边距和边框.

注2:在某些浏览器中,此方法产生子像素精度(结果是浮点数),而在其他浏览器上则不然(结果只是一个int).您可能希望对结果运行Math.floor(或Math.ceil),以避免不一致.由于基于DOM的方法从不是亚像素精确的,因此这种方法比其他方法具有更高的精度.

根据这个jsperf(感谢评论中的贡献者),如果将缓存添加到基于DOM的方法而您没有使用Firefox ,那么Canvas方法基于DOM的方法大致相同.在Firefox中,出于某种原因,这种Canvas方法基于DOM的方法快得多(截至2014年9月).

  • 这是一个很棒的选择,我不知道.有没有将其性能与DOM中的测量性能进行比较? (7认同)
  • 我将缓存添加到jsperf基准测试中.现在这两种方法几乎相同:http://jsperf.com/measure-text-width/4 (2认同)
  • 很好,因为用户看不到它! (2认同)

CMP*_*mer 337

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

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);
Run Code Online (Sandbox Code Playgroud)
#Test
{
    position: absolute;
    visibility: hidden;
    height: auto;
    width: auto;
    white-space: nowrap; /* Thanks to Herb Caudill comment */
}
Run Code Online (Sandbox Code Playgroud)
<div id="Test">
    abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
</div>
Run Code Online (Sandbox Code Playgroud)

  • 如果您认为文本超出浏览器宽度,您还应该放入white-space:nowrap. (43认同)
  • 只是好奇每个维度上的"+ 1"是什么? (9认同)
  • 我唯一要补充的是,根据使用的样式,这可能会给出错误的尺寸.请记住,你可能有p {letter-spacing:0.1em; DIV元素不会反映出来.您必须确保适当的样式适合您使用文本的位置. (7认同)
  • 同上吉姆的评论 - 仔细检查以确保容器,在这种情况下div,没有任何其他样式通过css选择规则应用于它当时你可能没有认识到.在测量之前,在应用您关心的样式之前,从容器中去除所有相关样式. (4认同)
  • @BBog所有黑客攻击的原因是为什么我建议在楼下采用另一种不那么黑的方法.看一下[这里](http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393). (4认同)
  • 这个答案对我来说不正常,它最初给了我错误的价值,不断增加它们,只有在几次电话后我才有正确的答案.是什么修复它将`visibility:hidden`替换为顶部/左侧设置为-1000px (3认同)
  • 此解决方案强制同步布局,页面上有许多元素可能会很慢. (2认同)
  • 它对我不起作用。test.clientWidth和test.clientHeight始终返回0。 (2认同)

Bob*_*rde 106

这是我在没有例子的情况下一起鞭打的人.看起来我们都在同一页面上.

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;
}
Run Code Online (Sandbox Code Playgroud)

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

**添加white-space: nowrap了宽度大于窗口宽度的字符串可以计算.

  • 这是*糟糕的设计*,它不仅将您的模型与您的视图结合在一起,而且还将它与jQuery直接耦合.最重要的是,这是回流地狱,这肯定不会很好地扩展. (21认同)
  • 我不会将此方法添加到`String`,因为它会减少程序的[关注点分离](https://en.wikipedia.org/wiki/Separation_of_presentation_and_content)(单独的核心代码与UI代码).想象一下,您希望将核心代码移植到具有非常不同的UI框架的平台上...... (3认同)
  • 谢谢JordanC.我要补充的一件事是,如果你在同一页面上多次调用它,并且性能是一个问题,你可以坚持DIV,只需更改内容并检查宽度.我只是这样做,所以一旦完成所有内容,DOM将与您开始时相同 (2认同)

小智 30

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);
Run Code Online (Sandbox Code Playgroud)


Pet*_*ete 24

这对我有用......

// 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;
}
Run Code Online (Sandbox Code Playgroud)

  • @Pete但你使用的是`document.createElement(**'lDiv'**)` (3认同)
  • 请勿在当前状态下使用此解决方案。使用textContent代替innerHTML来解决XSS漏洞。 (3认同)

big*_*lep 19

ExtJS的JavaScript库有一个名为Ext.util.TextMetrics说,"为文本块精确测量像素,让您可以准确确定的高和宽,以像素为单位,文本的给定块将是"一个伟大的阶级.您既可以直接使用它,也可以查看其源代码以了解如何完成此操作.

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

  • 能够查看源代码和开源之间存在差异,开源是由许可定义的. (9认同)
  • 你应该让 ExtJS 安息 (3认同)

Top*_*oph 11

我喜欢你只做一个静态字符宽度图的"唯一想法"!它实际上适合我的目的.有时,出于性能原因或者您无法轻松访问DOM,您可能只需要一个快速hacky独立计算器校准到单个字体.所以这里有一个校准到Helvetica; 传递一个字符串和(可选)字体大小:

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
}
Run Code Online (Sandbox Code Playgroud)

那个巨大的丑陋数组是由字符代码索引的ASCII字符宽度.所以这只支持ASCII(否则它假设平均字符宽度).幸运的是,宽度基本上与字体大小成线性比例,所以它适用于任何字体大小.它显然缺乏对字距或连字或其他任何东西的认识.

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

  • 请注意,这并没有考虑到每个现代字体用来光学校正特定字形之间的间距/跟踪的字距调整对。 (4认同)
  • @bryanph 当然,但在这种情况下,您的计算很简单,如“str.length * glyphWidth”,因为等宽字体中的每个字形都具有相同的提前宽度。上面的解决方案适用于非等宽字体,并且这些字体大多带有字距对,因此计算不精确。 (2认同)

sch*_*ing 10

我为此写了一个小工具.也许这对某人有用.它没有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
Run Code Online (Sandbox Code Playgroud)

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


rys*_*ama 6

您可以使用画布,这样您就不必处理如此多的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;
Run Code Online (Sandbox Code Playgroud)

  • 如果您已经在使用画布进行某些操作,那么这是一种干净的方法。但非常慢。单独的measureText调用大约需要8-10毫秒(使用Chrome)。 (2认同)

Bry*_*man 5

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

<script>
var textWidth = document.getElementById("text").offsetWidth;
</script>
Run Code Online (Sandbox Code Playgroud)

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