JavaScript Scale Text to Fit in Fixed Div
In JavaScript/jQuery, how can I scale a varying-length line of text inside a fixed-width Div so that the one line always fits inside 开发者_运维技巧the Div (as one line)?
This is somewhat of a hack, but will do what you want.
<div id="hidden-resizer" style="visibility: hidden"></div>
Place this at the bottom of your page, where it will not be moving other elements on the page.
Then do this:
var size;
var desired_width = 50;
var resizer = $("#hidden-resizer");
resizer.html("This is the text I want to resize.");
while(resizer.width() > desired_width) {
size = parseInt(resizer.css("font-size"), 10);
resizer.css("font-size", size - 1);
}
$("#target-location").css("font-size", size).html(resizer.html());
HTML:
<div class="box" style="width:700px">This is a sentence</div>
<div class="box" style="width:600px">This is a sentence</div>
<div class="box" style="width:500px">This is a sentence</div>
<div class="box" style="width:400px">This is a sentence</div>
JavaScript:
$( '.box' ).each(function ( i, box ) {
var width = $( box ).width(),
html = '<span style="white-space:nowrap"></span>',
line = $( box ).wrapInner( html ).children()[ 0 ],
n = 100;
$( box ).css( 'font-size', n );
while ( $( line ).width() > width ) {
$( box ).css( 'font-size', --n );
}
$( box ).text( $( line ).text() );
});
Live demo: http://jsfiddle.net/e8B9j/2/show/
Remove "/show/" from the URL to view the code.
I have written a jQuery plugin to do this:
http://michikono.github.com/boxfit
The plugin will scale the text both horizontally and vertically to the maximum available size inside the box and then center it.
The only thing you need to do is define a div with text inside it:
<div id="scale">some text</div>
And then call:
$('#scale').boxfit()
The method will accept some arguments to disable/enable text wrapping and centered alignment.
For new Browsers you can use Inline-SVG. This can be scaled like an image via CSS .. !
.smallerVersion{
max-width: 50%
}
<!DOCTYPE html>
<html>
<head>
<title>Scale SVG example</title>
</head>
<body>
<h2>Default version:</h2>
<svg viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg">
<style>
.small { font: italic 13px sans-serif; }
.heavy { font: bold 30px sans-serif; }
/* Note that the color of the text is set with the *
* fill property, the color property is for HTML only */
.Rrrrr { font: italic 40px serif; fill: red; }
</style>
<text x="20" y="35" class="small">My</text>
<text x="40" y="35" class="heavy">cat</text>
<text x="55" y="55" class="small">is</text>
<text x="65" y="55" class="Rrrrr">Grumpy!</text>
</svg>
<h2>Scaled version:</h2>
<svg class="smallerVersion" viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg">
<style>
.small { font: italic 13px sans-serif; }
.heavy { font: bold 30px sans-serif; }
/* Note that the color of the text is set with the *
* fill property, the color property is for HTML only */
.Rrrrr { font: italic 40px serif; fill: red; }
</style>
<text x="20" y="35" class="small">My</text>
<text x="40" y="35" class="heavy">cat</text>
<text x="55" y="55" class="small">is</text>
<text x="65" y="55" class="Rrrrr">Grumpy!</text>
</svg>
</body>
</html>
(width: 100%;
)
Most of the other answers use a loop to reduce the font-size until it fits on the div, this is VERY slow since the page needs to re-render the element each time the font changes size. I eventually had to write my own algorithm to make it perform in a way that allowed me to update its contents periodically without freezing the user browser. I added some other functionality (rotating text, adding padding) and packaged it as a jQuery plugin, you can get it at:
https://github.com/DanielHoffmann/jquery-bigtext
simply call
$("#text").bigText();
and it will fit nicely on your container.
See it in action here:
http://danielhoffmann.github.io/jquery-bigtext/
For now it has some limitations, the div must have a fixed height and width and it does not support wrapping text into multiple lines.
Edit2: I have now fixed those problems and limitations and added more options. You can set maximum font-size and you can also choose to limit the font-size using either width, height or both (default is both). I will work into accepting a max-width and max-height values in the wrapper element.
I don't know of an exact way, but here's an approximation:
var factor = 1/3; // approximate width-to-height ratio
var div = $('#mydiv');
div.css('font-size', div.width() / (div.text().length * factor) + 'px');
You will need to adjust factor
based on the font you are using. 1/3 seems to work okay for Times New Roman.
Modified version of @sworoc with the support of the target class
function resizeText(text, size, target) {
var fontSize = 0;
var resizer = $('<span>');
resizer.html(text).hide();
resizer.attr('class', target.attr('class'));
$('body').append(resizer);
while(resizer.width() < size) {
fontSize++
resizer.css("font-size", fontSize);
}
target.css('font-size', fontSize).html(text);
resizer.remove();
}
Usage
resizeText("@arunoda", 350, $('#username'));
Inside your div, have the text in a span that has no padding. Then the span's width will be the length of the text.
Untested code for finding the correct font-size to use:
var objSpan = $('.spanThatHoldsText');
var intDivWidth = $('.divThatHasAFixedWidth').width();
var intResultSize;
for (var intFontSize = 1; intFontSize < 100; intFontSize++)
objSpan.css('font-size', intFontSize);
if (objSpan.width() > intDivWidth) {
intResultSize = intFontSize - 1;
break;
}
}
objSpan.css('font-size', intResultSize);
Here is a coffeescript solution I came up with. I clone the element that is needing to have it's font resized then remove that at the end of the calculation. To increase performance I have some code that only executes the calculation after resizing has stopped for 200ms.
Tag elements with class autofont for this to be done unobtrusively.
#
# Automagically resize fonts to fit the width of the
# container they are in as much as possible.
#
fixfonts = ->
scaler = 1.2
for element in $('.autofont')
size = 1
element = $(element)
desired_width = $(element).width()
resizer = $(element.clone())
resizer.css
'font-size': "#{size}em"
'max-width': desired_width
'display': 'inline'
'width': 'auto'
resizer.insertAfter(element)
while resizer.width() < desired_width
size = size * scaler
resizer.css
'font-size': "#{size}em"
$(element).css
'font-size': "#{size / scaler }em"
resizer.remove()
$(document).ready =>
fixfonts()
timeout = 0
doresize = ->
timeout--
if timeout == 0
fixfonts()
$(window).resize ->
timeout++
window.setTimeout doresize, 200
I couldn't figure out how to add this to sworoc's post but i thought i would share anyway: Lonesomeday's solution gets messed up if you are using any sort of AJAX navigation. I modified it slightly to:
if ($('#hidden-resizer').length == 0){
$('<div />', {id: 'hidden-resizer'}).hide().appendTo(document.body);
}
I had a similar issue, which made me write my own plugin for this. One solution is to use the shrink-to-fit-approach, as described above. However if you have to fit multiple items or are concerned with performance, e.g., on window resize, have a look at jquery-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 (there is virtually no performance hit from the 2nd resize on forward) when dealing with multiple texts or having to fit a text multiple times, like e.g., on window resize.
Production example, fitting 14x16x2 texts
Here's my modification to @teambob's suggestion (in above comments) that takes also the height of the container into account:
<div class="box" style="width:700px; height:200px">This is a sentence</div>
<div class="box" style="width:600px; height:100px">This is a sentence</div>
<div class="box" style="width:500px; height:50px">This is a sentence</div>
<div class="box" style="width:400px; height:20px">This is a sentence</div>
And then..
$('.box').each(function(i, box) {
var width = $(box).width(),
height = $(box).height(),
html = '<span style="white-space:nowrap">',
line = $(box).wrapInner(html).children()[0],
maxSizePX = 2000;
$(box).css('font-size', maxSizePX);
var widthFontSize = Math.floor(width/$(line).width()*maxSizePX),
heightFontSize = Math.floor(height/$(line).height()*maxSizePX),
maximalFontSize = Math.min(widthFontSize, heightFontSize, maxSizePX);
$(box).css('font-size', maximalFontSize);
$(box).text($(line).text());
});
See Fiddle
Im not sure there is any accurate way to measure text's width in pixels (which is what you're really looking for at the root of your problem), as each browser may display the fonts differently. You might want to try using this CSS code, in addition to doing some "Fuzzy" resizing based on the number of letters in the sentence. You can try using a fixed-width font (or style) to display the text so that each character takes up the same amount of space. This way, you can make a best guess, and the CSS will at least give you a nice "..." before cropping the text, if needed.
.truncate
{
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
you can do multiple things like
overflow:hidden
this cuts the extra line
overflow:auto
// this shows the scroll bar
see this
http://www.w3schools.com/Css/pr_pos_overflow.asp
try to do this //check the length of the text
if($('#idfortext').length>sometthing)
{
var fontsize=10;
}
$('#yourid').css('font-size',fontsize)
I worked off a few posts here and made a jQuery plugin out of it. I found that starting from the bottom and going up wasn't very performant, so I make the assumption that the current font-size is close, and worked from there.
$.fn.fitTextToWidth = function(desiredWidth) {
// In this method, we want to start at the current width and grow from there, assuming that we are close to the desired size already.
var $el = $(this);
var resizer = $('<'+$el.get(0).tagName+' />').css({'vsibility':'hidden','position':'absolute','left':'-9999px','display':'inline'}).html($el.html());
resizer.appendTo(document.body);
var size = parseInt($el.css('font-size'));
resizer.css('font-size',size+'px');
var growing = desiredWidth > resizer.width() ? true : false;
var adder = growing ? 1 : -1;
do {
size += adder;
resizer.css('font-size',size+'px');
} while(((growing && resizer.width()<desiredWidth) || (!growing && resizer.width()>desiredWidth)) && size>10 && size<100);
if (growing)
size -= 2; //we never want to go over
resizer.remove();
$el.css('font-size',size+'px');
}
// Example use:
$('#main h1').fitTextToWidth(350);
The only gotcha with this is that it assumes the CSS for the element is global and only applied to the element type. In other words, it creates a tag of the same name in and assumes it will have the same CSS attributes as the element we want to change. If this isn't true for you, just modify the line where resizer
gets created.
Here is my solution: I measure the text in a new div within the provided div, so that the styling should be inherited. Then I insert the text into the div with a span to set the font-size. It will max out to the default font size but will shrink to fit.
It does not loop: it only draws the text once in a throw-away div before drawing directly to the supplied div. See @Hoffmann answer - I just did not like an entire 'plugin' to answer the question.
requires jquery.
var txt = txt || {}
txt.pad_factor = 1.1;
txt.insertText_shrinkToFit = function(div,text)
{
var d = txt.cache || $('<div></div>');
txt.cache = d;
d.css('white-space','nowrap');
d.css('position','absolute');
d.css('top','-10000px');
d.css('font-size','1000px');
var p = $(div);
var max_w = parseInt(p.css('max-width')) || p.width();
var max_h = parseInt(p.css('font-size'));
p.append(d);
d.html(text);
var w = d.width() * txt.pad_factor;
d.remove();
var h = Math.floor(max_w*1000/w);
var s = ( max_h < h ? max_h : h ) + "px";
p.html('<SPAN style="font-size:' + s + '">' + text + '</SPAN>');
}
For example:
<html><head><script src=http://code.jquery.com/jquery-latest.min.js></script>
<script>/*insert code, from above, here.*/</script></head><body>
<div id=mysmalldiv style="max-width:300px;font-size:50px"></div>
<script>
txt.insertText_shrinkToFit("#mysmalldiv","My super long text for this div.");
</script>
</body></html>
For testing: this is how I found that I needed the 1.1 pad_factor; which you may tune to your needs.
<div id=mytestdiv style="max-width:300px;font-size:50px"></div>
<script>
var t = "my text ";
var i = 0;
var f = function() {
t += i + " ";
txt.insertText_shrinkToFit("#mytestdiv",t);
i++;
if(i < 100)
setTimeout(f,1000);
}
f();
</script>
Note: this is assuming px font-size. I have not tested with em or pt.
There is a great way to do this for single line texts, like headers and such, in pure CSS:
h1{
font-size: 4.5vw;
margin: 0 0 0 0;
line-height: 1em;
height: 1em;
}
h1:after{
content:"";
display: inline-block;
width: 100%;
}
Make sure your font-size stays small enough to keep the header 1 line. Using 'vw' (viewport width) instead of 'px' makes it very easy to do this and scale at the same time. viewport width has the tendency to get nasty when you make your browser window very big, many sites use this to keep the site under a certain width on really wide screens:
body{
max-width: 820px;
margin: 0 auto;
}
To make sure your font is not based on how big the viewport window is once the rest of your site stops scaling use this:
@media(min-width:820px){
h1{
font-size: 30px;
}
}
This is what I found today:
- Set a really big fake font-size (f) to the target text
measure width (w) height (h) of the text container and divide by font-size (f):
iw = w/f
ih = h/f
Divide main container width (W) by iw and main container height (H) by ih:
kw = W/iw
kh = H/ih
Pick lowest between kw and kh: that is the wanted FontSize.
https://jsfiddle.net/pinkynrg/44ua56dv/
function maximizeFontSize($target) {
var testingFont = 1000;
$target.find(".text-wrapper").css("font-size", testingFont+"px");
var textWidth = $(".text-wrapper").width()/1000;
var textHeight = $(".text-wrapper").height()/1000;
var width = $(".container").width();
var height = $(".container").height();
var kWidth = width/textWidth;
var kHeight = height/textHeight;
var fontSize = kWidth < kHeight ? kWidth : kHeight;
// finally
$(".text-wrapper")
.css("font-size", fontSize+"px")
.css("line-height", height+"px");
}
I'm using vanilla js. A solution that worked for me for changes in horizontal sizing is when I create the div, I set a default font size % for a given width. In my case, I'm wrapping the div in a bigger class, but for these purposes, you could use:
document.getElementById("mydiv").fontScale = {scaleVal: scaleFactor, defaultWidth: defaultWidth}
the scaleFactor is the size of the font (in percentage) that you want for a given width (the default width). Then when I get a resize event:
let myDiv = document.getElementById("mydiv");
let width = myDiv.offsetWidth;
myDiv.style[fontSize] = String(width/myDiv.fontScale.defaultWidth * myDiv.fontScale.scaleFactor * 100) + "%";
Simple solution
<div class="fitt-text"><span>text</span></div>
js
$(".fitt-text").css('font-size', '1vw');
var cw = $(".fitt-text").width();
var w = $(".fitt-text span").width();
var f = cw/w;
$(".fitt-text").css('font-size', f + 'vw');
精彩评论