سؤال

أرغب في استخدام JavaScript لحساب عرض السلسلة.هل هذا ممكن دون الحاجة إلى استخدام محرف أحادي المسافة؟

إذا لم يكن مدمجًا، فإن فكرتي الوحيدة هي إنشاء جدول عرض لكل حرف، ولكن هذا غير معقول إلى حد كبير خاصة الدعم يونيكود وأحجام أنواع مختلفة (وجميع المتصفحات في هذا الشأن).

هل كانت مفيدة؟

المحلول

قم بإنشاء 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>

نصائح أخرى

في أتش تي أم أل 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

هذا الكمان يقارن طريقة Canvas هذه بمجموعة متنوعة من طريقة Bob Monteverde المستندة إلى DOM, ، حتى تتمكن من تحليل ومقارنة دقة النتائج.

هناك العديد من المزايا لهذا النهج، بما في ذلك:

  • أكثر إيجازًا وأكثر أمانًا من الطرق الأخرى (المعتمدة على DOM) لأنها لا تغير الحالة العامة، مثل DOM الخاص بك.
  • مزيد من التخصيص ممكن من خلال تعديل المزيد من خصائص النص القماشي, ، مثل textAlign و textBaseline.

ملحوظة:عند إضافة النص إلى DOM الخاص بك، تذكر أيضًا أن تأخذ في الاعتبار الحشو والهامش والحدود.

ملاحظة 2:في بعض المتصفحات، تنتج هذه الطريقة دقة بكسل فرعي (النتيجة هي رقم فاصلة عائمة)، وفي متصفحات أخرى لا تفعل ذلك (النتيجة هي int فقط).قد ترغب في الركض Math.floor (أو Math.ceil) على النتيجة، لتجنب التناقضات.نظرًا لأن الطريقة المستندة إلى DOM لا تكون دقيقة على الإطلاق بمقدار بكسل فرعي، فإن هذه الطريقة تتمتع بدقة أعلى من الطرق الأخرى هنا.

وفق هذا jsperf (شكرا للمساهمين في التعليقات)، و طريقة القماش و ال الطريقة المستندة إلى DOM تكون بنفس السرعة تقريبًا، إذا تمت إضافة التخزين المؤقت إلى ملف الطريقة المستندة إلى DOM وأنت لا تستخدم فايرفوكس.في فايرفوكس، لسبب ما، هذا طريقة القماش أسرع بكثير من الطريقة المستندة إلى DOM (اعتبارًا من سبتمبر 2014).

إليكم واحدة قمت بجلدها معًا بدون مثال.يبدو أننا جميعا على نفس الصفحة.

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 لذلك يمكن حساب السلاسل ذات العرض الأكبر من عرض النافذة.

مسج:

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

هذا يعمل بالنسبة لي...

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

ال مكتبة جافا سكريبت ExtJS يحتوي على فئة رائعة تسمى Ext.util.TextMetrics والتي "توفر قياسات دقيقة للبكسل لكتل ​​النص بحيث يمكنك تحديد مدى ارتفاع وعرض كتلة معينة من النص بالبكسل".يمكنك إما استخدامه مباشرة أو عرض المصدر الخاص به للتعليمات البرمجية لمعرفة كيفية القيام بذلك.

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

لقد كتبت أداة صغيرة لذلك.ربما يكون مفيدًا لشخص ما.إنها تعمل بدون مسج.

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/

تعجبني "فكرتك الوحيدة" المتمثلة في عمل خريطة ثابتة لعرض الأحرف!إنه في الواقع يعمل بشكل جيد لأغراضي.في بعض الأحيان، لأسباب تتعلق بالأداء أو لأنه لا يمكنك الوصول بسهولة إلى DOM، قد تحتاج فقط إلى آلة حاسبة مستقلة سريعة الاختراق تمت معايرتها بخط واحد.إذن، هذه واحدة تمت معايرتها وفقًا لـ 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
}

هذه المجموعة القبيحة العملاقة عبارة عن عروض أحرف ASCII مفهرسة بواسطة رمز الحرف.لذا فإن هذا يدعم فقط ASCII (وإلا فإنه يفترض متوسط ​​عرض الأحرف).لحسن الحظ، يتم قياس العرض بشكل خطي مع حجم الخط، لذلك فهو يعمل بشكل جيد مع أي حجم خط.إنه يفتقر بشكل ملحوظ إلى أي وعي بالمسافات بين الحروف أو الحروف المركبة أو أي شيء آخر.

من أجل "المعايرة" قمت للتو بتقديم كل حرف حتى charCode 126 (التلدا العظيمة) على ملف svg وحصلت على المربع المحيط وحفظته في هذه المصفوفة؛ المزيد من التعليمات البرمجية والشرح والعرض التوضيحي هنا.

يمكنك استخدام اللوحة القماشية حتى لا تضطر إلى التعامل كثيرًا مع خصائص 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>

يجب أن يعمل هذا طالما أن العلامة <span> لا تحتوي على أنماط أخرى مطبقة عليها.سوف يتضمن OffsetWidth عرض أي حدود، والحشوة الأفقية، وعرض شريط التمرير العمودي، وما إلى ذلك.

قصاصات التعليمات البرمجية أدناه، "تحسب" عرض العلامة الممتدة، وتضيف "..." إليها إذا كانت طويلة جدًا وتقلل من طول النص، حتى تتناسب مع أصلها (أو حتى تحاول أكثر من الف مرة)

CSS

div.places {
  width : 100px;
}
div.places span {
  white-space:nowrap;
  overflow:hidden;
}

لغة البرمجة

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

جافا سكريبت (مع مسج)

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

جرب هذا الكود:

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 و clientHeight

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

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

تأكد من تعيين خاصية موضع النمط على مطلق

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) {
    ...
}

بناء قبالة إجابة ديباك نادار, لقد قمت بتغيير معلمات الوظائف لقبول أنماط النص والخط.لا تحتاج إلى الإشارة إلى عنصر.أيضا، 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>

في حالة وصول أي شخص آخر إلى هنا للبحث عن طريقة لقياس عرض السلسلة و طريقة لمعرفة أكبر حجم خط يناسب عرضًا معينًا، إليك وظيفة تعتمد على حل @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 {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;
        }
    }
}

أعتقد أن هذا مشابه جدًا لمدخل ديباك، ولكنه يعتمد على عمل لويس لازاريس المنشور في مقال في صفحة ويب مثيرة للإعجاب

(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 لتنفيذ استدعاء الوظيفة على الفور بعد ربط الوظيفة بعنصر التحكم.

على سبيل المثال:$('input').autofit().trigger("fit");

بدون مسج:

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/

لغة البرمجة:

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

كود جافا سكريبت:

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

ال Element.getClientRects() تقوم الطريقة بإرجاع مجموعة من DOMRect الكائنات التي تشير إلى المستطيلات المحيطة بكل مربع حدود CSS في العميل.القيمة التي تم إرجاعها عبارة عن مجموعة من DOMRect كائنات، واحدة لكل مربع حدود CSS مرتبط بالعنصر.كل DOMRect يحتوي الكائن على للقراءة فقط left, top, right و bottom الخصائص التي تصف مربع الحدود، بالبكسل، مع الجزء العلوي الأيسر بالنسبة إلى الجزء العلوي الأيسر من إطار العرض.

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>

لقد قمت بإنشاء وحدة ES6 صغيرة (تستخدم 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();
}

سهل الاستخدام:

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

شيء رائع قد تلاحظه - فهو يسمح بمراعاة أي سمات CSS، حتى الحشوات!

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'));
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top