我需要显示用户输入的文本到一个固定大小的div。我想要的是字体大小自动调整,以便文本尽可能多地填充框。

如果div是400px x 300px。如果有人输入ABC,那么字体就很大。如果他们输入一个段落,那么它将是一个很小的字体。

我可能想要从最大字体大小开始——可能是32px,当文本太大而不适合容器时,缩小字体大小直到它适合。


当前回答

它使用二分搜索,进行10次迭代。最简单的方法是执行while循环,并将字体大小增加1,直到元素开始溢出。可以使用element来确定元素何时开始溢出。offsetHeight和element.scrollHeight。如果scrollHeight大于offsetHeight,则字体大小过大。

二分搜索是一个更好的算法。它还受到希望执行的迭代次数的限制。只需调用flexFont并插入div id,它将在8px到96px之间调整字体大小。

我花了一些时间研究这个主题,并尝试了不同的库,但最终我认为这是最简单、最直接的解决方案。

注意,如果你愿意,你可以更改使用offsetWidth和scrollWidth,或者将两者都添加到这个函数中。

    // Set the font size using overflow property and div height
    function flexFont(divId) {
        var content = document.getElementById(divId);
        content.style.fontSize = determineMaxFontSize(content, 8, 96, 10, 0) + "px";
    };

    // Use binary search to determine font size
    function determineMaxFontSize(content, min, max, iterations, lastSizeNotTooBig) {
        if (iterations === 0) {
            return lastSizeNotTooBig;
        }
        var obj = fontSizeTooBig(content, min, lastSizeNotTooBig);

        // if `min` too big {....min.....max.....}
        // search between (avg(min, lastSizeTooSmall)), min)
        // if `min` too small, search between (avg(min,max), max)
        // keep track of iterations, and the last font size that was not too big
        if (obj.tooBig) {
            (lastSizeTooSmall === -1) ?
                determineMaxFontSize(content, min / 2, min, iterations - 1, obj.lastSizeNotTooBig, lastSizeTooSmall) :
                    determineMaxFontSize(content, (min + lastSizeTooSmall) / 2, min, iterations - 1, obj.lastSizeNotTooBig, lastSizeTooSmall);
            
        } else {
            determineMaxFontSize(content, (min + max) / 2, max, iterations - 1, obj.lastSizeNotTooBig, min);
        }
    }

    // determine if fontSize is too big based on scrollHeight and offsetHeight, 
    // keep track of last value that did not overflow
    function fontSizeTooBig(content, fontSize, lastSizeNotTooBig) {
        content.style.fontSize = fontSize + "px";
        var tooBig = content.scrollHeight > content.offsetHeight;
        return {
            tooBig: tooBig,
            lastSizeNotTooBig: tooBig ? lastSizeNotTooBig : fontSize
        };
    }

其他回答

尽管我很喜欢这个答案偶尔得到的点赞(谢谢!),但这真的不是解决这个问题的最好方法。请在这里查看其他一些精彩的答案,特别是那些没有循环的答案。


不过,为了便于参考,以下是我最初的答案:

<html>
<head>
<style type="text/css">
    #dynamicDiv
    {
    background: #CCCCCC;
    width: 300px;
    height: 100px;
    font-size: 64px;
    overflow: hidden;
    }
</style>

<script type="text/javascript">
    function shrink()
    {
        var textSpan = document.getElementById("dynamicSpan");
        var textDiv = document.getElementById("dynamicDiv");

        textSpan.style.fontSize = 64;

        while(textSpan.offsetHeight > textDiv.offsetHeight)
        {
            textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
        }
    }
</script>

</head>
<body onload="shrink()">
    <div id="dynamicDiv"><span id="dynamicSpan">DYNAMIC FONT</span></div>
</body>
</html>

下面是一个带有类的版本:

<html>
<head>
<style type="text/css">
.dynamicDiv
{
    background: #CCCCCC;
    width: 300px;
    height: 100px;
    font-size: 64px;
    overflow: hidden;
}
</style>

<script type="text/javascript">
    function shrink()
    {
        var textDivs = document.getElementsByClassName("dynamicDiv");
        var textDivsLength = textDivs.length;

        // Loop through all of the dynamic divs on the page
        for(var i=0; i<textDivsLength; i++) {

            var textDiv = textDivs[i];

            // Loop through all of the dynamic spans within the div
            var textSpan = textDiv.getElementsByClassName("dynamicSpan")[0];

            // Use the same looping logic as before
            textSpan.style.fontSize = 64;

            while(textSpan.offsetHeight > textDiv.offsetHeight)
            {
                textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
            }

        }

    }
</script>

</head>
<body onload="shrink()">
    <div class="dynamicDiv"><span class="dynamicSpan">DYNAMIC FONT</span></div>
    <div class="dynamicDiv"><span class="dynamicSpan">ANOTHER DYNAMIC FONT</span></div>
    <div class="dynamicDiv"><span class="dynamicSpan">AND YET ANOTHER DYNAMIC FONT</span></div>
</body>
</html>

所提出的迭代解决方案可以在两个方面显著加速:

1)将字体大小乘以某个常数,而不是加或减1。

2)首先,使用零过程常数,比如,将每个循环的大小增加一倍。然后,有了一个大致的想法从哪里开始,做同样的事情,并进行更精细的调整,比如乘以1.1。虽然完美主义者可能想要理想字体的精确整数像素大小,但大多数观察者不会注意到100和110像素之间的区别。如果你是一个完美主义者,那么重复第三次,做更细致的调整。

我没有编写特定的例程或插件来回答确切的问题,而是依赖于基本思想并编写代码的变体来处理各种布局问题,而不仅仅是文本,包括拟合div、跨度、图像……通过宽度、高度、面积、……在容器中,匹配另一个元素....

这里有一个例子:

  var                           nWindowH_px             = jQuery(window).height();
  var                           nWas                    = 0;
  var                           nTry                    = 5;

  do{
   nWas = nTry;
   nTry *= 2;
   jQuery('#divTitle').css('font-size' ,nTry +'px');
  }while( jQuery('#divTitle').height() < nWindowH_px );

  nTry = nWas;

  do{
   nWas = nTry;
   nTry = Math.floor( nTry * 1.1 );
   jQuery('#divTitle').css('font-size' ,nTry +'px');
  }while( nWas != nTry   &&   jQuery('#divTitle').height() < nWindowH_px );

  jQuery('#divTitle').css('font-size' ,nWas +'px');

你可以使用FitText.js (github页面)来解决这个问题。与TextFill相比是非常小和有效的。TextFill使用了昂贵的while循环,而FitText没有。

FitText也更灵活(我在一个有非常特殊要求的项目中使用它,工作起来像个冠军!)。

HTML:

<div class="container">
  <h1 id="responsive_headline">Your fancy title</h1>
</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="jquery.fittext.js"></script>
<script>
  jQuery("#responsive_headline").fitText();
</script>

你也可以设置选项:

<script>
  jQuery("#responsive_headline").fitText(1, { minFontSize: '30px', maxFontSize: '90px'});
</script>

CSS:

#responsive_headline {
   width: 100%;
   display: block;
}

如果你需要它,FitText也有一个非jquery版本。

这是我对OP的答案的修改。

简而言之,许多尝试优化此功能的人抱怨使用了循环。是的,虽然循环可能很慢,但其他方法可能不准确。

因此,我的方法是使用二进制搜索来找到最好的字体大小:

$.fn.textfill = function()
{
    var self = $(this);
    var parent = self.parent();

    var attr = self.attr('max-font-size');
    var maxFontSize = parseInt(attr, 10);
    var unit = attr.replace(maxFontSize, "");

    var minFontSize = parseInt(self.attr('min-font-size').replace(unit, ""));
    var fontSize = (maxFontSize + minFontSize) / 2;

    var maxHeight = parent.height();
    var maxWidth = parent.width();

    var textHeight;
    var textWidth;

    do
    {
        self.css('font-size', fontSize + unit);

        textHeight = self.height();
        textWidth = self.width();

        if(textHeight > maxHeight || textWidth > maxWidth)
        {
            maxFontSize = fontSize;
            fontSize = Math.floor((fontSize + minFontSize) / 2);
        }
        else if(textHeight < maxHeight || textWidth < maxWidth)
        {
            minFontSize = fontSize;
            fontSize = Math.floor((fontSize + maxFontSize) / 2);
        }
        else
            break;

    }
    while(maxFontSize - minFontSize > 1 && maxFontSize > minFontSize);

    self.css('font-size', fontSize + unit);

    return this;
}

function resizeText()
{
  $(".textfill").textfill();
}

$(document).ready(resizeText);
$(window).resize(resizeText);

这也允许元素指定最小和最大字体:

<div class="container">
    <div class="textfill" min-font-size="10px" max-font-size="72px">
        Text that will fill the container, to the best of its abilities, and it will <i>never</i> have overflow.
    </div>
</div>

此外,该算法是无单位的。你可以指定em, rem, %等,它将使用它的最终结果。

这是小提琴:https://jsfiddle.net/fkhqhnqe/1/

只是想增加我的内容表版本。

$.fn.fitInText = function() { this.each(function() { let textbox = $(this); let textboxNode = this; let mutationCallback = function(mutationsList, observer) { if (observer) { observer.disconnect(); } textbox.css('font-size', 0); let desiredHeight = textbox.css('height'); for (i = 12; i < 50; i++) { textbox.css('font-size', i); if (textbox.css('height') > desiredHeight) { textbox.css('font-size', i - 1); break; } } var config = { attributes: true, childList: true, subtree: true, characterData: true }; let newobserver = new MutationObserver(mutationCallback); newobserver.observe(textboxNode, config); }; mutationCallback(); }); } $('#inner').fitInText(); #outer { display: table; width: 100%; } #inner { border: 1px solid black; height: 170px; text-align: center; display: table-cell; vertical-align: middle; word-break: break-all; } <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div id="outer"> <div id="inner" contenteditable=true> TEST </div> </div>