Reputation: 785
I need to display user entered text into a fixed size div. What I want is for the font size to be automatically adjusted so that the text fills the box as much as possible.
I'd probably want to start with a maximum font size and while the text is too big to fit the container, shrink the font size until it fits and the font must be displayed as a single line.
Upvotes: 60
Views: 110449
Reputation: 6802
There is no need to iterate over font sizes. You just need to calculate the current size of the text and the size of the container and apply the scaling.
const text_el = document.querySelector("#text");
const container_el = document.querySelector("#container");
text_el.addEventListener("input", function(e) {
container_el.children[0].textContent = this.value;
resize2fit(container_el)
});
function resize2fit(el) {
if (!el.parentElement) return;
el.style.setProperty("--font-size", "1em");
const {width: max_width, height: max_height} = el.getBoundingClientRect();
const {width, height} = el.children[0].getBoundingClientRect();
el.style.setProperty("--font-size", Math.min(max_width/width, max_height/height)+"em");
}
window.addEventListener("resize", function(e) {
resize2fit(container_el);
});
resize2fit(container_el);
#container {
width: 100%;
height: 100px;
font-size: var(--font-size, 1em);
white-space: nowrap;
border: 1px solid blue;
}
#container > * {
display: inline-block;
}
<input id="text" value="lorem ipsum" />
<div id="container">
<span>lorem ipsum</span>
</div>
Upvotes: 9
Reputation: 170
Although this is an old question and already has several answers (mostly based on changing the font size until the content fits), I recently had to do something similar with one of our projects.
Initially I used an edited version of Joe's answer (https://stackoverflow.com/a/33492161/587526), but then I began to think about it and realised that the same effect can be obtained much more easily by using a simple "transform: scale(x)" operation. An added advantage is that the content is scaled to exactly fit in the container, whereas adjusting font-size may result in suboptimal text sizes.
Calculating the value of the scale factor is ridiculously simple, just divide the parent container width by the child container width. If the child content is larger than the parent, the resulting value will be < 1.0 - and passing this to "transform: scale()" results in a perfectly redimensioned child element.
Here is the function I use. It uses jQuery, but could also be easily updated to use the available DOM functions directly.
The code is mostly self-explanatory, but I will point out that the child's "display" is initially changed to "inline-block" because elements such as <p>,<span> etc. do not otherwise report their widths.
Something else to be aware of, using "transform: scale()" may affect margins. In my case I needed to ensure that the child elements were centered inside the parent container so I made use of display: flex:
.parent-container {
display: flex;
flex-direction: column;
align-items: center;
}
Hope someone finds this useful!
function resizeToFit(parent, child) {
let $parent = jQuery(parent);
let $child = jQuery(child);
// Temporarily redefine child's display so we
// can obtain the real width
let $display = $child.css('display');
$child.css('display', 'inline-block');
// Remove any existing inline transform
$child.css('transform', '');
// Calculate the scale factor
let factor = $parent.width() / $child.width();
if (factor < 1.0) {
let scale = 'scale(' + factor + ')';
$child.css({
'-webkit-transform' : scale,
'-moz-transform' : scale,
'-ms-transform' : scale,
'-o-transform' : scale,
'transform' : scale
});
}
// Restore the original display value
$child.css('display', $display);
};
Upvotes: 4
Reputation: 41
You can try this one, it's working 100% for me.
function resize(){
const isOverflown = ({ clientWidth, scrollWidth, clientHeight, scrollHeight}) => scrollWidth > clientWidth || scrollHeight > clientHeight
const resizeText = ({ element, elements, minSize = 10, maxSize = 512, step = 1, unit = 'px' }) => {
(elements || [element]).forEach(el => {
let i = minSize
let overflow = false
const parent = el.parentNode
while (!overflow && i < maxSize) {
el.style.fontSize = `${i}${unit}`
overflow = isOverflown(parent)
el.style.lineHeight = ((el.style.fontSize).slice(0, -2))*1.2+"px"
if (!overflow) i += step
}
el.style.fontSize = (`${i - step - 1}${unit}`)
})
}
resizeText({
elements: document.querySelectorAll('.text'),
step: 0.5
})
}
if (document.readyState === 'complete') {
resize();
} else {
window.onload = resize();
}
<div id="outer" style="width:200px; height:80px; border:1px solid red;">
<div class="text">Lorem ipsum</div>
</div>
<div id="outer" style="width:200px; height:80px; border:1px solid red;">
<div class="text">Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Integer mollis dui felis, vel vehicula tortor cursus nec.</div>
</div>
Upvotes: 0
Reputation: 1994
Based on the logic of the top answer here, I created a no-jQuery version,
const input = document.querySelector('input');
const output = document.querySelector('.output');
const outputContainer = document.querySelector('.container');
function resize_to_fit() {
let fontSize = window.getComputedStyle(output).fontSize;
output.style.fontSize = (parseFloat(fontSize) - 1) + 'px';
if(output.clientHeight >= outputContainer.clientHeight){
resize_to_fit();
}
}
function processInput() {
output.innerHTML =this.value;
output.style.fontSize = '100px'; // Default font size
resize_to_fit();
}
input.addEventListener('input', processInput);
<input type="text" placeholder="Add text input here" style="margin: 10px; width:50%;">
<div id="outer" class="container"
style="width:80%; height:100px; border:2px solid red; font-size:20px;">
<div class="output" style="word-break: break-all; word-wrap: break-word;">
</div>
</div>
Upvotes: 38
Reputation: 6573
Using very little Javascript, you can assign a CSS class with the length of the string, eg: text-length-5
or text-length-20
Then use exclusively CSS to target different font-size
according to those class names.
It probably won't work for every case, but it did very well for mine, where the max length was about 10-12 chars.
If you have 100+ or 1000+ chars you can also make a function that receives the length and returns a CSS class, then style base on those ranges.
HTML:
<div class="box">ABC</div>
<div class="box">ABCDE</div>
Javascript (jQuery, but the same idea can be applied to React, or vanilla JS)
$(".box").each((i, el)=> {
const length = $(el).text().length;
$(el).addClass("text-length-"+length);
})
CSS:
.box {
font-size: 16px;
}
.box.text-length-4,
.box.text-length-5,
.box.text-length-6 {
font-size: 12px;
}
Upvotes: 1
Reputation: 15213
Say you have this:
<div id="outer" style="width:200px; height:20px; border:1px solid red;">
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer mollis dui felis, vel vehicula tortor cursus nec</div>
</div>
Then you can do something like:
$(document).ready(function () {
resize_to_fit();
});
function resize_to_fit(){
var fontsize = $('div#outer div').css('font-size');
$('div#outer div').css('fontSize', parseFloat(fontsize) - 1);
if($('div#outer div').height() >= $('div#outer').height()){
resize_to_fit();
}
}
Working Sample
$(document).ready(function() {
resize_to_fit();
});
function resize_to_fit() {
var fontsize = $('div#outer div').css('font-size');
$('div#outer div').css('fontSize', parseFloat(fontsize) - 1);
if ($('div#outer div').height() >= $('div#outer').height()) {
resize_to_fit();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outer" style="width:200px; height:20px; border:1px solid red;">
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer mollis dui felis, vel vehicula tortor cursus nec</div>
</div>
Upvotes: 35
Reputation: 627
If by text length you mean character count, there is this article which points to a short jquery snippet Change Font Size dynamically based on Character count
$('.text').each(function(){
var el= $(this);
var textLength = el.html().length;
if (textLength > 20) {
el.css('font-size', '0.8em');
}
});
Upvotes: 3
Reputation: 37
Thanks putvande for letting me know the general method. Here is an improved version.
Same HTML:
<div id="outer" style="width:200px; height:20px; border:1px solid red;">
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer mollis dui felis, vel vehicula tortor cursus nec</div>
</div>
Here is the function:
resize_to_fit($('#outer'), $('#outer div'));
function resize_to_fit(outer, inner) {
while(inner.height() > outer.height()) {
var fontsize = parseInt(inner.css('font-size')) - 1;
inner.css('font-size', fontsize);
// some browsers(chrome) the min font return value is 12px
if(fontsize <= 1 || parseInt(inner.css('font-size')) >= fontsize+1)
break;
}
}
Upvotes: 1
Reputation: 931
Whoever wants a dynamic function that depends on the width of the element (based on putvande answer):
$(document).ready(function () {
var arrEl = ['div1', 'div2', 'div3'];
resize_to_fit(arrEl);
});
function resize_to_fit(arr){
for (var el in arr) {
var jEl = $('.' + arr[el] + ' span');
var fontsize = jEl.css('font-size');
jEl.css('fontSize', parseFloat(fontsize) - 1);
if (jEl.width() >= $('.' + arr[el]).width()){
resize_to_fit([arr[el]]);
}
}
}
and the HTML:
<div class="div1">
<span>Lorem Ipsum</span>
</div>
<br/>
<div class="div2">
<span>Lorem Ipsum 2</span>
</div>
<br />
<div class="div3">
<span>Lorem Ipsum 3</span>
</div>
whith CSS:
.div1, .div2, .div3 {
width: 207px;
white-space: nowrap;
}
Notice you only set font-size to span inside, and not to the outer div.
Upvotes: 0
Reputation: 10658
Getting the size in pixels of a string of text is a hard thing to do and depends a lot on the browser and the user's settings, so it will probably be hard to come up with a solution that works in all cases.
By "display user entered text" do you mean the user is typing into a textbox? If so, you can attach a keypress listener that checks the length of the value of the text, then adjusts the font-size
accordingly. Here's an example, though you will probably need to change the lengths and font-sizes to meet your needs:
$('#text').on('keypress', function(e) {
var that = $(this),
textLength = that.val().length
;
if(textLength > 30) {
that.css('font-size', '5px');
} else if(textLength > 20) {
that.css('font-size', '10px');
} else if(textLength > 10) {
that.css('font-size', '15px');
}
});
Upvotes: 2
Reputation: 2874
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#div1{
width: 200px;
height: 200px;
font-size: 32px;
line-height: 1.2em;
}
</style>
<script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
<script>
$(function(){
n = 1;
while(n==1){
n = 0
if ($('#div1 .holder').outerHeight()>$('#div1').outerHeight()){
var fz = parseInt($('#div1').css('font-size'));
$('#div1').css({'font-size' : fz-1});
n = 1
} else {n = 0}
}
});
</script>
</head>
<body>
<div id="div1">
<div class="holder">I need to display user entered text into a fixed size div. What i want is for the font size to be automatically adjusted so that the text fills the box as much as possible.
I'd probably want to start with a maximum font size and while the text is too big to fit the container, shrink the font size until it fits and the font must be displayed as a single line. Is it possible to do this using only css and html.</div>
</div>
</body>
</html>
Upvotes: 1