Domanda

Vorrei usare JavaScript per calcolare la larghezza di una stringa. È possibile senza dover usare un carattere monospaziale?

Se non è integrato, la mia unica idea è quella di creare una tabella di larghezze per ogni personaggio, ma questo è abbastanza irragionevole, specialmente supportando Unicode e tipi di dimensioni diverse (e tutti i browser per quella materia).

È stato utile?

Soluzione

Crea un DIV con i seguenti stili. Nel tuo JavaScript, imposta la dimensione del carattere e gli attributi che stai cercando di misurare, inserisci la stringa nel DIV, quindi leggi la larghezza e l'altezza correnti del DIV. Si allungherà per adattarsi al contenuto e la dimensione sarà entro pochi pixel della dimensione del rendering della stringa.

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>

Altri suggerimenti

In HTML 5 , puoi semplicemente utilizzare il metodo Canvas.measureText (ulteriore spiegazione qui ).

Prova questo violino :

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

Questo violino confronta questo metodo Canvas con una variazione di Metodo di DOM basato su DOM di Monteverde , in modo da poter analizzare e confrontare l'accuratezza dei risultati.

Esistono diversi vantaggi in questo approccio, tra cui:

NOTA: quando aggiungi il testo al DOM, ricorda di tenere conto anche di imbottitura, margine e bordo .

NOTA 2: in alcuni browser, questo metodo produce una precisione sub-pixel (il risultato è un numero in virgola mobile), in altri no (il risultato è solo un int). Potresti voler eseguire Math.floor (o Math.ceil ) sul risultato, per evitare incoerenze. Poiché il metodo basato su DOM non è mai accurato sotto-pixel, questo metodo ha una precisione ancora maggiore rispetto agli altri metodi qui.

Secondo questo jsperf (grazie ai collaboratori nei commenti), il Il metodo Canvas e il metodo basato su DOM sono altrettanto veloci, se la memorizzazione nella cache viene aggiunta al metodo basato su DOM e non si utilizza Firefox. In Firefox, per qualche motivo, questo metodo Canvas è molto più veloce del metodo basato su DOM (da settembre 2014).

Eccone uno che ho montato insieme senza esempio. Sembra che siamo tutti sulla stessa pagina.

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

Usarlo è semplice: " una stringa " .width ()

** Aggiunto white-space: nowrap in modo da poter calcolare stringhe con larghezza maggiore della larghezza della finestra.

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

Questo funziona per me ...

// 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 libreria javascript ExtJS ha una grande classe chiamata Ext.util.TextMetrics che " ; fornisce misurazioni precise dei pixel per blocchi di testo in modo da poter determinare esattamente quanto in alto e in larghezza, in pixel, un determinato blocco di testo sarà " ;. Puoi usarlo direttamente o visualizzare il suo codice sorgente per vedere come è fatto.

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

Ho scritto un piccolo strumento per quello. Forse è utile a qualcuno. Funziona senza jQuery .

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

Utilizzo:

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

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

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

Mi piace la tua " unica idea " di fare semplicemente una mappa statica della larghezza dei caratteri! In realtà funziona bene per i miei scopi. A volte, per motivi di prestazioni o perché non si ha un facile accesso a un DOM, è possibile che si desideri semplicemente una calcolatrice autonoma hacky rapida calibrata su un singolo carattere. Quindi eccone uno calibrato su Helvetica; passare una stringa e (facoltativamente) una dimensione del carattere:

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
}

Quella brutta matrice gigante è la larghezza dei caratteri ASCII indicizzata dal codice carattere. Quindi questo supporta solo ASCII (altrimenti assume una larghezza media dei caratteri). Fortunatamente, la larghezza si ridimensiona sostanzialmente in linea con la dimensione del carattere, quindi funziona abbastanza bene con qualsiasi dimensione del carattere. È evidentemente privo di consapevolezza di crenatura, legature o altro.

Per " calibrare " Ho appena eseguito il rendering di ogni personaggio fino al charCode 126 (la potente tilde) su uno svg e ho ottenuto il rettangolo di selezione e l'ho salvato in questo array; più codice, spiegazione e demo qui .

Puoi usare la tela in modo da non doverti occupare così tanto delle proprietà dei 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>

Dovrebbe funzionare fino a quando < span > al tag non sono stati applicati altri stili. offsetWidth includerà la larghezza di tutti i bordi, l'imbottitura orizzontale, la larghezza della barra di scorrimento verticale, ecc.

Il codice viene visualizzato di seguito, " calcola " la larghezza del tag span, aggiunge " ... " se è troppo lungo e riduce la lunghezza del testo, finché non si adatta al suo genitore (o fino a quando non ha provato più di mille volte)

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

Prova questo codice:

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 larghezza e l'altezza di un testo possono essere ottenute con clientWidth e clientHeight

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

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

assicurati che la proprietà position style sia impostata su assoluta

element.style.position = "absolute";

non deve trovarsi all'interno di un div , può trovarsi all'interno di un p o di un span

Il meglio di è rilevare se il testo si adatterà subito prima di visualizzare l'elemento. Quindi puoi usare questa funzione che non richiede che l'elemento sia sullo schermo.

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

Utilizzo:

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

Partendo da risposta di Deepak Nadar , ho modificato i parametri delle funzioni per accettare gli stili di testo e font. Non è necessario fare riferimento a un elemento. Inoltre, fontOptions ha impostazioni predefinite, quindi non è necessario fornirle tutte.

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

Nel caso in cui qualcun altro fosse qui alla ricerca di un modo per misurare la larghezza di una stringa e un modo per sapere qual è la dimensione del carattere più grande che si adatterà in una larghezza particolare, ecco una funzione che si basa sulla soluzione di @Domi con una ricerca binaria:

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

Immagino che questo sia molto simile alla voce di Depak, ma si basa sul lavoro di Louis Lazaris pubblicato in un articolo in pagina impressionantewebs

(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'evento fit viene utilizzato per eseguire la chiamata di funzione immediatamente dopo l'associazione della funzione al controllo.

ad es .: $ ('input'). autofit (). trigger (" fit ");

Senza 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 di esempio funzionante: 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>

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

Il metodo Element.getClientRects () restituisce una raccolta di oggetti DOMRect che indicano i rettangoli di delimitazione per ogni riquadro del bordo CSS in un client. Il valore restituito è una raccolta di oggetti DOMRect , uno per ogni riquadro del bordo CSS associato all'elemento. Ogni oggetto DOMRect contiene proprietà di sola lettura left , top , right e bottom descrivendo il riquadro, in pixel, con la parte superiore sinistra rispetto alla parte superiore sinistra della finestra.

Element.getClientRects () di < a href = "https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects$history" rel = "nofollow noreferrer"> Mozilla Contributors è concesso in licenza in CC-BY-SA 2.5 .

Riassumendo tutte le larghezze rettangolari restituite si ottiene la larghezza totale del testo in pixel.

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>

Ho creato un piccolo modulo ES6 (usa 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 da usare:

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

Cosa interessante che potresti notare: ti permette di prendere in considerazione qualsiasi attributo CSS, persino i padding!

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'));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top