Cant change webkit border-spacing - html

I've been working on some sort of a drawing grid recently and used border-spacing.
HTML:
<table id="table"></table>
<script>
var res = 16;
var cell_size = 8;
var table = document.getElementById("table");
var border_size = parseInt(window.getComputedStyle(table).getPropertyValue("border-spacing"),10);
table.style.width = (cell_size + border_size) * res + border_size + "px";
table.style.height = (cell_size + border_size) * res + border_size + "px";
for(y=0;y<res;y++){
var row = table.insertRow();
for(x=0;x<res;x++){
row.insertCell();
}
}
</script>
CSS:
table{
background: #000000;
border-spacing: 1px;
}
td{
padding: 0;
background: #c9c9c9;
}
Idea is to form a table with fixed amount of cells with pre-set size and border spacing.
var res = 16;
var cell_size = 8;
var border_size = parseInt(window.getComputedStyle(table).getPropertyValue("border-spacing"),10);
Size of the whole table is then computed based on these parameters.
table.style.width = (cell_size + border_size) * res + border_size + "px";
table.style.height = (cell_size + border_size) * res + border_size + "px";
Finally the table is created via nested loop.
for(y=0;y<res;y++){
var row = table.insertRow();
for(x=0;x<res;x++){
row.insertCell();
}
}
If I use small (<4px) border size it doesn't seem to work right in Chrome - cell sizes start to be offset around 0.4 px. In Chrome there are 2 computed webkit options affecting this offset:
-webkit-border-horizontal-spacing
-webkit-border-vertical-spacing
However I can't change them like so:
-webkit-border-horizontal-spacing: 1px;
-webkit-border-vertical-spacing: 1px;
Computed values will still stay at 0.8 px. It isn't much at the end, but it really bothers me. Is there any reason for such behaviour and is it possible to have exact cell sizes with such low values?
Here is an example fiddle: https://jsfiddle.net/kdqoc4vy/1/
Note that cell_size should be exactly 20px, but in fact it is 19.2.
P.S. With Edge evertyhing works as it should. This 0.8px is almost nothing when the table is small, but when it has 64 rows and columns such inconsistency becomes a problem. Thanks in advance for your reply!

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";
}
}

Scrolling parent of display:grid unexpectedly scrolls when a child's grid-row-start changes

We have a list with cells that have explicit grid-rows/columns set inside a grid which is inside a scrollable element. We draw a selection border over a number of these cells.
When the scrolling element is not scrolled all the way to the top, changing the selection element's grid-row-start causes the scrolling area to scroll to the point where the offset of the top of the selection element in relation to the viewport is maintained. In other words, if the selection element is 100px from the top of the viewport, and you click on a cell lower down on the page, the scroll element will scroll down until the selection element is 100px from the top of the viewport again.
This usually happens. Sometimes it doesn't on a fresh page. But click on the scrollbar and scrolling a teeny bit causes this behavior to start happening again. This leads me to believe this is a browser bug and not intended behavior, but it repros in Chrome, Edge, AND FireFox.
You can even open the F12 tools and modify grid-row-start manually, and the scrolling behavior occurs.
Sample code is below. I've found a workaround, which is commented out, but it is not ideal. To replicate the "bug", scroll down a bit then start clicking on cells.
Is this intended behavior? What's causing it? Any easy way to avoid it?
Thanks!
window.onload = () => {
const list = document.getElementById('list');
const numRows = 100;
const numColumns = 8;
for (let i = 0; i < numRows; i++) {
let row = document.createElement('div');
row.className = 'row';
row.style.gridRow = String(i + 1);
for (let j = 0; j < numColumns; j++) {
let cell = document.createElement('div');
cell.className = 'cell';
cell.style.gridRow = String(i + 1);
cell.style.gridColumn = String(j + 1);
cell.innerText = `${i} - ${j}`;
cell.onclick = () => {
const selection = document.getElementById('selection');
selection.style.gridArea = `${i+1} / ${j+1} / ${i+2} / ${j+2}`;
/*
selection.style.display = 'none';
setTimeout(() => {
document.getElementById('selection').style.display = '';
}, 0);
*/
};
row.appendChild(cell);
}
list.appendChild(row);
}
};
.list {
display: grid;
}
.row {
display: contents;
}
.cell {
outline: 1px solid black;
padding: 12px 4px;
}
.selection {
outline: 5px solid green;
grid-area: 20 / 4 / 21 / 5;
z-index: 1;
}
<div class='list' id='list'>
<div class='selection' id='selection'></div>
</div>

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 on page hide after scale increase

I use jQuery to change scale of my web page. Here is the code:
<script>
var currFFZoom = 1;
var currIEZoom = 1;
function plus(){
var step = 0.1;
currFFZoom += step;
if (currFFZoom > 10) currFFZoom = 10;
$('body').css('transform','scale(' + currFFZoom + ')');
};
function minus(){
var step = 0.1;
currFFZoom -= step;
$('body').css('transform','scale(' + currFFZoom + ')');
};
</script>
It works fine, but when i increase the scale, some elements moves out of the browser edge and I can't scroll page to them. So, they become unreachable.
PICTURE OF MAILFUNCTION
How I can fix it?
So, I solved the problem. First, I changed
html
{ height: 0%; }
And width needs to be calculated using jquery. This code should be bind to some event handler (button click or something else):
currFFZoom = $('body').css('transform','scale');
$('html').width(currFFZoom*100 + '%');
Worked for me. The whole page is scrollable

How can I achieve a 2 column layout that switches between fluid and fixed divs?

When the window is resized, Column A should shrink its width up to 50 pixels, but Column B should remain at a fixed width . After 50 pixels are removed from Column A, it should become fixed, and then Column B should then begin shrink (unlimited).
Within the HTML code, A should come before B. The divs need to be in the order because of the requirements I need with responsive design.
<div id="ColumnA"></div>
<div id="ColumnB"></div>
Can this be achieved?
Just for fun, and to help my js I put together this little FIDDLE that does what you want with js.
JS
var startleftwidth = 150;
var leftwidth = 150;
var minleftwidth = 101;
var startrightwidth = 420;
var rightwidth = 420;
var startwindowwidth = $(window).width();
var difference = 0;
$(window).resize(function(){
difference = $(window).width() - startwindowwidth;
leftwidth = startleftwidth + difference;
if (leftwidth < minleftwidth)
{
setleftwidth(100);
newrightwidth = rightwidth + difference + (startleftwidth - minleftwidth);
setrightwidth(newrightwidth);
}
else
{
setleftwidth(leftwidth);
setrightwidth(startrightwidth);
}
if (leftwidth > startleftwidth)
{
setleftwidth(startleftwidth);
}
});
function setleftwidth(leftsize)
{
$('.leftdiv').css('width', leftsize);
}
function setrightwidth(rightsize)
{
$('.rightdiv').css('width', rightsize);
}