调整字体大小以适合div(在一行上)

时间:2010-08-03 22:06:02

标签: jquery css

已经提出了类似的问题,但解决方案确实与我想要做的事情相吻合。基本上,我有一篇标题为<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属性我可以设置更好,但我似乎找不到(溢出在这种情况下不起作用)。

14 个答案:

答案 0 :(得分:45)

CSS no,Javascript yes

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

最好的方法是让DIV具有以下(至少)风格:

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

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

这样,这种检查将隐藏在用户眼中,因此也可以更快地完成。

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

循环优化

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

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

答案 1 :(得分:22)

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

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

white-space:nowrap;

到元素。

在标题中包含jquery和quickfit之后。您可以使用以下命令触发quickfit:

$('h1').quickfit();

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

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

Demo for a similar situation as yours

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

答案 2 :(得分:20)

以下是我经常使用的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);

答案 3 :(得分:4)

@Clovis Six 谢谢你的回答。它对我来说非常有用。遗憾的是,我不能仅仅因为投票而感谢你。

注意:我必须更改“$ J(”for“$(”才能使用我的配置。

evendo,这超出了这个问题的范围而不是使用SO,我扩展了你的代码,使其适用于具有max-height的多行框。

  /**
   * 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:我知道这应该是评论,但评论不允许我正确发布代码。

答案 4 :(得分:4)

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

$('h1').responsiveHeadlines();

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

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

祝你好运!

答案 5 :(得分:3)

很简单。为文本创建范围,获取宽度并减小字体大小,直到跨度具有与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); 
}

答案 6 :(得分:1)

这可能对您的要求有些过分,但我发现这个库非常有用:

http://fittextjs.com/

但这对单线只有好处,所以我不确定这是否符合您的要求。

答案 7 :(得分:1)

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

  1. 固定高度

  2. 不要太久以至于它超过了10px的边界 - 这个想法是,你不希望它在那之下拍摄并调整文本大小变得难以理解。在我们的用例中不是问题,但是如果它可能在你的用例中你想要在运行之前给出一些单独截断的想法。

  3. 它使用二进制搜索来找到最佳大小,因此我怀疑它在这里和其他地方的表现优于其他许多解决方案,只是将字体大小逐步减去一个像素。今天大多数浏览器也支持字体大小的小数,这个脚本有一些好处,因为它会达到最佳答案的.1px内,无论是什么,即使它是一个相对较长的小数。您可以轻松地将max - fontSize < .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/

答案 8 :(得分:1)

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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

答案 11 :(得分:0)

我知道这个问题似乎得到了相当彻底的回答,但在某些情况下,这里的解决方案可能会导致其他问题。例如,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中。这可以通过将css属性添加到内部div类来轻松完成,如下所示:

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

希望这有助于某人!

答案 12 :(得分:0)

在这里查看此示例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");
        }
    });
}

答案 13 :(得分:0)

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

css 字体大小可以接受屏幕的相对大小。如果标题的包含元素的大小是相对于屏幕大小的大小,则可以通过简单地设置font-size: 3vw或适当的大小以使文本适合一行而实现此目的。如果调整了屏幕大小,则文本也会随之变化。请注意,宽度将取决于确切的文本内容,并且如果您将字符添加到以前适合一行的文本中,则可能需要调整字体大小。

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