how to add inline style width:calc(100% / var) in reactjs? - html

for (let i = 0; i < divCount.length; i++) {
var radio = divCount[i].getElementsByClassName("radio_count");
for (var index = 0; index < radio.length; index++) {
var element: any = radio[index];
console.log(element);
var style ="width:calc(100% / " + element + ")";
element.style = style;
}
}
i want add inline style
set dynamic value in 100% divided
ex. width:clac(100% / var);
how can i do this ?

Template literals should help.
<div style={{width: `calc(100% / ${yourVariable})`}}></div>
Example https://jsfiddle.net/69z2wepo/81579/

What #Andrew wrote is correct, however here's a non ES2015/ES6 version (the question did not specify):
In the react component render, you can use the following JSX:
render: function() {
var dynamicWidth = 'calc(100% / ' + value + ')';
return (
<div>
<div style={{width: dynamicWidth}}></div>
</div>
);
Basically how this works is on each render that string gets interpolated.
What #Andrew has shown you is just a better syntax available to do the same thing in ES6
calc(100% / ${value}) is conceptually equivalent to 'calc(100% / ' + value + ')'

1.Inline styles can be added to a component in ReactJS as shown below:
render() {
return (
<div style={{ color: 'white', backgroundColor: 'red' }}>
Div to be styled
</div>
)
}
Or by passing a style object to the component:
render() {
const divStyle = {
color: 'white',
backgroundColor: 'red'
};
return (
<div style={divStyle}>
Div to be styled
</div>
)
}
In order to set a dynamic width based on the number of child elements as you have described, I would recommend using CSS. Use display: flex on the wrapping element and flex-grow: 1 on the inner element.
Example: https://jsfiddle.net/edyjt34r/

Without using Calc function you can this desired result
<div style = width: `${100 / ${yourVariable}}%`></div>

for (let i = 0; i < divCount.length; i++) {
var radio = divCount[i].getElementsByClassName("radio_count");
for (var index = 0; index < radio.length; index++) {
var element: any = radio[index];
console.log(element);
let width = 100 / radio.length;
element.style.width=width + "%";
}
}

Related

Dynamically change font-size within parent element with no overflow [duplicate]

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.
So - If the div is 400px x 300px. If someone enters ABC then it's really big font. If they enter a paragraph, then it would be a tiny font.
I'd probably want to start with a maximum font size - maybe 32px, and while the text is too big to fit the container, shrink the font size until it fits.
This is what I ended up with:
Here is a link to the plugin: https://plugins.jquery.com/textfill/
And a link to the source: http://jquery-textfill.github.io/
;(function($) {
$.fn.textfill = function(options) {
var fontSize = options.maxFontPixels;
var ourText = $('span:visible:first', this);
var maxHeight = $(this).height();
var maxWidth = $(this).width();
var textHeight;
var textWidth;
do {
ourText.css('font-size', fontSize);
textHeight = ourText.height();
textWidth = ourText.width();
fontSize = fontSize - 1;
} while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > 3);
return this;
}
})(jQuery);
$(document).ready(function() {
$('.jtextfill').textfill({ maxFontPixels: 36 });
});
and my HTML is like this
<div class='jtextfill' style='width:100px;height:50px;'>
<span>My Text Here</span>
</div>
I didn't find any of the previous solutions to be adequate enough due to bad performance, so I made my own that uses simple math instead of looping. Should work fine in all browsers as well.
According to this performance test case it is much faster then the other solutions found here.
(function($) {
$.fn.textfill = function(maxFontSize) {
maxFontSize = parseInt(maxFontSize, 10);
return this.each(function(){
var ourText = $("span", this),
parent = ourText.parent(),
maxHeight = parent.height(),
maxWidth = parent.width(),
fontSize = parseInt(ourText.css("fontSize"), 10),
multiplier = maxWidth/ourText.width(),
newSize = (fontSize*(multiplier-0.1));
ourText.css(
"fontSize",
(maxFontSize > 0 && newSize > maxFontSize) ?
maxFontSize :
newSize
);
});
};
})(jQuery);
If you want to contribute I've added this to Gist.
As much as I love the occasional upvotes I get for this answer (thanks!), this is really not the greatest approach to this problem. Please check out some of the other wonderful answers here, especially the ones that have found solutions without looping.
Still, for the sake of reference, here's my original answer:
<html>
<head>
<style type="text/css">
#dynamicDiv
{
background: #CCCCCC;
width: 300px;
height: 100px;
font-size: 64px;
overflow: hidden;
}
</style>
<script type="text/javascript">
function shrink()
{
var textSpan = document.getElementById("dynamicSpan");
var textDiv = document.getElementById("dynamicDiv");
textSpan.style.fontSize = 64;
while(textSpan.offsetHeight > textDiv.offsetHeight)
{
textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
}
}
</script>
</head>
<body onload="shrink()">
<div id="dynamicDiv"><span id="dynamicSpan">DYNAMIC FONT</span></div>
</body>
</html>
And here's a version with classes:
<html>
<head>
<style type="text/css">
.dynamicDiv
{
background: #CCCCCC;
width: 300px;
height: 100px;
font-size: 64px;
overflow: hidden;
}
</style>
<script type="text/javascript">
function shrink()
{
var textDivs = document.getElementsByClassName("dynamicDiv");
var textDivsLength = textDivs.length;
// Loop through all of the dynamic divs on the page
for(var i=0; i<textDivsLength; i++) {
var textDiv = textDivs[i];
// Loop through all of the dynamic spans within the div
var textSpan = textDiv.getElementsByClassName("dynamicSpan")[0];
// Use the same looping logic as before
textSpan.style.fontSize = 64;
while(textSpan.offsetHeight > textDiv.offsetHeight)
{
textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
}
}
}
</script>
</head>
<body onload="shrink()">
<div class="dynamicDiv"><span class="dynamicSpan">DYNAMIC FONT</span></div>
<div class="dynamicDiv"><span class="dynamicSpan">ANOTHER DYNAMIC FONT</span></div>
<div class="dynamicDiv"><span class="dynamicSpan">AND YET ANOTHER DYNAMIC FONT</span></div>
</body>
</html>
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.
I will work on getting an option to set the maximum font-size.
Edit: I have found some more problems with the plugin, it does not handle other box-model besides the standard one and the div can't have margins or borders. I will work on it.
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. I will work into accepting a max-width and max-height values in the wrapper element.
Edit3: I have updated the plugin to version 1.2.0. Major cleanup on the code and new options (verticalAlign, horizontalAlign, textAlign) and support for inner elements inside the span tag (like line-breaks or font-awesome icons.)
This is based on what GeekyMonkey posted above, with some modifications.
; (function($) {
/**
* Resize inner element to fit the outer element
* #author Some modifications by Sandstrom
* #author Code based on earlier works by Russ Painter (WebDesign#GeekyMonkey.com)
* #version 0.2
*/
$.fn.textfill = function(options) {
options = jQuery.extend({
maxFontSize: null,
minFontSize: 8,
step: 1
}, options);
return this.each(function() {
var innerElements = $(this).children(':visible'),
fontSize = options.maxFontSize || innerElements.css("font-size"), // use current font-size by default
maxHeight = $(this).height(),
maxWidth = $(this).width(),
innerHeight,
innerWidth;
do {
innerElements.css('font-size', fontSize);
// use the combined height of all children, eg. multiple <p> elements.
innerHeight = $.map(innerElements, function(e) {
return $(e).outerHeight();
}).reduce(function(p, c) {
return p + c;
}, 0);
innerWidth = innerElements.outerWidth(); // assumes that all inner elements have the same width
fontSize = fontSize - options.step;
} while ((innerHeight > maxHeight || innerWidth > maxWidth) && fontSize > options.minFontSize);
});
};
})(jQuery);
Here's an improved looping method that uses binary search to find the largest possible size that fits into the parent in the fewest steps possible (this is faster and more accurate than stepping by a fixed font size). The code is also optimized in several ways for performance.
By default, 10 binary search steps will be performed, which will get within 0.1% of the optimal size. You could instead set numIter to some value N to get within 1/2^N of the optimal size.
Call it with a CSS selector, e.g.: fitToParent('.title-span');
/**
* Fit all elements matching a given CSS selector to their parent elements'
* width and height, by adjusting the font-size attribute to be as large as
* possible. Uses binary search.
*/
var fitToParent = function(selector) {
var numIter = 10; // Number of binary search iterations
var regexp = /\d+(\.\d+)?/;
var fontSize = function(elem) {
var match = elem.css('font-size').match(regexp);
var size = match == null ? 16 : parseFloat(match[0]);
return isNaN(size) ? 16 : size;
}
$(selector).each(function() {
var elem = $(this);
var parentWidth = elem.parent().width();
var parentHeight = elem.parent().height();
if (elem.width() > parentWidth || elem.height() > parentHeight) {
var maxSize = fontSize(elem), minSize = 0.1;
for (var i = 0; i < numIter; i++) {
var currSize = (minSize + maxSize) / 2;
elem.css('font-size', currSize);
if (elem.width() > parentWidth || elem.height() > parentHeight) {
maxSize = currSize;
} else {
minSize = currSize;
}
}
elem.css('font-size', minSize);
}
});
};
I've created a directive for AngularJS - heavely inspired by GeekyMonkey's answer but without the jQuery dependency.
Demo: http://plnkr.co/edit/8tPCZIjvO3VSApSeTtYr?p=preview
Markup
<div class="fittext" max-font-size="50" text="Your text goes here..."></div>
Directive
app.directive('fittext', function() {
return {
scope: {
minFontSize: '#',
maxFontSize: '#',
text: '='
},
restrict: 'C',
transclude: true,
template: '<div ng-transclude class="textContainer" ng-bind="text"></div>',
controller: function($scope, $element, $attrs) {
var fontSize = $scope.maxFontSize || 50;
var minFontSize = $scope.minFontSize || 8;
// text container
var textContainer = $element[0].querySelector('.textContainer');
angular.element(textContainer).css('word-wrap', 'break-word');
// max dimensions for text container
var maxHeight = $element[0].offsetHeight;
var maxWidth = $element[0].offsetWidth;
var textContainerHeight;
var textContainerWidth;
var resizeText = function(){
do {
// set new font size and determine resulting dimensions
textContainer.style.fontSize = fontSize + 'px';
textContainerHeight = textContainer.offsetHeight;
textContainerWidth = textContainer.offsetWidth;
// shrink font size
var ratioHeight = Math.floor(textContainerHeight / maxHeight);
var ratioWidth = Math.floor(textContainerWidth / maxWidth);
var shrinkFactor = ratioHeight > ratioWidth ? ratioHeight : ratioWidth;
fontSize -= shrinkFactor;
} while ((textContainerHeight > maxHeight || textContainerWidth > maxWidth) && fontSize > minFontSize);
};
// watch for changes to text
$scope.$watch('text', function(newText, oldText){
if(newText === undefined) return;
// text was deleted
if(oldText !== undefined && newText.length < oldText.length){
fontSize = $scope.maxFontSize;
}
resizeText();
});
}
};
});
I forked the script above from Marcus Ekwall: https://gist.github.com/3945316 and tweaked it to my preferences, it now fires when the window is resized, so that the child always fits its container. I've pasted the script below for reference.
(function($) {
$.fn.textfill = function(maxFontSize) {
maxFontSize = parseInt(maxFontSize, 10);
return this.each(function(){
var ourText = $("span", this);
function resizefont(){
var parent = ourText.parent(),
maxHeight = parent.height(),
maxWidth = parent.width(),
fontSize = parseInt(ourText.css("fontSize"), 10),
multiplier = maxWidth/ourText.width(),
newSize = (fontSize*(multiplier));
ourText.css("fontSize", maxFontSize > 0 && newSize > maxFontSize ? maxFontSize : newSize );
}
$(window).resize(function(){
resizefont();
});
resizefont();
});
};
})(jQuery);
Here's my modification of the OP's answer.
In short, many people who tried to optimize this complained that a loop was being used. Yes, while loops can be slow, other approaches can be inaccurate.
Therefore, my approach uses Binary Search to find the best Font Size:
$.fn.textfill = function()
{
var self = $(this);
var parent = self.parent();
var attr = self.attr('max-font-size');
var maxFontSize = parseInt(attr, 10);
var unit = attr.replace(maxFontSize, "");
var minFontSize = parseInt(self.attr('min-font-size').replace(unit, ""));
var fontSize = (maxFontSize + minFontSize) / 2;
var maxHeight = parent.height();
var maxWidth = parent.width();
var textHeight;
var textWidth;
do
{
self.css('font-size', fontSize + unit);
textHeight = self.height();
textWidth = self.width();
if(textHeight > maxHeight || textWidth > maxWidth)
{
maxFontSize = fontSize;
fontSize = Math.floor((fontSize + minFontSize) / 2);
}
else if(textHeight < maxHeight || textWidth < maxWidth)
{
minFontSize = fontSize;
fontSize = Math.floor((fontSize + maxFontSize) / 2);
}
else
break;
}
while(maxFontSize - minFontSize > 1 && maxFontSize > minFontSize);
self.css('font-size', fontSize + unit);
return this;
}
function resizeText()
{
$(".textfill").textfill();
}
$(document).ready(resizeText);
$(window).resize(resizeText);
This also allows the element to specify the minimum and maximum font:
<div class="container">
<div class="textfill" min-font-size="10px" max-font-size="72px">
Text that will fill the container, to the best of its abilities, and it will <i>never</i> have overflow.
</div>
</div>
Furthermore, this algorithm is unitless. You may specify em, rem, %, etc. and it will use that for its final result.
Here's the Fiddle: https://jsfiddle.net/fkhqhnqe/1/
I had exactly the same problem with my website. I have a page that is displayed on a projector, on walls, big screens..
As I don't know the max size of my font, I re-used the plugin above of #GeekMonkey but incrementing the fontsize :
$.fn.textfill = function(options) {
var defaults = { innerTag: 'span', padding: '10' };
var Opts = jQuery.extend(defaults, options);
return this.each(function() {
var ourText = $(Opts.innerTag + ':visible:first', this);
var fontSize = parseFloat(ourText.css('font-size'),10);
var doNotTrepass = $(this).height()-2*Opts.padding ;
var textHeight;
do {
ourText.css('font-size', fontSize);
textHeight = ourText.height();
fontSize = fontSize + 2;
} while (textHeight < doNotTrepass );
});
};
The proposed iterative solutions can be sped up dramatically on two fronts:
1) Multiply the font size by some constant, rather than adding or subtracting 1.
2) First, zero in using a course constant, say, double the size each loop. Then, with a rough idea of where to start, do the same thing with a finer adjustment, say, multiply by 1.1. While the perfectionist might want the exact integer pixel size of the ideal font, most observers don't notice the difference between 100 and 110 pixels. If you are a perfectionist, then repeat a third time with an even finer adjustment.
Rather than writing a specific routine or plug-in that answers the exact question, I just rely on the basic ideas and write variations of the code to handle all kinds of layout issues, not just text, including fitting divs, spans, images,... by width, height, area,... within a container, matching another element....
Here's an example:
var nWindowH_px = jQuery(window).height();
var nWas = 0;
var nTry = 5;
do{
nWas = nTry;
nTry *= 2;
jQuery('#divTitle').css('font-size' ,nTry +'px');
}while( jQuery('#divTitle').height() < nWindowH_px );
nTry = nWas;
do{
nWas = nTry;
nTry = Math.floor( nTry * 1.1 );
jQuery('#divTitle').css('font-size' ,nTry +'px');
}while( nWas != nTry && jQuery('#divTitle').height() < nWindowH_px );
jQuery('#divTitle').css('font-size' ,nWas +'px');
Here is a version of the accepted answer which can also take a minFontSize parameter.
(function($) {
/**
* Resizes an inner element's font so that the inner element completely fills the outer element.
* #author Russ Painter WebDesign#GeekyMonkey.com
* #author Blake Robertson
* #version 0.2 -- Modified it so a min font parameter can be specified.
*
* #param {Object} Options which are maxFontPixels (default=40), innerTag (default='span')
* #return All outer elements processed
* #example <div class='mybigdiv filltext'><span>My Text To Resize</span></div>
*/
$.fn.textfill = function(options) {
var defaults = {
maxFontPixels: 40,
minFontPixels: 10,
innerTag: 'span'
};
var Opts = jQuery.extend(defaults, options);
return this.each(function() {
var fontSize = Opts.maxFontPixels;
var ourText = $(Opts.innerTag + ':visible:first', this);
var maxHeight = $(this).height();
var maxWidth = $(this).width();
var textHeight;
var textWidth;
do {
ourText.css('font-size', fontSize);
textHeight = ourText.height();
textWidth = ourText.width();
fontSize = fontSize - 1;
} while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > Opts.minFontPixels);
});
};
})(jQuery);
You can use FitText.js (github page) to solve this problem. Is really small and efficient compared to TextFill. TextFill uses an expensive while loop and FitText don't.
Also FitText is more flexible (I use it in a proyect with very special requirements and works like a champ!).
HTML:
<div class="container">
<h1 id="responsive_headline">Your fancy title</h1>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="jquery.fittext.js"></script>
<script>
jQuery("#responsive_headline").fitText();
</script>
You also can set options to it:
<script>
jQuery("#responsive_headline").fitText(1, { minFontSize: '30px', maxFontSize: '90px'});
</script>
CSS:
#responsive_headline {
width: 100%;
display: block;
}
And if you need it, FitText also has a no-jQuery version.
EDIT: This code was used to show notes on top of a HTML5 video. It changes the font-size on the fly when the video is resized (when the browser window is resized.) The notes was connected to the video (just like notes on YouTube), which is why the code uses instances instead of a DOM handle directly.
As per request I'll throw in some code that I used to achieve this. (Text boxes over an HTML5 video.) The code was written a long time ago, and I quite frankly think it's pretty messy. Since the question is already answered and an answer is already accepted a long time ago I don't bother rewriting this. But if anyone wants to simplify this a bit, you're more than welcome!
// Figure out the text size:
var text = val['text'];
var letters = text.length;
var findMultiplier = function(x) { // g(x)
/* By analysing some functions with regression, the resulting function that
gives the best font size with respect to the number of letters and the size
of the note is:
g(x) = 8.3 - 2.75x^0.15 [1 < x < 255]
f(x) = g(letters) * (x / 1000)^0.5
Font size = f(size)
*/
return 8.3 - 2.75 * Math.pow(x, 0.15);
};
var findFontSize = function(x) { // f(x)
return findMultiplier(letters) * Math.pow(x / 1000, 0.5);
};
val.setFontSizeListener = function() {
p.style.fontSize = '1px'; // So the text should not overflow the box when measuring.
var noteStyle = window.getComputedStyle(table);
var width = noteStyle.getPropertyValue('width');
var height = noteStyle.getPropertyValue('height');
var size = width.substring(0, width.length - 2) * height.substring(0, height.length - 2);
p.style.fontSize = findFontSize(size) + 'px';
};
window.addEventListener('resize', val.setFontSizeListener);
You'll probably need to tweak these numbers from font-family to font-family. A good way to do this is download a free graph visualizer called GeoGebra. Change the length of the text and the size of the box. Then you manually set the size. Plot the manual results into the coordinate system. Then you enter the two equations I've posted here and you tweak the numbers until "my" graph fits your own manually plotted points.
Just wanted to add my version for contenteditables.
$.fn.fitInText = function() {
this.each(function() {
let textbox = $(this);
let textboxNode = this;
let mutationCallback = function(mutationsList, observer) {
if (observer) {
observer.disconnect();
}
textbox.css('font-size', 0);
let desiredHeight = textbox.css('height');
for (i = 12; i < 50; i++) {
textbox.css('font-size', i);
if (textbox.css('height') > desiredHeight) {
textbox.css('font-size', i - 1);
break;
}
}
var config = {
attributes: true,
childList: true,
subtree: true,
characterData: true
};
let newobserver = new MutationObserver(mutationCallback);
newobserver.observe(textboxNode, config);
};
mutationCallback();
});
}
$('#inner').fitInText();
#outer {
display: table;
width: 100%;
}
#inner {
border: 1px solid black;
height: 170px;
text-align: center;
display: table-cell;
vertical-align: middle;
word-break: break-all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="outer">
<div id="inner" contenteditable=true>
TEST
</div>
</div>
This uses binary search, doing 10 iterations. The naive way was to do a while loop and increase the font size by 1 until the element started to overflow. You can determine when an element begins to overflow using element.offsetHeight and element.scrollHeight. If scrollHeight is bigger than offsetHeight, you have a font size that is too big.
Binary search is a much better algorithm for this. It also is limited by the number of iterations you want to perform. Simply call flexFont and insert the div id and it will adjust the font size between 8px and 96px.
I have spent some time researching this topic and trying different libraries, but ultimately I think this is the easiest and most straightforward solution that will actually work.
Note if you want you can change to use offsetWidth and scrollWidth, or add both to this function.
// Set the font size using overflow property and div height
function flexFont(divId) {
var content = document.getElementById(divId);
content.style.fontSize = determineMaxFontSize(content, 8, 96, 10, 0) + "px";
};
// Use binary search to determine font size
function determineMaxFontSize(content, min, max, iterations, lastSizeNotTooBig) {
if (iterations === 0) {
return lastSizeNotTooBig;
}
var obj = fontSizeTooBig(content, min, lastSizeNotTooBig);
// if `min` too big {....min.....max.....}
// search between (avg(min, lastSizeTooSmall)), min)
// if `min` too small, search between (avg(min,max), max)
// keep track of iterations, and the last font size that was not too big
if (obj.tooBig) {
(lastSizeTooSmall === -1) ?
determineMaxFontSize(content, min / 2, min, iterations - 1, obj.lastSizeNotTooBig, lastSizeTooSmall) :
determineMaxFontSize(content, (min + lastSizeTooSmall) / 2, min, iterations - 1, obj.lastSizeNotTooBig, lastSizeTooSmall);
} else {
determineMaxFontSize(content, (min + max) / 2, max, iterations - 1, obj.lastSizeNotTooBig, min);
}
}
// determine if fontSize is too big based on scrollHeight and offsetHeight,
// keep track of last value that did not overflow
function fontSizeTooBig(content, fontSize, lastSizeNotTooBig) {
content.style.fontSize = fontSize + "px";
var tooBig = content.scrollHeight > content.offsetHeight;
return {
tooBig: tooBig,
lastSizeNotTooBig: tooBig ? lastSizeNotTooBig : fontSize
};
}
I got the same problem and the solution is basically use javascript to control font-size.
Check this example on codepen:
https://codepen.io/ThePostModernPlatonic/pen/BZKzVR
This is example is only for height, maybe you need to put some if's about the width.
try to resize it
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Documento sem título</title>
<style>
</style>
</head>
<body>
<div style="height:100vh;background-color: tomato;" id="wrap">
<h1 class="quote" id="quotee" style="padding-top: 56px">Because too much "light" doesn't <em>illuminate</em> our paths and warm us, it only blinds and burns us.</h1>
</div>
</body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
var multiplexador = 3;
initial_div_height = document.getElementById ("wrap").scrollHeight;
setInterval(function(){
var div = document.getElementById ("wrap");
var frase = document.getElementById ("quotee");
var message = "WIDTH div " + div.scrollWidth + "px. "+ frase.scrollWidth+"px. frase \n";
message += "HEIGHT div " + initial_div_height + "px. "+ frase.scrollHeight+"px. frase \n";
if (frase.scrollHeight < initial_div_height - 30){
multiplexador += 1;
$("#quotee").css("font-size", multiplexador);
}
console.log(message);
}, 10);
</script>
</html>
I did like
let name = "Making statements based on opinion; back them up with references or personal experience."
let originFontSize = 15;
let maxDisplayCharInLine = 50;
let fontSize = Math.min(originFontSize, originFontSize / (name.length / maxDisplayCharInLine));
I have found a way to prevent the use of loops to shrink the text. It adjusts the font-size by multiplying it for the rate between container's width and content width. So if the container's width is 1/3 of the content, the font-size will be reduced by 1/3 and will container's width. To scale up, I have used a while loop, until content is bigger than container.
function fitText(outputSelector){
// max font size in pixels
const maxFontSize = 50;
// get the DOM output element by its selector
let outputDiv = document.getElementById(outputSelector);
// get element's width
let width = outputDiv.clientWidth;
// get content's width
let contentWidth = outputDiv.scrollWidth;
// get fontSize
let fontSize = parseInt(window.getComputedStyle(outputDiv, null).getPropertyValue('font-size'),10);
// if content's width is bigger than elements width - overflow
if (contentWidth > width){
fontSize = Math.ceil(fontSize * width/contentWidth,10);
fontSize = fontSize > maxFontSize ? fontSize = maxFontSize : fontSize - 1;
outputDiv.style.fontSize = fontSize+'px';
}else{
// content is smaller than width... let's resize in 1 px until it fits
while (contentWidth === width && fontSize < maxFontSize){
fontSize = Math.ceil(fontSize) + 1;
fontSize = fontSize > maxFontSize ? fontSize = maxFontSize : fontSize;
outputDiv.style.fontSize = fontSize+'px';
// update widths
width = outputDiv.clientWidth;
contentWidth = outputDiv.scrollWidth;
if (contentWidth > width){
outputDiv.style.fontSize = fontSize-1+'px';
}
}
}
}
This code is part of a test that I have uploaded to Github https://github.com/ricardobrg/fitText/
I went with geekMonkey solution, but it's too slow. What he does, is he adjusts the font size to maximum (maxFontPixels) and then checks if it fits inside the container. else it reduces the font size by 1px and checks again. Why not simply check the previous container for the height and submit that value? (yes, I know why, but I now made a solution, that only works on the height and also has a min/max option)
Here is a much quicker solution:
var index_letters_resize;
(index_letters_resize = function() {
$(".textfill").each(function() {
var
$this = $(this),
height = Math.min( Math.max( parseInt( $this.height() ), 40 ), 150 );
$this.find(".size-adjust").css({
fontSize: height
});
});
}).call();
$(window).on('resize', function() {
index_letters_resize();
);
and this would be the HTML:
<div class="textfill">
<span class="size-adjust">adjusted element</span>
other variable stuff that defines the container size
</div>
Again: this solution ONLY checks for the height of the container. That's why this function does not has to check, if the element fits inside. But I also implemented a min/max value (40min, 150max) so for me this works perfectly fine (and also works on window resize).
Here is another version of this solution:
shrinkTextInElement : function(el, minFontSizePx) {
if(!minFontSizePx) {
minFontSizePx = 5;
}
while(el.offsetWidth > el.parentNode.offsetWidth || el.offsetHeight > el.parentNode.offsetHeight) {
var newFontSize = (parseInt(el.style.fontSize, 10) - 3);
if(newFontSize <= minFontSizePx) {
break;
}
el.style.fontSize = newFontSize + "px";
}
}

Split HTML into virtual pages

I have long content (multiple content with imgaes, lists, divs, text, etc...). I want to show this content to user as virtual PDF pages. I dont want to generate PDF, just show this HTML content as pages (defined by width/height) with same header/footer. It should looks like in image bellow, as you can see on first page, I want to split that text and show in next page:
I´m working this app on React. I dont know what will be in this content, every render will be different (with different content based on user activity).
Do you have any suggestions how to do this? (CSS solutions, or JS, or I dont know maybe some React lib ...)
Here is a naive but working implementation.
The idea is to mount the html into an offscreen div which has the same dimensions as the pages we're trying to render.
Then iterate over the elements (ie children from parsed html) of this offscreen div and query the dom using getBoundingClientRect to find the first overflowing element.
We then remove all elements before the overflowing one from the offscreen div and cache them in an array.
Start over for a new chunk until there is no more elements in the offscreen div.
Adapting this to React is just a matter of using dangerouslySetInnerHTML with the html content of each page.
(the display flex is just there to force flowing of elements, but any layout would do provided it's the same in offscreenDiv and in page)
function generateRandomContent() {
var alph = "abcdefghijklmnopqrstuvwxyz";
var content = "";
// we will generate 100 random elements displaying their index to keep track of what's happening
for (var i = 0; i < 100; i++) {
var type = parseInt(Math.random() * 2, 10);
switch (type) {
case 0: // text, generates and random p block
content = content + "<p>" + i + " ";
var numWords = 10 + parseInt(Math.random() * 50, 10);
for (var j = 0; j < numWords; j++) {
var numLetters = 2 + parseInt(Math.random() * 15, 10);
if (j > 0) {
content = content + " ";
}
for (var k = 0; k < numLetters; k++) {
content = content + alph[parseInt(Math.random() * 26, 10)];
}
}
content = content + "</p>";
break;
case 1: // colored div, generates a div of random size and color
var width = 30 + parseInt(Math.random() * 20, 10) * 10;
var height = 30 + parseInt(Math.random() * 20, 10) * 10;
var color = "rgb(" + parseInt(Math.random() * 255, 10) + ", " + parseInt(Math.random() * 255, 10) + ", " + parseInt(Math.random() * 255, 10) + ")";
content = content + '<div style="width: ' + width + 'px; height: ' + height + 'px; background-color: ' + color + '">' + i + '</div>';
break;
}
}
return content;
}
function getNodeChunks(htmlDocument) {
var offscreenDiv = document.createElement('div');
offscreenDiv.className = 'page';
offscreenDiv.style.position = 'absolute';
offscreenDiv.style.top = '-3000px';
offscreenDiv.innerHTML = htmlDocument;
offscreenDiv.display = 'flex';
offscreenDiv.flexWrap = 'wrap';
document.body.appendChild(offscreenDiv);
offscreenRect = offscreenDiv.getBoundingClientRect();
// console.log('offscreenRect:', offscreenRect);
var chunks = [];
var currentChunk = []
for (var i = 0; i < offscreenDiv.children.length; i++) {
var current = offscreenDiv.children[i];
var currentRect = current.getBoundingClientRect();
currentChunk.push(current);
if (currentRect.bottom > (offscreenRect.bottom)) {
// current element is overflowing offscreenDiv, remove it from current chunk
currentChunk.pop();
// remove all elements in currentChunk from offscreenDiv
currentChunk.forEach(elem => elem.remove());
// since children were removed from offscreenDiv, adjust i to start back at current eleme on next iteration
i -= currentChunk.length;
// push current completed chunk to the resulting chunklist
chunks.push(currentChunk);
// initialise new current chunk
currentChunk = [current];
offscreenRect = offscreenDiv.getBoundingClientRect();
}
}
// currentChunk may not be empty but we need the last elements
if (currentChunk.length > 0) {
currentChunk.forEach(elem => elem.remove());
chunks.push(currentChunk);
}
// offscreenDiv is not needed anymore
offscreenDiv.remove();
return chunks;
}
function appendChunksToPages(chunks) {
var container = document.getElementsByClassName('root_container')[0];
chunks.forEach((chunk, index) => {
// ex of a page header
var header = document.createElement('div');
header.innerHTML = '<h4 style="margin: 5px">Page ' + (index + 1) + '</h4>';
container.appendChild(header);
var page = document.createElement('div');
page.className = 'page';
chunk.forEach(elem => page.appendChild(elem));
container.appendChild(page);
});
}
// generateRandom content outputs raw html, getNodeChunks returns
// an array of array of elements, the first dimension is the set of
// pages, the second dimension is the set of elements in each page
// finally appendChunks to pages generates a page for each chunk
// and adds this page to the root container
appendChunksToPages(getNodeChunks(generateRandomContent()));
.page {
border: 1px solid;
display: flex;
flex-wrap: wrap;
height: 700px;
width: 50%;
margin-bottom: 20px;
}
<div class="root_container"></div>
As an addition to the other answer by remix23, I was trying to do something along the same lines by formatting an html document before turning into pdf to print out on paper. I found CSS formatting to be my answer https://www.smashingmagazine.com/2015/01/designing-for-print-with-css/
I am not sure if this is the solution you are looking for but using
<style>
#media print {
div { break-inside: avoid; }
}
</style>
was all I needed before I could open the html document in my browser and use its inbuilt ctrl-P to see a "virtual PDF". This method also let me modify margins and specify width/height of the paper.

elements unintentionally move on content change

I'm learning React and JavaScript generally via writing chess game and got to some intersting issue with elements changing position depending on whether content is empty or not.
I create simple CodePen to illustrate problem - https://codepen.io/anon/pen/KbGKrX?editors=0010
Clicking on each square will change it content.
Problem is that this square will change position, unless all squares in the same row have same content (null or text). Tried with empty strings and spaces instead null and got the same behaviour.
Inspecting in Dev Tools I see no change of CSS so I am puzzled and not sure whether its is some strange behaviour because of React or something else.
I would appreciate any help on this one.
JS part:
const BoardSquare = (props) => {
return (
<button
className={`board-square board-square-${props.squareColor}`}
onClick={props.onClick}
>
{props.piece}
</button>
);
};
const squares = new Array(8);
for (let i = 0; i < 8; i++) {
let row = new Array(8);
for (let j = 0; j < 8; j++) {
row[j] = {
rowIndex: i,
colIndex: j,
key: (j + 10).toString(36) + (8 - i).toString(),
color: (i + j) % 2 === 0 ? "light" : "dark",
piece: i === 1 ? j : null
};
}
squares[i] = row;
}
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: squares,
whiteMove: true
};
}
handleClickSquare(i, j) {
//alert("clicked:" + i + " " + j);
const squares = this.state.squares.slice();
squares[i][j].piece = squares[i][j].piece !== null ? null : j;
this.setState({ squares: squares, whiteMove: !this.state.whiteMove });
}
handleClickNewGame() {
const squares = this.state.squares.slice();
for (let i = 0; i < 8; i++) {
squares[1][i].piece = i;
squares[6][i].piece = i;
}
this.setState({ squares: squares, whiteMove: true });
}
render() {
return (
<div id="board">
{this.state.squares.map((row, rowIndex) => {
return (
<div>{
row.map((square, colIndex) => {
return (
<BoardSquare
key={square.key}
squareColor={square.color}
id={square.key}
piece={square.piece}
onClick={() => this.handleClickSquare(rowIndex, colIndex)}
/>
);
})}
</div>)
})}
</div>
)
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);
CSS:
#board {
font-size: 0;
}
.board-square {
text-align: center;
align-items: center;
justify-content: center;
height: 20px;
width: 20px;
line-height: 20px;
font-size: 10px;
margin: 0;
padding: 0;
border: 0;
}
.board-square-dark {
background-color: green;
}
.board-square-light {
background-color: #E0AB76;
}
The reason for this behavior is because of the default baseline vertical alignment. Already well explained in this answer: https://stackoverflow.com/a/13550706/1790728
By changing the display to inline-block and setting vertical-align to top, the squares will be aligned without using a white-space.
Working example: https://codepen.io/anon/pen/MZLZYj

How to dynamically add mat-icon to divs with angular renderer2?

I have a HTML with a lot of divs. I have already generated divs that look like this.
static HTML (not dynamically generated) example of desired result using renderer2
<div class="time-rowss clearfixx" #timerowss>
<div><mat-icon>today</mat-icon> date </div>
</div>
<div class="time-rows clearfix" #timerows>
<div><mat-icon>brightness_3</mat-icon>00:00</div>
<div><mat-icon>brightness_3</mat-icon>01:00</div>
<div><mat-icon>brightness_3</mat-icon>02:00</div>
</div>
I want to achieve the same but dynamically generating the divs.
What I have done so far is add dynamically times and dates.
Here is my code:
for (let j = this.requestVehicle.startDateTime.getDate(); j < this.requestVehicle.endDateTime.getDate(); j++) {
const newTime = new Date(time.getTime() + 24 * 3600 * 1000);
time = newTime;
const date = this.renderer.createElement('div');
this.renderer.appendChild(date, this.renderer.createText(newTime.getDate() + '/' + newTime.getMonth() + '/' + newTime.getFullYear()));
this.renderer.appendChild(this.d7.nativeElement, date);
for (let i = 0; i < 24; i++) {
const b = this.renderer.createElement('div');
const icon = this.renderer.createElement('mat-icon');
if (i < 7 || i > 18) {
this.renderer.setAttribute(icon, 'svgIcon', '"brightness_3"');
} else {
this.renderer.setProperty(icon, 'svgIcon', '"wb_sunny"');
}
let text;
if (i >= 10) {
text = ' ' + i;
} else {
text = '0' + i;
}
this.renderer.appendChild(b, icon);
this.renderer.appendChild(b, this.renderer.createText(text + ':00'));
this.renderer.appendChild(this.d3.nativeElement, b);
}
}
I have tried several options:
this.renderer.setProperty(icon, 'svgIcon', '"wb_sunny"');
this.renderer.setProperty(icon, 'svgIcon', 'wb_sunny');
this.renderer.setAttribute(icon, 'svgIcon', '"brightness_3"');
this.renderer.setAttribute(icon, 'svgIcon', 'brightness_3');
this.renderer.appendChild(icon, this.renderer.createText('brightness'));
this.renderer.appendChild(icon, 'brightness_3');
none of these options work. I also tried iconName instead of svgIcon.
how should I add iconName or svgIcon with renderer2?
I figured it out. I what I was noticed when I tried to add mat-icon value with renderer createText. It was adding it correctly. The problem was that the IconName was appearing in html as name not as an icon. So I realized the css was missing. I looked into the dev tools and inspected the divs and mat-icons. I found out that they were missing classes.
So I added the classes manually.
In short
you need to create mat-icon element.
const dateIcon = this.renderer.createElement('mat-icon');
add value using createText.
this.renderer.appendChild(dateIcon, this.renderer.createText('today'));
give classes for css styling.
this.renderer.addClass(dateIcon, 'mat-icon');
this.renderer.addClass(dateIcon, 'material-icons');
Full code if curious. -->
for (let j = this.requestVehicle.startDateTime.getDate(); j < this.requestVehicle.endDateTime.getDate(); j++) {
const newTime = new Date(time.getTime() + 24 * 3600 * 1000);
time = newTime;
const date = this.renderer.createElement('div');
const dateIcon = this.renderer.createElement('mat-icon');
this.renderer.appendChild(dateIcon, this.renderer.createText('today'));
this.renderer.addClass(dateIcon, 'mat-icon');
this.renderer.addClass(dateIcon, 'material-icons');
this.renderer.appendChild(date, dateIcon);
this.renderer.appendChild(date, this.renderer.createText(newTime.getDate() + '/' + newTime.getMonth() + '/' + newTime.getFullYear()));
this.renderer.appendChild(this.d7.nativeElement, date);
for (let i = 0; i < 24; i++) {
const b = this.renderer.createElement('div');
const icon = this.renderer.createElement('mat-icon');
if (i < 7 || i > 18) {
this.renderer.appendChild(icon, this.renderer.createText('brightness_3'));
} else {
this.renderer.appendChild(icon, this.renderer.createText('wb_sunny'));
}
let text;
if (i >= 10) {
text = ' ' + i;
} else {
text = '0' + i;
}
this.renderer.appendChild(b, icon);
this.renderer.addClass(icon, 'mat-icon');
this.renderer.addClass(icon, 'material-icons');
this.renderer.appendChild(b, this.renderer.createText(text + ':00'));
this.renderer.appendChild(this.d3.nativeElement, b);
}
}

Initializing the height of a textarea based on the content using a knockout data-bind

I am currently expanding and collapsing the height of a text area on the keyupevent. However I want the text area also to initialise it's height once the value is binded to the text area via a knockout custom binding. Any solutions?
(With the use of only javascript) (Without adding any jquery libraries)
Current key up handelling code
var textElement = $textBox.get(0);
var textElementOriginalHeight = $textBox.height();
while ($textBox.height() > textElementOriginalHeight && textElement.scrollHeight < textElement.offsetHeight) {
$textBox.height($textBox.height() - 1);
}
var h = 0;
while (textElement.scrollHeight > textElement.offsetHeight && h !== textElement.offsetHeight) {
h = textElement.offsetHeight;
$textBox.height($textBox.height() + 1);
}
You're gonna need to register a custom binding-handler to do that. Something like:
(function(ko)
{
function handleAutoFit(textElement, val)
{
if (!textElement.value)
textElement.value = val;
var $textBox = $(textElement);
var textElementOriginalHeight = $textBox.height();
while ($textBox.height() > textElementOriginalHeight && textElement.scrollHeight < textElement.offsetHeight) {
$textBox.height($textBox.height() - 1);
}
var h = 0;
while (textElement.scrollHeight > textElement.offsetHeight && h !== textElement.offsetHeight) {
h = textElement.offsetHeight;
$textBox.height($textBox.height() + 1);
}
}
ko.bindingHandlers.autoFit = {
update: function (element, valueAccessor) {
var val = ko.unwrap(valueAccessor());
handleAutoFit(element, val);
}
};
})(ko);
HTML:
<textarea data-bind="autoFit: someObservable, value: someObservable, valueUpdate: 'afterkeydown'"></textarea>
Or if you're using the Knockout 3.1 and above:
<textarea data-bind="autoFit: someObservable, textInput: someObservable"></textarea>
See Fiddle