【问题标题】:resize font to fit in a div (on one line)调整字体大小以适合 div(在一行上)
【发布时间】:2019-05-06 00:23:47
【问题描述】:

有人问过类似的问题,但解决方案确实与我正在尝试做的事情相吻合。基本上,我有一篇标题为 (<h1>) 的文章。我不想控制标题的长度,但我也不希望标题出现在多行上。有没有办法使用 css 或 jQuery 来根据 <div> 标签的宽度调整文本大小?

如果我能检测到文本与<div> 边缘的重叠,我知道我会用伪代码做什么:

var fontize = $("#title").css("font-size");
var i = /*remove unit from integer*/
while( /*text overlaps div*/ ){
    $("#title").css("font-size", --i+"pt");
}

如果有一个我可以设置的 CSS 属性会更好,但我似乎找不到一个(在这种情况下溢出不起作用)。

【问题讨论】:

  • 我喜欢倾向于 CSS 解决方案的态度。确实更好,因为它还可以为未启用或无法使用 Javascript 的用户正确显示(这可能是有原因的)。
  • 我不敢相信 css3 没有这方面的功能。在 JS 中感觉很肮脏……现在在响应式世界中,这一定是一种普遍的需求!

标签: jquery css


【解决方案1】:

CSS 没有,Javascript 有

你无法使用 CSS 来做到这一点,但你可以在 javascript/jQuery 中做到这一点。帮助您处理伪代码,因为您已经知道该怎么做。只是你不知道如何检测多余的宽度。

最好的方法是使用以下(至少)样式的 DIV:

position: absolute;
visibility: hidden;
white-space: nowrap;
font-family: /* same as your title's */

然后将您的文本复制到其中并设置一些起始字体大小。然后您可以遍历您的 while 循环并在 div 的宽度合适时停止。然后将计算出的字体大小设置为原始标题。

这样,这种检查将隐藏在用户的眼睛之外,因此它也会更快地工作。

顺便说一句:这是自动增长textarea 脚本的常用方式。他们使用与原始文本区域具有相同样式设置的虚拟 div,并在用户输入文本时调整区域的高度。因此,文本区域起初可能非常小,但如果用户输入大量文本,文本区域会自动增长以适应内容。

while循环优化

您可以优化您的 while 循环,通过执行以下操作显着减少迭代次数:

  1. 设置起始字体大小。
  2. 获取测试 DIV 的宽度。
  3. 计算 orig_div 和 test_div 之间的宽度因子。
  4. 按此系数调整字体大小,而不是增加/减少一个单位
  5. 测试 test_div 宽度

【讨论】:

  • 这很有趣。所以基本上,我可以使用隐藏的 div 来获取宽度,然后在 while(test_div.width > title_div.width) 中进行减量。那是小说。谢谢!
  • @Tim:在关于 while 循环优化的答案中查看我的其他信息。它应该会加快速度。
  • 我会给你因果报应,如果有人弄乱了这个工作;p
  • 我还可以想到一个 pure CSS3 解决方案,它依赖于 @media-query 功能,例如我的回答 [这里] (stackoverflow.com/a/18685884/1711186) 中建议的另一个相关的问题。好处当然是无需 Javascript 即可实现正确显示
  • @humanityANDpeace:这只是部分解决方案,因为它不适合文本,而只是根据媒体查询设置调整其大小。但这种调整大小只能是各种文本的一般近似值。个别字符串仍然不同。如果您有一些包含许多宽字符(m、w 等)的文本,它将比具有许多窄字符(i、l、j、f 等)的文本长得多。 Javascript 拟合将适用于两者...
【解决方案2】:

我遇到了类似的问题,这让我为此编写了自己的插件。看看jquery-quickfit(这与Robert Koritnik's 解决方案非常相似,我非常喜欢)。

为了防止标题跨越多行,只需添加一个css样式:

white-space:nowrap;

到元素。

在标题中包含 jquery 和 quickfit 之后。您可以通过以下方式触发 quickfit:

$('h1').quickfit();

它测量并计算适合文本的每个字母的大小不变测量,并使用它来计算适合文本到容器中的下一个最佳字体大小。

计算被缓存,这使得它在处理必须适应多个文本或必须多次适应文本时非常快,例如在调整窗口大小时(调整大小几乎没有性能损失)。

Demo for a similar situation as yours

更多文档、示例和源代码在项目页面上。

【讨论】:

  • “有一个图书馆!”
  • 而且这个库是一个单行器:P (fittextjs)
  • 如果你去掉换行符,所有的 JavaScript 都是单行的!
【解决方案3】:

这是我经常使用的 3 个函数,用于获取文本宽度、高度以及将大小调整为容器的宽度。

  • getTextWidth() 将返回包含在启动器中的文本的实际宽度。
  • getTextHeight(width) 将返回包含在具有特定指定宽度的启动器中的换行文本的实际高度。
  • autoTextSize(minSize, maxSize, truncate) 将根据最小和最大尺寸调整容器内文本的大小,使其适合。
  • autoTruncateText() 只会显示用户实际可以看到的字符,并以“...”结束文本。
(function ($) {
  $.fn.getTextWidth = function() {
    var spanText = $("BODY #spanCalculateTextWidth");

    if (spanText.size() <= 0) {
      spanText = $("<span id='spanCalculateTextWidth' style='filter: alpha(0);'></span>");
      spanText.appendTo("BODY");
    }

    var valu = this.val();
    if (!valu) valu = this.text();

    spanText.text(valu);

    spanText.css({
      "fontSize": this.css('fontSize'),
      "fontWeight": this.css('fontWeight'),
      "fontFamily": this.css('fontFamily'),
      "position": "absolute",
      "top": 0,
      "opacity": 0,
      "left": -2000
    });

    return spanText.outerWidth() + parseInt(this.css('paddingLeft')) + 'px';
  };

  $.fn.getTextHeight = function(width) {
    var spanText = $("BODY #spanCalculateTextHeight");

    if (spanText.size() <= 0) {
      spanText = $("<span id='spanCalculateTextHeight'></span>");
      spanText.appendTo("BODY");
    }

    var valu = this.val();
    if (!valu) valu = this.text();

    spanText.text(valu);

    spanText.css({
      "fontSize": this.css('fontSize'),
      "fontWeight": this.css('fontWeight'),
      "fontFamily": this.css('fontFamily'),
      "top": 0,
      "left": -1 * parseInt(width) + 'px',
      "position": 'absolute',
      "display": "inline-block",
      "width": width
    });

    return spanText.innerHeight() + 'px';
  };

  /**
   * Adjust the font-size of the text so it fits the container.
   *
   * @param minSize     Minimum font size?
   * @param maxSize     Maximum font size?
   * @param truncate    Truncate text after sizing to make sure it fits?
   */
  $.fn.autoTextSize = function(minSize, maxSize, truncate) {
    var _self = this,
        _width = _self.innerWidth(),
        _textWidth = parseInt(_self.getTextWidth()),
        _fontSize = parseInt(_self.css('font-size'));

    while (_width < _textWidth || (maxSize && _fontSize > parseInt(maxSize))) {
      if (minSize && _fontSize <= parseInt(minSize)) break;

      _fontSize--;
      _self.css('font-size', _fontSize + 'px');

      _textWidth = parseInt(_self.getTextWidth());
    }

    if (truncate) _self.autoTruncateText();
  };

  /**
   * Function that truncates the text inside a container according to the
   * width and height of that container. In other words, makes it fit by chopping
   * off characters and adding '...'.
   */
  $.fn.autoTruncateText = function() {
    var _self = this,
        _width = _self.outerWidth(),
        _textHeight = parseInt(_self.getTextHeight(_width)),
        _text = _self.text();

    // As long as the height of the text is higher than that
    // of the container, we'll keep removing a character.
    while (_textHeight > _self.outerHeight()) {
      _text = _text.slice(0,-1);
      _self.text(_text);
      _textHeight = parseInt(_self.getTextHeight(_width));
      _truncated = true;
    }

    // When we actually truncated the text, we'll remove the last
    // 3 characters and replace it with '...'.
    if (!_truncated) return;
    _text = _text.slice(0, -3);

    // Make sure there is no dot or space right in front of '...'.
    var lastChar = _text[_text.length - 1];
    if (lastChar == ' ' || lastChar == '.') _text = _text.slice(0, -1);
    _self.text(_text + '...');
  };
})(jQuery);

【讨论】:

  • autotruncate 可以通过添加两条规则来实现纯 CSS:'overflow: hidden' 和 'text-overflow: ellipsis'。我知道这真的很老了,但是嘿-也许有人会受益:)
【解决方案4】:

非常简单。为文本创建一个 span,获取宽度并减小 font-size,直到 span 与 div 容器的宽度相同:

while($("#container").find("span").width() > $("#container").width()) {
    var currentFontSize = parseInt($("#container").find("span").css("font-size")); 
    $("#container").find("span").css("font-size",currentFontSize-1); 
}

【讨论】:

  • 我必须在span 上设置white-space:nowrap; 才能使其正常工作。不过很好的解决方案。
【解决方案5】:

@克洛维斯六 谢谢你的回答。事实证明它对我非常有用。很遗憾,我只能感谢你,只能投赞成票。

注意:我必须将 "$J(" 更改为 "$(" 才能在我的配置上工作。

evendo,这超出了这个问题的范围,而不是在使用 SO,我扩展了您的代码以使其适用于具有最大高度的多行框。

  /**
   * Adjust the font-size of the text so it fits the container
   *
   * support multi-line, based on css 'max-height'.
   * 
   * @param minSize     Minimum font size?
   * @param maxSize     Maximum font size?
   */
  $.fn.autoTextSize_UseMaxHeight = function(minSize, maxSize) {
    var _self = this,
        _width = _self.innerWidth(),
        _boxHeight = parseInt(_self.css('max-height')),
        _textHeight = parseInt(_self.getTextHeight(_width)),
        _fontSize = parseInt(_self.css('font-size'));

    while (_boxHeight < _textHeight || (maxSize && _fontSize > parseInt(maxSize))) {
      if (minSize && _fontSize <= parseInt(minSize)) break;

      _fontSize--;
      _self.css('font-size', _fontSize + 'px');

      _textHeight = parseInt(_self.getTextHeight(_width));
    }
  };

PS:我知道这应该是一个评论,但是 cmets 不让我正确发布代码。

【讨论】:

    【解决方案6】:

    今天我创建了一个名为 jQuery Responsive Headlines 的 jQuery 插件,它完全符合您的要求:它调整标题的字体大小以适应其包含的元素(div、body 或其他) .您可以设置一些不错的选项来自定义插件的行为,我还采取了预防措施来添加对 Ben Altmans Throttle/Debounce 功能的支持,以提高性能并在用户调整大小时减轻浏览器/计算机上的负载窗户。在最简单的用例中,代码如下所示:

    $('h1').responsiveHeadlines();
    

    ...这会将页面元素上的所有 h1 转换为响应式单行标题,调整其大小以适应父/容器元素。

    您将找到插件on this GitHub page 的源代码和更长的描述。

    祝你好运!

    【讨论】:

    • 非常感谢先生。 +1!!
    【解决方案7】:

    这对于您的要求可能有点过头了,但我发现这个库非常有用:

    http://fittextjs.com/

    但它只适用于单行,所以我不确定这是否符合您的要求。

    【讨论】:

      【解决方案8】:

      我的解决方案,作为基于Robert Koritnik's answer的jQuery扩展:

      $.fn.fitToWidth=function(){
          $(this).wrapInner("<span style='display:inline;font:inherit;white-space:inherit;'></span>").each(function(){
              var $t=$(this);
              var a=$t.outerWidth(),
                  $s=$t.children("span"),
                  f=parseFloat($t.css("font-size"));
              while($t.children("span").outerWidth() > a) $t.css("font-size",--f);
              $t.html($s.html());
          });
      }
      

      这实际上创建了一个临时跨度继承重要属性,并比较宽度差异。当然,需要优化 while 循环(减少两个大小之间计算的百分比差异)。

      示例用法:

      $(function(){
          $("h1").fitToWidth();
      });
      

      【讨论】:

        【解决方案9】:

        我不喜欢那里的其他解决方案,所以,这里有另一个解决方案。它需要您正在调整文本大小的标签:

        1. 固定高度

        2. 不要太长以至于超出 10 像素的边界 - 想法是,您不希望它低于 10 像素并调整文本大小以使其变得不可读。在我们的用例中不是问题,但如果在您的用例中可能,您需要考虑在运行之前单独截断。

        它使用二分搜索来找到最佳尺寸,因此我怀疑它优于这里和其他地方的许多其他解决方案,这些解决方案只是一遍又一遍地将字体大小降低一个像素。今天的大多数浏览器也支持字体大小的小数,并且这个脚本在那里有一些好处,因为它会在最佳答案的 0.1px 范围内到达,不管是什么,即使它是一个相对较长的小数。您可以轻松地将 max - fontSize &lt; .1 更改为 .1 以外的其他值,以降低 CPU 使用率的精度。

        用法:

        $('div.event').fitText();
        

        代码:

        (function() {
            function resizeLoop(testTag, checkSize) {
                var fontSize = 10;
                var min = 10;
                var max = 0;
                var exceeded = false;
        
                for(var i = 0; i < 30; i++) {
                    testTag.css('font-size', fontSize);
                    if (checkSize(testTag)) {
                        max = fontSize;
                        fontSize = (fontSize + min) / 2;
                    } else {
                        if (max == 0) {
                            // Start by growing exponentially
                            min = fontSize;
                            fontSize *= 2;
                        } else {
                            // If we're within .1px of max anyway, call it a day
                            if (max - fontSize < .1)
                                break;
        
                            // If we've seen a max, move half way to it
                            min = fontSize;
                            fontSize = (fontSize + max) / 2;
                        }
                    }
                }
        
                return fontSize;
            }
        
            function sizeText(tag) {
                var width = tag.width();
                var height = tag.height();
        
                // Clone original tag and append to the same place so we keep its original styles, especially font
                var testTag = tag.clone(true)
                .appendTo(tag.parent())
                .css({
                    position: 'absolute',
                    left: 0, top: 0,
                    width: 'auto', height: 'auto'
                });
        
                var fontSize;
        
                // TODO: This decision of 10 characters is arbitrary. Come up
                // with a smarter decision basis.
                if (tag.text().length < 10) {
                    fontSize = resizeLoop(testTag, function(t) {
                        return t.width() > width || t.height() > height;
                    });
                } else {
                    testTag.css('width', width);
                    fontSize = resizeLoop(testTag, function(t) {
                        return t.height() > height;
                    });
                }
        
                testTag.remove();
                tag.css('font-size', fontSize);
            };
        
            $.fn.fitText = function() {
                this.each(function(i, tag) {
                    sizeText($(tag));
                });
            };
        })();
        

        http://jsfiddle.net/b9chris/et6N6/1/

        【讨论】:

        • 感谢您的提琴。我对垂直对齐进行了更改jsfiddle.net/et6N6/23希望将来有人可以使用它。
        【解决方案10】:

        github 上有一个可用的 jquery 插件,它可能只是做你想做的事。它被称为 jquery-quickfit。它使用 Jquery 提供了一种快速而肮脏的方法来将文本放入其周围的容器中。

        HTML:

        <div id="quickfit">Text to fit*</div>
        

        Javascript:

        <script src=".../jquery.min.js" type="text/javascript" />
        <script src="../script/jquery.quickfit.js" type="text/javascript" />
        
        <script type="text/javascript">
          $(function() {
            $('#quickfit').quickfit();
          });
        </script>
        

        更多信息: https://github.com/chunksnbits/jquery-quickfit

        【讨论】:

          【解决方案11】:

          我做了一些 jQuery 的东西来调整你的 div 的字体大小。它甚至适用于多行,但只会增加字体大小(如果需要,您可以简单地将默认字体大小设置为 1px)。 我增加字体大小直到有一个换行符,设置减少它1px。没有其他花哨的东西,隐藏的 div 左右。只需将 quickfit 类添加到您的 div 中即可。

          $(".quickfit").each(function() {
              var jThis=$(this);
              var fontSize=parseInt(jThis.css("font-size"));
              var originalLines=jThis.height()/fontSize;
          
              for(var i=0;originalLines>=jThis.height()/fontSize && i<30;i++)
              { jThis.css("font-size",""+(++fontSize)+"px"); }
              jThis.css("font-size",""+(fontSize-1)+"px");
          });
          

          【讨论】:

            【解决方案12】:

            我知道这个问题似乎已经得到了相当彻底的回答,但在某些情况下,这里的解决方案可能会导致其他问题。例如,tkahn 的库看起来非常有用,但它改变了它所附加的元素的显示,这可能是一个问题。就我而言,它使我无法将文本垂直和水平居中。经过一番折腾和试验,我想出了一个简单的方法,涉及 jQuery 以将文本放在一行上,而无需修改父元素的任何属性。请注意,在这段代码中,我使用了 Robert Koritnik 的建议来优化 while 循环。要使用此代码,只需将“font_fix”类添加到包含需要在一行中适合它的文本的任何 div。对于标题,这可能需要在标题周围有一个额外的 div。然后,为固定大小的 div 调用一次此函数,或将其设置为调整大小和/或方向的侦听器以获取不同的大小。

            function fixFontSize(minimumSize){
              var x = document.getElementsByClassName("font_fix");
              for(var i = 0; i < x.length; i++){
                x[i].innerHTML = '<div class="font_fix_inner" style="white-space: nowrap; display: inline-block;">' + x[i].innerHTML + '</div>';
            
                var y = x[i].getElementsByClassName("font_fix_inner")[0];
                var size = parseInt($("#" + x[i].id).css("font-size"));
            
                size *= x[i].clientWidth / y.clientWidth;
                while(y.clientWidth > x[i].clientWidth){
                  size--;
                  if(size <= minimumSize){
                    size = minimumSize;
                    y.style.maxWidth = "100%";
                    y.style.overflow = "hidden";
                    y.style.textOverflow = "ellipsis";
                    $("#" + x[i].id).css("font-size", size + "px");
                    break;
                  }
                  $("#" + x[i].id).css("font-size", size + "px");
                }
              }
            }
            

            现在,为了方便起见,我添加了一个额外的案例,如果文本变得太小(低于传递给函数的最小阈值),文本将被截断。达到该阈值后发生的另一件事可能需要也可能不需要,那就是将最大宽度更改为 100%。这应该针对每个用户的场景进行更改。最后,将此答案作为替代发布的全部目的是使其能够将内容集中在父 div 中。这可以通过向内部 div 类添加 css 属性来轻松完成,如下所示:

            .font_fix_inner {
              position: relative;
              float: center;
              top: 50%;
              -webkit-transform: translateY(-50%);
              transform: translateY(-50%);
            }
            

            希望这对某人有所帮助!

            【讨论】:

              【解决方案13】:

              在此处查看此示例http://codepen.io/LukeXF/pen/yOQWNb,您可以使用一个简单的页面加载和页面调整功能来实现奇迹。虽然要小心不要因为太多的函数调用而延迟客户端。 (示例有延迟)。

              function resize() {  
                  $('.resize').each(function(i, obj) {
                      $(this).css('font-size', '8em');
              
                      while ($(this).width() > $(this).parent().width()) {
                          $(this).css('font-size', (parseInt($(this).css('font-size')) - 1) + "px");
                      }
                  });
              }
              

              【讨论】:

                【解决方案14】:

                在某些情况下,您可以在 css 中完成此操作,但它很脆弱且依赖于上下文。

                css font-sizes 可以接受相对于屏幕的尺寸。如果标题的包含元素的大小相对于屏幕大小,您可以通过简单地设置font-size: 3vw 或适当的大小来完成此操作,以便文本适合一行。如果调整屏幕大小,文本也会随之改变。请注意,宽度将取决于确切的文本内容,如果将字符添加到曾经适合单行的文本中,则可能需要调整字体大小。

                看到这个小提琴:https://jsfiddle.net/mguk8v27/

                【讨论】:

                  猜你喜欢
                  • 2011-03-24
                  • 2011-11-24
                  • 2015-09-16
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-10-22
                  • 2013-12-21
                  • 1970-01-01
                  相关资源
                  最近更新 更多