【问题标题】:resize font to fit in a div (on one line)调整字体大小以适应 div(在一行上)
【发布时间】:2011-03-24 23:43:21
【问题描述】:

有人问过类似的问题,但解决方案确实与我正在尝试做的事情相吻合。基本上,我有一篇标题为 (<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】:

这里是我经常使用的 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'。我知道这真的很老了,但是嘿-也许有人会受益:)
【解决方案3】:

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

注意:我必须将 "$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 不允许我正确发布代码。

【讨论】:

    【解决方案4】:

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

    http://fittextjs.com/

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

    【讨论】:

      【解决方案5】:

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

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

      white-space:nowrap;
      

      到元素。

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

      $('h1').quickfit();
      

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

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

      Demo for a similar situation as yours

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

      【讨论】:

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

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

      $('h1').responsiveHeadlines();
      

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

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

      祝你好运!

      【讨论】:

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

      我的解决方案,作为基于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();
      });
      

      【讨论】:

        【解决方案8】:

        我做了一些 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");
        });
        

        【讨论】:

          【解决方案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】:

            非常简单。为文本创建一个 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; 才能使其正常工作。不过很好的解决方案。
            【解决方案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/

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-05-23
                  • 2017-10-22
                  • 2018-09-04
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-12-19
                  • 2016-10-06
                  相关资源
                  最近更新 更多