Resizing a font with pure JavaScript based on string length - javascript

I know this question is asked multiple times, yet mine is different. I noticed that with the pure JavaScript solution, there is a need to resize the screen, while mine has absolutely nothing to do with resizing a screen.
What I have is a container div with some text in it. All texts have a certain font-size, but I want to change the font-size whenever the text gets a certain length.
I have seen this solution on SO:
Resize font depending on string length
Yet, this absolutely looks horrible in pure JavaScript and it's a post of three years ago. There surely must be a better (shorter, better) solution for this. I have read about the CSS solutions, but as I said: I am not using a certain viewport and I don't want to. I just want to change the font-size when it's too long.
I have made a JSFiddle to illustrate the problem:
https://jsfiddle.net/tpx71aqL/
<div class="test">
Blablabla
</div>
<div class="test">
Blablabla12124e121211asdasasas
</div>
PS: I can't use jQuery and don't want to use ellipsis.

An idea for this solution is actually really simple. Check out my codepen here.
Using a simple while loop which checks clientWidth against scrollWidth which you can learn more about here, we use the javascript .style.fontSize = "smaller" which decreases the font size by 1 unit. This works well when we don't know what unit is assigned in the CSS. You can read more about it here.
Hope this helps.
document.onreadystatechange = () => {
if (document.readyState === 'complete') {
var container = document.getElementById("test");
while (container.scrollWidth > container.clientWidth) {
container.style.fontSize = "smaller";
}
}
};
.test {
width: 200px;
border: 1px solid black;
font-size: 16px;
}
<div class="test">
Blablabla
</div>
<div class="test" id="test">
Blablabla12124e121211asdasasas
</div>

This fiddle shows what I suggested in my comment. You extract the current width of the text wrapper and reduce font size until you have a wrapper the same size or slightly smaller than the parent.
var fit = document.getElementById("fit"),
wrap = document.getElementById("wrap"),
step = 0.5,
currentSize;
while (fit.offsetWidth < wrap.offsetWidth) {
currentSize = parseFloat(window.getComputedStyle(wrap, null).getPropertyValue('font-size'));
wrap.style.fontSize = (currentSize - step) + "px";
}
Note the getComputedStyle to really get the calculated size.
You could improve this by making the reduction step smarter, instead of just going down a step again and again; for example calculate how far a 1px reduction approximated the wrapper width to the parent width and adjust step size accordingly.
Also this does assume that the text indeed needs scaling down, no scaling up - the same idea applies.

I needed something similar and I ended up doing something like this:
var elem = document.getElementById("test");
elem.style.fontSize = 30 - elem.innerHTML.length / 6 + 'px';
The idea is to set the max size and reduce it based on the length of the string.
Not fancy or sophisticated but worked for me.

Related

Auto-resize elements based on container size without using scale

Example:
Just having a problem with the computation to auto adjust width of element-image and font size of the element-text based on the container's width. It's like,
$('.element-image').width() = $('.container').width() - $('.element-image').width();
$('.element-text').css('font-size') = $('.container').width() - $('.element-text').css('font-size');
<div class="container">
<img class="element-image" src="sample.jpg" />
<span class="element-text">Sample Text</span>
</div>
something like that but I can't get the logic. Can someone help? Note that this is for my web app, it's like an ad or graphic builder.
Here's the sample app:
https://drive.google.com/file/d/1dnsRg6TjLqUx6IzgLwOYq5uNJrrx7dUv/view?usp=drivesdk
Scaling images is trivial, just use percentage widths.
Scaling text is harder. You can use viewport units to scale the text based on the window size:
.element-text {
font-size: 10vw
}
<span class="element-text">Sample Text</span>
<div>Unscaled text (put this snippet in "fullscreen" mode and change the window size to see the effect)</div>
...but if you need text to scale to exact container sizes, you'll need to resort to javascript. The following is a (kind of dumb) method for setting the font-size to match the container size -- you wouldn't use short-interval polling in real life, and would probably do something smarter than simply incrementing / decrementing the font-size at each step, but this should suffice for demonstrating the technique:
window.setInterval(function() { // In real life you'd likely use the window resize event
var ewidth = $('.element-text').width();
var cwidth = $('.container').width();
var fontsize = parseInt($('.element-text').css("font-size"));
// change font size if the text block doesn't match the container width
if (ewidth > cwidth - 20) { // leave a little extra space to prevent jitter
$('.element-text').css("font-size", --fontsize);
} else if (ewidth < cwidth ) {
$('.element-text').css("font-size", ++fontsize);
}
}, 1);
.container {
resize: both;
overflow: hidden;
border: 1px solid;
width: 20px;
}
.element-image {
width: 100%
}
.element-text {
white-space: nowrap
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Resize me:
<div class="container">
<span class="element-text">Sample Text</span>
<img class="element-image" src="http://placehold.it/200x200" />
</div>
This jQuery plugin uses much the same technique. In its source code is the following comment block:
// I understand this is not optimized and we should
// consider implementing something akin to
// Daniel Hoffmann's answer here:
//
// http://stackoverflow.com/a/17433451/1094964
//
...which is maybe the most roundabout way I've ever discovered a potential question duplicate. (Clearly I need to pay more attention to the "related questions" list, because there it is right at the top of it...)
You dont have to do it with jquery. You can use CSS width:100%; or any percentage for that and it will generate a width accorind to its parent width.
Check this DEMO to understand.
I didnt really got what do you need but im fixing your jquery
var contwidth = $('.container').width();
var imgwidth = $('.element-image').width();
$('.element-image').width(contwidth - imgwidth);
$('.element-text').css('font-size',contwidth - imgwidth);
<div class="container">
<img class="element-image" src="sample.jpg" />
<span class="element-text">Sample Text</span>
</div>
btw as you are changing the width of the image, what is the logic of calculating container width - image width? This will be an error because as you trying to get the width of image the other function changes it. never ending cycle.

Javascript/jQuery get true width and position of float affected element?

Is it possible to get the width (using javascript or jQuery) of a float-affected element? When text is being pushed over due to a floating image is it possible to get its position and true width? I have attached an image to explain better.
Code example,
<div>
<img style="...float: left"/>
<h1>A title!</h1>
<p>Text!</p>
<h1>New header added.</h1>
</div>
Picture
I need to find the width starting from the arrow, (the gray box is the image)(the dotted line is the width according to Firefox inspect mode).
I would like to avoid changing all the elements display types if possible.
Thank you!
I'm a little late to the party, but I had a similar problem and came up with a solution which (so far) seems to work in all instances of this issue. I like this solution because as far as I can tell, it works independent of the floating element - all you need is the element whose true width/position you want to get, nothing more. I've done it in pure Javascript for speed purposes, but it can easily be streamlined with jQuery and a separate CSS Stylesheet if you so choose.
//Get the rendered bounding box for the content of any HTMLElement "el"
var getLimits = function(el) {
//Set a universal style for both tester spans; use "!important" to make sure other styles don't mess things up!
var testerStyle = 'width: 0px!important; overflow: hidden!important; color: transparent!important;';
//Create a 'tester' span and place it BEFORE the content
var testerStart = document.createElement('SPAN');
testerStart.innerHTML = '|';
var testerFloat = ' float: left!important;';
testerStart.setAttribute('style', testerStyle + testerFloat);
//Insert testerStart before the first child of our element
if (el.firstChild) {
el.insertBefore(testerStart, el.firstChild);
} else {
el.appendChild(testerStart);
}
//Create a 'tester' span and place it AFTER the content
var testerEnd = document.createElement('SPAN');
testerEnd.innerHTML = '|';
testerFloat = ' float: right!important;';
testerEnd.setAttribute('style', testerStyle + testerFloat);
el.appendChild(testerEnd);
//Measure the testers
var limits = {
top: testerStart.offsetTop,
bottom: testerEnd.offsetTop + testerEnd.offsetHeight,
left: testerStart.offsetLeft,
right: testerEnd.offsetLeft
}
//Remove the testers and return
el.removeChild(testerStart);
el.removeChild(testerEnd);
return limits;
};
So, in your case, the code would just be:
var paragraphBoundingBox = getLimits($('div>p').get(0));
A couple things to note:
1) The float direction would be reversed if you are using an RTL language
2) All of the four edge positions in the output object are relative to the el.offsetParent - use this handy function can find their positions relative to the document.
First of all, the "full width" is exactly the true width.
You can watch this picture, it can help you understand why the true width and true position of the affected element is the way firefox tells you.
http://i.stack.imgur.com/mB5Ds.png
To get the width of inline text where it's pushed right by the float image, there's no good way except using the full width minus the float image's width.
var w = $('p').width()
- $('img').width()
- $('img').css('margin-left').replace("px", "")
- $('img').css('margin-right').replace("px", "")
- $('img').css('padding-left').replace("px", "")
- $('img').css('padding-right').replace("px", "")
- $('img').css('border-left-width').replace("px", "")
- $('img').css('border-right-width').replace("px", "");

Find out the effective width of element and resize text in it

I'd like to find out the width of a TD which has defined style='width: 200px;, but the text makes it longer (no break). I want to find out the effective width and, as long it is bigger than 200px, shrink the text size in it for about 2px each time.
Is this possible at all?
This can be done in javascript but it's a little complex.
Here's the complete working example : http://jsfiddle.net/dystroy/kdrrA/
The HTML :
<table><tr><td id=txtcell nowrap><span id=txt>My long text is long. My long text.</span></td></tr></table>
<br>Font Size : <span id=mes></span>​
The CSS :
#txtcell{ max-width:200px;}
The javascript :
var fontSize = 20;
var reduce = function() {
while ($('#txt').width() > 200) {
fontSize -= 1;
$('#txt').css('font-size', fontSize);
}
$('#mes').html(fontSize); // this line is only for demonstration, remove it in "production"
};
reduce();
Note that I had to add a span in the cell because TD width computation by jquery doesn't work very well.
You need JavaScript to make it. But if you wish to make it using CSS alone, you can use CSS expressions with expression.
td {width: expression(this.style.width);}
Hope it helps! :)

Resizing text to fit inside a static div

Basicly I want a text to resize depending how long the text is. If I wrote "WWWWWWWW" it would be resized to fit inside the 100px div.
If i wrote "Test" it wouldn't be resized because it wont break the 100px limit.
I'm sure you get the idea by now.
I'm open for anything. Javascript, php, whatever you have to offer.
I think javascript is your friend here, as it allows you to best fit the text regardless of the user's browser window size (if you do it in PHP and oversize/the layout will break for certain users).
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 here.
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.
Demo for a similar situation as yours
Further documentation, examples and source code are on the project page.
if you use a font that has a fixed width(i.e. all letters are equal width) then you can do this using php or javascript.
count the number of letters in php using strlen. then hard code the font size e.g. this example is showing how it can be done and is not the best way to do. Let me know if you have any questions.
code didnt work properly
but
if(strlen($text)>5){
$fontsize = 11px;
}
etc...
Do something like this in PHP:
$length = strlen($string);
if($length < 5)
{
$added_class = 'short';
}
elseif($length < 10)
{
$added_class = 'medium';
}
else
{
$added_class = 'long';
}
echo '<div class="normal_class '.$added_class.'">'.$string.'</div>';
And in your CSS file:
div.short { font-size: 15px }
div.medium { font-size: 13px }
div.long { font-size: 10px }

Why is this while JavaScript loop infinite?

I hate doing this. This is THE small piece to end a large project and my mind is fried...
Here's the code. It checks to see if an element is overflowing and resizes the font. It is supposed to resize it until it doesn't overflow. The condition for the loop seems to be ignored and the browser freezes... I feel that I'm missing something crucial in how jQuery works here.
$.fn.fontBefitting = function() {
var _elm = $(this)[0];
var _hasScrollBar = false;
while ((_elm.clientHeight < _elm.scrollHeight) || (_elm.clientWidth < _elm.scrollWidth)) {
var fontSize = $(this).css('fontSize');
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))*0.95;
$(this).css('fontSize',fontSize+'px');
}
}
Thanks in advance.
Change:
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))*0.95;
to:
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))-1;
Here's a Working Demo. When the font size reached 10px, 10*.95 was 9.5 which the browser was rounding up to 10px. Thus infinite loop.
You need to step through your code in a debugger and actually check your condition values to make sure they are changing how you expect. My guess is _elm.clientHieght and _elm.clientWidth aren't actually changing.
var fontSize = $(this).css('fontSize');
fontSize = parseInt(fontSize, ...
The unit you get from font-size is not necessarily (a) pixels, nor (b) the same unit as you put in.
It's not specified what unit is used to return the length, but in many browsers it is currently points. Since points are smaller than pixels, the integer length will be longer, so you can quite easily keep on *0.95ing it forever.
Even if it were pixels, the browser could round the size up to the nearest pixel, making 95%-size the same size as 100% when you read it back. Or you could hit the minimum-font-size setting and you wouldn't be able to reduce it any more.
So instead of reading the current font size back on each step, keep the pixel size you want in a variable and reduce that variable each time. Then if you reach a predetermined lower bound for the value of that variable, give up.
You are probably running into an endless loop because the font size doesn't actually change. E.g. if the font size found is 10px you will update it to become 9.5px which is probably rounded back to 10px by the browser. In that case nothing changes and the function will keep running forever.
You've got an unrelated problem when you do
$('div').fontBefitting()
This will make the text in the first div fit it's box, then make the font size of all the other divs the same as the first. This does not sound like intended behaviour. You would hope that it would make each div resize its text and only its text to fit.
You need to change your code to this:
$.fn.fontBefitting = function() {
/* $.fn.* runs on a jQuery object. Make sure to return it for chaining */
return this.each(function() {
var fontSize = parseInt($(this).css('fontSize'));
while (this.clientHeight < this.scrollHeight ||
this.clientWidth < this.scrollWidth) {
fontSize--;
$(this).css('fontSize', fontSize + 'px');
}
});
}
You're checking to see if the clientHeight or clientWidth are LESS than the scrollHeight or scrollWidth, and if they are you are REDUCING the font size? It will never converge under those circumstances. You want to INCREASE the font size.

Categories

Resources