Question

J'aimerais utiliser JavaScript pour calculer la largeur d'une chaîne. Est-ce possible sans avoir à utiliser une police de caractère monospace?

Si ce n’est pas intégré, mon unique idée est de créer un tableau des largeurs pour chaque caractère, mais c’est plutôt déraisonnable de prendre en charge Unicode et différentes tailles de types (et tous les navigateurs d'ailleurs).

Était-ce utile?

La solution

Créez une DIV avec les styles suivants. Dans votre JavaScript, définissez la taille de la police et les attributs que vous essayez de mesurer, placez votre chaîne dans la DIV, puis lisez la largeur et la hauteur actuelles de la DIV. Il s’étendra pour s’adapter au contenu et la taille ne sera que de quelques pixels par rapport à la taille de la chaîne.

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>

Autres conseils

Dans HTML 5 , vous pouvez simplement utiliser la méthode Canvas.measureText. (explications supplémentaires ici ).

Essayez ce violon :

/**
 * 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

Ce violon compare cette méthode Canvas à une variante de Méthode basée sur le DOM de Bob Monteverde , vous permettant ainsi d'analyser et de comparer la précision des résultats.

Cette approche présente plusieurs avantages, notamment:

  • Plus concise et plus sûre que les autres méthodes (basées sur DOM) car elle ne modifie pas l'état global, tel que votre DOM.
  • D'autres autres propriétés de texte de la toile , telles que textAlign <, peuvent être personnalisées. / code> et textBaseline .

REMARQUE: lorsque vous ajoutez le texte à votre DOM, n'oubliez pas de prendre également en compte les padding, margin and border .

NOTE 2: Sur certains navigateurs, cette méthode donne une précision inférieure au pixel (le résultat est un nombre à virgule flottante), sur d'autres non (le résultat est uniquement un entier). Vous voudrez peut-être exécuter Math.floor (ou Math.ceil ) sur le résultat pour éviter les incohérences. Comme la méthode basée sur le DOM n’est jamais précise au sous-pixel, cette méthode est encore plus précise que les autres méthodes présentées ici.

Selon ce jsperf (grâce aux contributeurs dans les commentaires), le La méthode Canvas et la méthode DOM sont à peu près aussi rapides, si la mise en cache est ajoutée à la méthode DOM et que vous n'utilisez pas Firefox. Dans Firefox, pour une raison quelconque, cette méthode Canvas est beaucoup plus rapide que la méthode basée sur DOM (à partir de septembre 2014).

En voici un que j'ai fouetté ensemble sans exemple. On dirait que nous sommes tous sur la même page.

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

Son utilisation est simple: "une chaîne" .width ()

** Ajout de espace-blanc: nowrap pour que les chaînes dont la largeur est supérieure à la largeur de la fenêtre puissent être calculées.

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

Cela fonctionne pour moi ...

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

La la bibliothèque JavaScript ExtJS possède une classe qui s'appelle Ext.util.TextMetrics that " ; fournit des mesures de pixels précises pour des blocs de texte afin que vous puissiez déterminer exactement à quelle hauteur, en pixels, un bloc de texte donné sera ". Vous pouvez l’utiliser directement ou consulter son code source pour voir comment procéder.

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

J'ai écrit un petit outil pour ça. C'est peut-être utile à quelqu'un. Cela fonctionne sans jQuery .

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

Utilisation:

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

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

Fiddle: http://jsfiddle.net/PEvL8/

J'aime votre "seule idée". de faire juste une carte de largeur de caractère statique! Cela fonctionne bien pour mes besoins. Parfois, pour des raisons de performances ou parce que vous n’avez pas un accès facile à un DOM, vous voudrez peut-être simplement une calculatrice autonome hacky calibrée avec une seule police. Donc, voici un calibré à Helvetica; passer une chaîne et (éventuellement) une taille de police:

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
}

Ce tableau moche est la largeur des caractères ASCII indexés par le code de caractère. Donc, cela ne supporte que l'ASCII (sinon, on suppose une largeur de caractère moyenne). Heureusement, la largeur est fondamentalement linéaire avec la taille de la police. Elle convient donc parfaitement à toutes les tailles de police. Il manque visiblement toute conscience de crénage, de ligatures ou autre.

Pour " calibrer " Je viens de restituer chaque caractère jusqu'à charCode 126 (le puissant tilde) sur un svg et j'ai récupéré le cadre de sélection et l'ai sauvegardé dans ce tableau; plus de code, d'explications et de démos ici .

Vous pouvez utiliser le canevas pour ne pas trop vous préoccuper des propriétés 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;
<span id="text">Text</span>

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

Cela devrait fonctionner aussi longtemps que le < span > La balise n'a pas d'autres styles appliqués. offsetWidth inclura la largeur des bordures, le remplissage horizontal, la largeur de la barre de défilement verticale, etc.

Le code-snips ci-dessous, " calculer " la largeur de la balise span, ajoute "" ... " si elle est trop longue et réduit la longueur du texte, jusqu’à ce qu’elle tienne dans son parent (ou jusqu’à ce qu’elle ait essayé plus de mille fois)

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

Essayez ce code:

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

La largeur et la hauteur d'un texte peuvent être obtenues avec clientWidth et clientHeight

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

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

assurez-vous que la propriété de position de style est définie sur absolue

element.style.position = "absolute";

non requis pour être dans un div , peut être dans un p ou un span

Le meilleur de consiste à détecter si le texte convient avant d'afficher l'élément. Vous pouvez donc utiliser cette fonction qui n'exige pas que l'élément soit à l'écran.

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

Utilisation:

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

En me basant sur la réponse de Deepak Nadar , j'ai modifié le paramètre de fonction pour accepter les styles de texte et de police. Vous n'avez pas besoin de référencer un élément. De plus, les fontOptions ont des valeurs par défaut, vous n’avez donc pas besoin de toutes les fournir.

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

Si quelqu'un d'autre venait ici chercher un moyen de mesurer la largeur d'une chaîne et de savoir quelle est la plus grande taille de police pouvant contenir une largeur particulière, voici une fonction qui s'appuie sur la solution de @ Domi avec une recherche binaire:

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

Je suppose que cela ressemble beaucoup à l’entrée Depak, mais est basé sur le travail de Louis Lazaris publié dans un article paru dans page impressionnantewebs

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

L'événement fit est utilisé pour exécuter l'appel de fonction immédiatement après que la fonction est associée au contrôle.

Par exemple: $ ('input'). autofit (). trigger ("fit");

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

Fiddle de l'exemple de travail: 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>

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

La méthode Element.getClientRects () renvoie une collection d'objets DOMRect qui indiquent les rectangles de délimitation de chaque zone de bordure CSS d'un client. La valeur renvoyée est une collection d'objets DOMRect , un pour chaque zone de bordure CSS associée à l'élément. Chaque objet DOMRect contient des propriétés gauche en lecture seule, haut , droite et bas . décrivant la zone de bordure, en pixels, avec le coin supérieur gauche par rapport au coin supérieur gauche de la fenêtre.

Element.getClientRects () par < a href = "https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects$history" rel = "nofollow noreferrer"> Mozilla Contributors est sous licence CC-BY-SA 2.5 .

La somme de toutes les largeurs de rectangle retournées donne la largeur totale du texte en pixels.

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>

J'ai créé un tout petit module ES6 (utilisant jQuery):

import $ from 'jquery';

const $span=$('<span>');
$span.css({
    position: 'absolute',
    display: 'none'
}).appendTo('body');

export default function(str, css){
    $span[0].style = ''; // resetting the styles being previously set
    $span.text(str).css(css || {});
    return $span.innerWidth();
}

Facile à utiliser:

import stringWidth from './string_width';
const w = stringWidth('1-3', {fontSize: 12, padding: 5});

Ce qui est cool, vous remarquerez peut-être - cela permet de prendre en compte tous les attributs CSS, même les rembourrages!

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'));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top