jQuery 调整字体大小以适应 div(在一行上)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3401136/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
resize font to fit in a div (on one line)
提问by TCCV
There have been similar questions asked, but the solutions do mesh with what I'm trying to do. Basically, I have an article with a title (<h1>
). I don't want to control the length of the title, but I also don't want the title to appear on multiple lines. Is there a way with css or jQuery to resize text based on the width of a <div>
tag?
有人问过类似的问题,但解决方案确实与我正在尝试做的事情相吻合。基本上,我有一篇标题为 ( <h1>
)的文章。我不想控制标题的长度,但我也不希望标题出现在多行上。有没有办法使用 css 或 jQuery 根据<div>
标签的宽度调整文本大小?
I know what I would do with pseudocode if I could detect the overlap of the text to the edge of the <div>
:
如果我可以检测到文本与 边缘的重叠,我知道我会用伪代码做什么<div>
:
var fontize = $("#title").css("font-size");
var i = /*remove unit from integer*/
while( /*text overlaps div*/ ){
$("#title").css("font-size", --i+"pt");
}
If there's a CSS attribute I can set that would be even nicer, but I can't seem to find one (overflow wouldn't work in this situation).
如果有我可以设置的 CSS 属性会更好,但我似乎找不到(溢出在这种情况下不起作用)。
采纳答案by Robert Koritnik
CSS no, Javascript yes
CSS 否,Javascript 是
There's no way you could do this using CSS, but you can do it in javascript/jQuery. To help you with your pseudo code because you already know what to do. It's just that you don't know how to detect excess width.
您无法使用 CSS 做到这一点,但您可以在 javascript/jQuery 中做到这一点。帮助您编写伪代码,因为您已经知道该怎么做。只是你不知道如何检测多余的宽度。
The best way would be to have a DIV with following (at least) style:
最好的方法是拥有一个具有以下(至少)样式的 DIV:
position: absolute;
visibility: hidden;
white-space: nowrap;
font-family: /* same as your title's */
then copy your text to it and set some starting font size. Then you can iterate through your while loop and stop when div's width is appropriate. Then set calculated font size to your original title.
然后将您的文本复制到它并设置一些起始字体大小。然后你可以遍历你的 while 循环并在 div 的宽度合适时停止。然后将计算出的字体大小设置为您的原始标题。
This way this checking will be hidden from user's eyes and it will therefore work faster as well.
通过这种方式,这种检查将不会被用户看到,因此它的工作速度也会更快。
BTW: This is the usual way how auto growing textarea
scripts work. They use dummy divs with same style settings as the original text area and adjust area's height as the user types in text. So Text area can be quite small at first but if user types in lots of text it will auto grow to accommodate content.
顺便说一句:这是自动增长textarea
脚本工作的常用方式。他们使用与原始文本区域具有相同样式设置的虚拟 div,并在用户键入文本时调整区域的高度。所以文本区域一开始可能很小,但如果用户输入大量文本,它会自动增长以容纳内容。
While loop optimization
while循环优化
You could optimize your while loop to decrease the number of iterations considerably by doing this:
您可以通过执行以下操作来优化 while 循环以显着减少迭代次数:
- Set a starting font size.
- get test DIV's width.
- calculate width factor between orig_div and test_div.
- adjust font size by this factor rather than increase/decrease by one unit
- test test_div width
- 设置起始字体大小。
- 获取测试 DIV 的宽度。
- 计算 orig_div 和 test_div 之间的宽度因子。
- 通过这个系数调整字体大小而不是增加/减少一个单位
- 测试 test_div 宽度
回答by chunks_n_bits
I had a similar issue, which made me write my own plugin for this. Have a look at jquery-quickfit(which is quite similar to Robert Koritnik'ssolution, which I really like).
我有一个类似的问题,这让我为此编写了自己的插件。看看jquery-quickfit(这与我非常喜欢的Robert Koritnik 的解决方案非常相似)。
In order to prevent the headline to span multiple lines, just add a css style of:
为了防止标题跨越多行,只需添加一个css样式:
white-space:nowrap;
to the element.
到元素。
After including jquery and quickfit in the header. You can trigger quickfit with:
在标题中包含 jquery 和 quickfit 之后。您可以通过以下方式触发 quickfit:
$('h1').quickfit();
It meassures and calculates a size invariant meassure for each letter of the text to fit and uses this to calculate the next best font-size which fits the text into the container.
它测量并计算适合文本的每个字母的大小不变度量,并使用它来计算适合文本到容器中的下一个最佳字体大小。
The calculations are cached, which makes it very fast when dealing having to fit multiple text or having to fit a text multiple times, like e.g., on window resize (there is almost no performance penalty on resize).
计算被缓存,这使得它在处理必须适合多个文本或必须多次适合文本时非常快,例如在窗口调整大小时(调整大小几乎没有性能损失)。
Demo for a similar situation as yours
Further documentation, examples and source code are on the project page.
更多文档、示例和源代码位于项目页面上。
回答by Clovis Six
Here are 3 functions I use frequently to get the text width, height and adjust the size to the container's width.
这是我经常使用的 3 个函数来获取文本宽度、高度并将大小调整为容器的宽度。
- getTextWidth()will return you the actual width of the text contained in the initiator.
- getTextHeight(width)will return the actual height of the wrapped text contained in the initiator with a certain specified width.
- autoTextSize(minSize, maxSize, truncate)will size the text within the container so it fits, considering a minimum and maximum size.
- autoTruncateText()will only show the characters the user can actually see and end the text with '...'.
- 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);
回答by Christ
@Clovis Six thank for your answer. It prove very usefull to me. A pity I cannot thanks you more than just a vote up.
@Clovis Six 感谢您的回答。它证明对我非常有用。遗憾的是,我只能感谢您,而不仅仅是投票。
Note: I have to change the "$J(" for "$(" for it to work on my config.
注意:我必须将“$J(”更改为“$(”才能在我的配置上工作。
evendo, this out of the scope of this question and not in the use of SO, I extended your code to make it work for multi-line box with max-height.
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: I know this should be a comment, but comments don't let me post code properly.
PS:我知道这应该是评论,但评论不允许我正确发布代码。
回答by tkahn
Today I created a jQuery plugin called jQuery Responsive Headlinesthat does exactly what you want: it adjusts the font size of a headline to fit inside its containing element (a div, the body or whatever). There are some nice options that you can set to customize the behavior of the plugin and I have also taken the precaution to add support for Ben Altmans Throttle/Debounce functionality to increase performance and ease the load on the browser/computer when the user resizes the window. In it's most simple use case the code would look like this:
今天我创建了一个名为jQuery Responsive Headlines的 jQuery 插件,它完全符合您的要求:它调整标题的字体大小以适应其包含元素(div、正文或其他任何内容)。您可以设置一些不错的选项来自定义插件的行为,我还采取了预防措施来添加对 Ben Altmans Throttle/Debounce 功能的支持,以提高性能并在用户调整大小时减轻浏览器/计算机的负载窗户。在最简单的用例中,代码如下所示:
$('h1').responsiveHeadlines();
...which would turn all h1 on the page elements into responsive one-line headlines that adjust their sizes to the parent/container element.
...这会将页面元素上的所有 h1 转换为响应式单行标题,根据父/容器元素调整其大小。
You'll find the source code and a longer description of the plugin on this GitHub page.
您将在此 GitHub 页面 上找到该插件的源代码和更长的描述。
Good luck!
祝你好运!
回答by MeV
Very simple. Create a span for the text, get the width and reduce font-size until the span has the same width of the div container:
很简单。为文本创建一个跨度,获取宽度并减小字体大小,直到跨度与 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);
}
回答by Chris Moschini
I wasn't in love with the other solutions out there, so, here's another one. It requires the tag you're resizing the text inside of:
我不喜欢那里的其他解决方案,所以,这是另一个解决方案。它需要您在其中调整文本大小的标签:
Be fixed height
Not be so long that it'd overrun the boundaries at 10px - the idea being, you don't want it to shoot below that and resize text to become unreadable. Not an issue in our use case, but if it's possible in yours you'd want to give some thoughts to separately truncating before running this.
固定高度
不要太长,以至于它会超出 10 像素的边界 - 这个想法是,您不希望它在低于 10 像素的情况下拍摄并调整文本大小以使其变得不可读。在我们的用例中这不是问题,但如果在您的用例中可能,您希望在运行此之前考虑单独截断。
It uses a binary search to find the best size so I suspect it outperforms a lot of the other solutions here and elsewhere that just step the font-size down by a pixel over and over. Most browsers today support decimals in font sizes as well, and this script has some benefits there since it will get to within .1px of the best answer, whatever that is, even if it's a relatively long decimal. You could easily change the max - fontSize < .1
to some other value than .1
to get less precision for less CPU usage.
它使用二进制搜索来找到最佳大小,所以我怀疑它比这里和其他地方的许多其他解决方案都要好,这些解决方案只是一遍又一遍地将字体大小降低一个像素。今天的大多数浏览器也支持字体大小的小数,并且这个脚本在那里有一些好处,因为它会到达最佳答案的 .1px 之内,无论是什么,即使它是一个相对较长的小数。您可以轻松地将 更改max - fontSize < .1
为其他值,而不是.1
为了减少 CPU 使用率而获得较低的精度。
Usage:
用法:
$('div.event').fitText();
Code:
代码:
(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));
});
};
})();
回答by Melicerte
There is a jquery plugin available on github that probably just do what you want. It is called jquery-quickfit. It uses Jquery to provide a quick and dirty approach to fitting text into its surrounding container.
github 上有一个 jquery 插件,它可能只是做你想做的。它被称为 jquery-quickfit。它使用 Jquery 提供一种快速而肮脏的方法来将文本放入其周围的容器中。
HTML:
HTML:
<div id="quickfit">Text to fit*</div>
Javascript:
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>
More information: https://github.com/chunksnbits/jquery-quickfit
回答by SEoF
My solution, as a jQuery extension based on Robert Koritnik's answer:
我的解决方案,作为基于Robert Koritnik 的回答的 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());
});
}
This actually creates a temporary span inheriting important properties, and compares the width difference. Granted, the while loop needs to be optimised (reduce by a percentage difference calculated between the two sizes).
这实际上创建了一个继承重要属性的临时跨度,并比较了宽度差异。当然,while 循环需要优化(减少两个大小之间计算的百分比差异)。
Example usage:
用法示例:
$(function(){
$("h1").fitToWidth();
});
回答by Cory
This may be overkill for what you require, but I found this library to be very helpful:
这对于您的需求可能有点过分,但我发现这个库非常有帮助:
It's only good for single lines though, so I'm not certain if that fits your requirement.
不过,它只适用于单行,所以我不确定这是否符合您的要求。