使用 JavaScript 计算文本宽度

IT技术 javascript textbox
2021-01-28 20:54:45

我想使用 JavaScript 来计算字符串的宽度。这是否可能而不必使用等宽字体?

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

6个回答

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

如果要使用某些特定元素的字体大小myEl,可以使用getCanvasFontSize实用程序功能:

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

说明:该getCanvasFontSize函数采用一些元素的(默认为body's)字体并将其转换为与Context.font 属性兼容的格式当然,任何元素在使用之前都必须先添加到 DOM 中,否则它会给你虚假的值。

el.remove()); // hackfix: 不要这样做!返回 el; } console.log(getTextWidth("你好!", getCanvasFontSize(createEl('span')))); console.log(getTextWidth("你好!", getCanvasFontSize(createEl('h1')))); ``` -->

更多笔记

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

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

注意:当您将文本添加到 DOM 时,请记住还要考虑padding、margin 和 border

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

根据这个 jsperf(感谢评论中的贡献者),如果将缓存添加到基于 DOM 的方法并且您没有使用 Firefox ,则Canvas 方法基于 DOM 的方法的速度差不多在 Firefox 中,出于某种原因,这种Canvas 方法基于 DOM 的方法(截至 2014 年 9 月)快得多

表现

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

这是一个很棒的选择,我不知道。有没有将它的性能与在 DOM 中测量的性能进行比较?
2021-03-09 20:54:45
@马丁嗯?“许多 HTML 元素”与这里的任何内容有什么关系?这个例子中的画布对象甚至没有附加到 DOM。甚至除此之外,更改画布绘图上下文中的字体甚至不会影响画布,直到您strokeText()fillText(). 也许您打算对不同的答案发表评论?
2021-03-09 20:54:45
不错的解决方案。我已经围绕 Domi 的答案包装了一些其他方法,以便我可以 - 如果它不适合给定空间(尽可能多的字符串),则在末尾获取一个(可能)带有省略号 (...) 的截断字符串尽可能使用) - 传入一个包含(可能被截断的)字符串的 JQuery 元素,并动态确定字体属性,这样它们就不必进行硬编码,允许 CSS 字体属性在不中断的情况下更改布局。JSFiddle在这里:jsfiddle.net/brebey/1q94gLsu/6/embed
2021-03-16 20:54:45
很好,因为用户看不到它!
2021-04-02 20:54:45
我将缓存添加到该 jsperf 基准测试中。现在这两种方法差不多了jsperf.com/measure-text-width/4
2021-04-08 20:54:45

创建具有以下样式的 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);
#Test
{
    position: absolute;
    visibility: hidden;
    height: auto;
    width: auto;
    white-space: nowrap; /* Thanks to Herb Caudill comment */
}
<div id="Test">
    abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
</div>

@BBog 所有的黑客攻击就是为什么我建议在楼下采用一种不同的、不那么黑客的方法。看一看这里
2021-03-09 20:54:45
我唯一要补充的是,这可能会根据使用的样式给出错误的尺寸。请记住,您可能有类似 p { letter-spacing: 0.1em; 之类的样式。一个 DIV 元素不会反映。您必须确保适当的样式适合您将使用文本的位置。
2021-03-12 20:54:45
只是好奇+1每个维度上用途是什么
2021-03-17 20:54:45
如果您认为文本会超过浏览器宽度,您还应该输入 white-space:nowrap。
2021-03-20 20:54:45
同上 Jim 的评论 - 仔细检查以确保容器(在本例中为 div)没有通过 css 选择规则对其应用任何其他样式,而您当时可能不知道这些样式。在测量之前应用您关心的样式之前,从容器中去除所有相关样式。
2021-04-07 20:54:45

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

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

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

这是一个糟糕的设计,它不仅将您的模型与您的视图耦合,而且还直接将它与 jQuery 耦合。最重要的是,这是回流地狱,这绝对不会很好地扩展。
2021-03-23 20:54:45
我不会添加此方法,String因为它会减少程序的关注点分离(分离核心代码与 UI 代码)。想象一下,您想将核心代码移植到具有非常不同的 UI 框架的平台上……
2021-03-25 20:54:45
如果你想要浮点值,你可以o[0].getBoundingClientRect().width按照这里的建议使用stackoverflow.com/a/16072668/936397
2021-04-02 20:54:45
感谢 JordanC。我要补充的一件事是,如果您在同一页面上多次调用它,并且性能是一个问题,您可以保留 DIV,只需更改内容并检查宽度。我就是这样做的
2021-04-05 20:54:45
在我的这个版本中,我使用了一个对象而不是 font 参数,因此您可以为 div 提供 css 参数以用于粗体和斜体等。
2021-04-07 20:54:45

这对我有用...

// 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;
}
嗨,您的回答非常有用,但是请使用 .cssText 而不是 .style 来支持 IE 8 及更低版本..
2021-03-25 20:54:45
这个答案很好,但是当我生成一大段文本时,它会变得不准确几个像素。是不是因为宽度不是整数?
2021-04-03 20:54:45
请勿在当前状态下使用此解决方案。使用 textContent 而不是 innerHTML 来解决 XSS 漏洞。
2021-04-08 20:54:45

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