Make paragraph auto fill remaining height of parent - javascript

in my current project I have the following structure
<ol>
<li>...</li>
<li>
<section>
<h1>some title</h1>
<p>some text</p>
</section>
</li>
<li>...</li>
</ol>
The ol has a variable but set height which is filled by the li (via height:100%;). Now I want the text in the paragraph to fill the remaining height (via line-height and/or font-size).
I have researched this but didn't really find a solution to this, only FitText.js but it "is for gigantic display text only" as they say.
So I've come up with a JavaScript solution, calculating the remaining height from two perspectives (default line-height: 1em and line-height: 0em) and calculate the required line-height so remaining height is 0 (via basic maths, interpolating the function y = a*x + b where y equals the remaining height).
var outerHeight = $("section").innerHeight(),
h1Height = $("section").children("h1").first().outerHeight(),
pHeight = $("section").children("p").first().outerHeight();
var defaultHeight, a, b, x;
defaultHeight = outerHeight - (h1Height + pHeight);
$("section").children("p").first().css("line-height", "0em");
b = outerHeight - (h1Height + $("section").children("p").first().outerHeight());
a = defaultHeight - b;
x = -b / a;
$("section").children("p").first().css("line-height", x+"em");
jsfiddle here
Finally my question: Is it possible to achieve this in a better way (pure CSS would be awesome but I doubt that)? If not, what events could occur in which I had to recalculate (resizing browser eg)?
Also I'd like the text to fill the section to maximum extent - currently the last line's line-height might create a small visible area between the characters and the bottom of the section.

This looked interesting to me, so I thought I might take a crack at it. Here's a FIDDLE that might give you some new ideas.
I switched everything to divs (not sure why, perhaps it's just me)
I made the assumption that the title would be fixed in size, and the text would be distributed over the size of the large div minus the size of the title.
Then a simple division of totalheight and initial size of the text.
Obviously it won't work at the extremes (three words, or a book-length text.
I left the alerts in just for show, obviously you can delete them or comment them out.
JS
var outerheight = $(".oldiv").innerHeight();
var titleheight = $(".oldiv div:first-child").outerHeight();
var textheight = $(".oldiv div:last-child").outerHeight();
var heighttodistribute = outerheight - titleheight;
alert( outerheight + '-' + titleheight + '-' + textheight + '-' + heighttodistribute);
var newlineheight = heighttodistribute / textheight;
alert(newlineheight);
$('.oldiv div:last-child').css('line-height', newlineheight);

Related

Html : Text width is different than summation of its character's width

I need to calculate the text width without appending it to DOM.For this,i calculate and save each character's width i.e from (A-Z & a-z and other necessary characters).
Code :
function calculateCharactersWidth(){
for(var i=65;i<123;i++){
var x = $('<div style="float:left">').text(String.fromCharCode(i)).appendTo('#hiddenView');
charactersWidth[i] = x.width();
remHeight = x.height();
}
var x = $('<div style="float:left">').text(String.fromCharCode(45)).appendTo('#hiddenView');
charactersWidth[45] = x.width();
$('#hiddenView').empty();
}
and get each word's width like this
function getStringWidth(word){
var width=0;
for(var i=0;i<word.length;i++){
width=width+charactersWidth[word.charAt(i).charCodeAt(0)];
}
return width;
}
but when the word is appended to DOM. Its width differs from the calculated width.In some cases it differs by 3-4 px.
Even if we calculate it like this
// css font-weight:bold and font-size:14px
<div>Absin</div> width = 35.031
<div>A</div> width = 10.125
<div>b</div> width = 7.797
<div>s</div> width = 6.219
<div>i</div> width = 7
<div>n</div> width = 4.672
Added width = 35.831
This small difference creates a huge difference with array of words.So why is the difference and how can i make it work?
The kerning of the font is definitely messing up your calculation.
I am not sure why you'd need to do this, but why don't you print out your text in a hidden element and take the width of that?
function getStringWidth(word){
var x = $('<div style="float:left">').text(word).appendTo('#hiddenView');
return x.clientWidth;
}
This way you can also change the style, the font type and size, etc.

How to find css unit for this number

I have a input type text
<input type="text">
Basically I am using javascript ClientRect to get caret details. ClientRect looks like this
[object ClientRect]
{
[functions]: ,
__proto__: { },
bottom: 540.7999877929687,
constructor: { },
height: 24,
left: 1034.5399169921875,
right: 1034.5399169921875,
top: 516.7999877929687,
width: 0
}
This is generated on everytext input.
left: 1034.5399169921875,
left: 1065.5399169921875,
left: 1078.5399169921875,
I want to convert this number to CSS units like px/%/rem/vh. So that I can put dynamic css. How to do it?
Try accessing the left position of your input and subtract the left position of your caret. This should give you an approximate width of the text in the input, if that's what you are looking for. You'll need to add an id or create a selector for your text input.
var inputElementRect = document.getElementById('YOURINPUTID').getBoundingClientRect()
var width = inputElementRect.left - caretRect.left
Those values are px by default .. so just add suffix as px to that value and use it.
<input type="text">
to get that value
let text = document.querySelector('input');
let values = text.getBoundingClientRect();
let top_value = values.top + 'px';
let bottom_value = values.bottom + 'px';
let width_value = values.width + 'px';
let height_value = values.height + 'px';
console.log('top: '+ top_value);
console.log('bottom: '+ bottom_value);
console.log('width: '+ width_value);
console.log('height: '+ height_value);
here properties other than width and height are relative to the view port ( top, bottom, left, right ) ,
so if scroll this values will changes ..
to get the perfect values even if scroll add this values with window.scrollX , window.scrollY or can use window.pageXOffset , window.pageYOffset
So if I understand the question correctly, you have position values for the cursor inside of the input and you want to convert it into different types of CSS units, presumably so you can do something to the input or related things
The first thing to understand is that ClientRect positions are relative to the viewport. So as vhutchinson pointed out, if you want the width of text you need to compare to the input's "left" value as defined by getBoundingClientRects. That's a good start, but if you're not just influencing left but also care about top, you need to account for scrolling. If your window/page is the only scrolling container, you should be able to do this simply by adding window.scrollY to top, and window.scrollX to left to understand your offset relative to the window.
All of these units are pixels by default... if you want to convert to rem it's pretty straightforward, 1 rem = the font-size of your root element, so to convert to rem you can do something like
var remBase = parseInt(window.getComputedStyle(document.body).getPropertyValue('font-size'), 10);
var remValue = (myComputedPixelValue / remBase) + "rem";
Doing VW is similar using the answer in Get the browser viewport dimensions with JavaScript for cross-browser window dimensions, you'd end up with something that looks like
var viewportWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
var vwValue = (myComputedPixelValue / viewportWidth) + "vw";
Percentages are trickier, because you'd need to compute it based on the parent of the element you're applying the css value to, but the general idea follows the same principle.

Text align by word length

I have been working on a project that has a similar result as the spritz app. Basically, it takes a string, turns each word into an array and then displays each output one at a time. The problem is that I can't seem to find a way to align each word based on the word length (see link).
Here is a demo of the spritz output that I am looking for: http://imgur.com/a/UlZ6W
Does anyone know how to center an output based on the the length of a word?
IE:
If array length is 1 letter, center letter
If array length is 2 - 4, center second letter
if array length is 5 - 7, center third letter
ect.
A start that might could work for you.
http://jsfiddle.net/kimiliini/ap64C/
Use an element to measure width. Calculate width of word to middle w/wo center letter.
For example having the element:
<span id="tw"></span>
and CSS:
#tw {
position: absolute;
visibility: hidden;
white-space: nowrap;
font: bold 18px Arial;
}
One can:
// get element
tw = document.getElementById('tw');
// set text
tw.textContent = 'foo';
// get width
tw.offsetWidth;
Having this as base we can split word into three groups:
Letters belonging to the left of center letter.
Letters for left + center.
Letters after center.
Example. Having the word starting and a box where we want to center at offset 110 pixels from left.
length = 8
left = sta
center = r
right = ting
width_left = 15
width_left + width_center = 19
mid = (19 - 15) / 2
box margin left = 110 - (width_left + mid)
Simple, should be refactored code, sample:
// span's for left, center and right
var tx = {
L: document.getElementById('txt_L'),
C: document.getElementById('txt_C'),
R: document.getElementById('txt_R')
};
// Box to that holds span's, to set margin left on.
var b = document.getElementById('bw');
function width(w) {
tw.textContent = w;
return tw.offsetWidth;
}
function middle(w) {
var n = w.length,
// Center char calculation. 1=1, 2-5=2, 6-8=3, ...
c = ~~((n + 1) / 3) + 1,
z = {};
z.a = width(w.substr(0, c - 1));
z.b = width(w.substr(0, c));
z.c = (z.b - z.a) / 2;
b.style.marginLeft = ~~(110 - (z.a + z.c)) + 'px';
tx.L.textContent = w.substr(0, c - 1);
tx.C.textContent = w.substr(c - 1, 1);
tx.R.textContent = w.substr(c);
}
Some Background On Your Problem
I believe you're going to find this either very difficult to solve or else are going to have to railroad you users quite dramatically.
Words don't have lengths, because characters aren't all of the same size, and even the space between letters is different The exception is of course monospaced fonts (and they aren't really an exception they just behave like one).
To achieve what you're after, you need to know the precise size of each letter, and for real accuracy would need to know the precise size of each letter in relation to it's adjacent letters. But that's typography nitty-gritty and probably not what you want to do.
If it were me, I'd artificially do it with tags, whose width you could specify (say, your largest character + 1 px). Then word length will always be equivalent to some multiple of the parent element.
The other option would be to render the text as image via HTML5 canvas, which could be made to autofit the word and then would have, itself, a width property.
A Solution to Consider
Try something like:
<style>
li.char {
display:inline-block;
text-align:center;
width:4px;
}
li.char.center { color:red}
</style>
<body>
...
<div style="text-align:center">
<ul id="spritztext"> </ul>
</div>
<script>
var my_string = "strings are cool";
var center = ceil( my_string.len() / 2);
/* rounding up/down is a bad solution to finding the center character, for what it's worth. examine the words 'will' vis 'limb' in a proportional font and you'll see why*/
var count = 0;
$(my_string).each(function() {
var class = count == center ? "char center" : "char";
count++
var char_element ="<li class='" + class +"'>" + my_string[count] + "</li>";
$("#spritztext").append(char_element)
});
TLDR: text isn't graphics; you need a monospaced font, a rendering strategy, or a hack via parent elements
One solution may be divide the word into two parts and use a table-like two column layout to show the word. Like this:
<table style="border-collapse: collapse; width: 200px;"><tr>
<td style="text-align: right; width: 100px; padding: 0;">wo</td>
<td style="text-align: left; width: 100px; padding: 0;">rd</td>
</tr></table>
This doesn't exactly align a letter center at one position, but could align left or right edge of a letter exactly at some position.
To refine the solution, you may need to get letter/character width on demand (By creating a temp 'span' node with inner text of the letter, then get the offsetWidth). Then adjust the margin of the two parts to achieve the exactly goal.

auto adjustment of font size to fit in a fixed shaped div

Here is the fiddle im working with:
http://jsfiddle.net/LbUFg/
The html code is:
<div class="body">
<div class="variation1 font700 green1">
<h2>
sample <span class="divider"> arrowshape </span>
</h2>
</div>
<div class="clear"></div>
<div class="variation2 font700 green2">
<h2>
as the text increases the font size must decrease but the block height must remain same <span class="divider"> as the text increases the font size must decrease but the block height must remain same </span>
</h2>
</div>
<div class="clear"></div>
<!-- OTHER HTML -->
</div>
I want to adjust the text such that it fits in the div without changing the dimensions(size) of the arrow block shown(Text size can change but not the block size). The arrow block must look like the sample arrow and Im facing the issue as shown in variation2. Can someone please help me out with this??
try a jquery plugin FITTEXT this will help you
You'll have to use javascript. CSS itself can't handle this.
For a poor example:
$(".font700").each(function(i, obj) {
newSize = $(obj).text().length;
newSize = 64 - newSize;
newSize = (newSize < 10) ? 10 : newSize;
$(obj).css("font-size", newSize + "px");
});
JSFiddle
There will be better solutions than this, by the way. This just demonstrates that it is possible using javascript (jQuery, specifically). You can probably find some plugins such as FitText that can solve a lot of these issues for you.
(Thanks to Grim for the link)
For those who don't like plugins like FitText, I just played around and calculate the font size to fit in the containing element by looking at the current average letter width (complication:multiple lines, solved here hilariously by temporarily changing the css-width)
HTML is
<div><h1><a><span>Title</span></a></h1></div>
And jQuery:
$("h1").each(function() {
var l = $(this).text().length;
var w = $(this).prop("offsetWidth"); //clientWidth doesn't always work
var old_pos = $(this).find("a").css("position");
var old_w = $(this).find("a").css("width");
$(this).find("a").css("position", "absolute");
$(this).find("a").css("width", "1000px");
var w2 = $(this).find("span").prop("offsetWidth");
var h2 = $(this).find("span").prop("offsetHeight");
var c = 1.2*(w2/h2)/l; // Current proportion of font width, 1.2 is constant
var fontsize = w/l/c;
fontsize = (fontsize<10)?10:fontsize; //limit min
//$(this).append(" "+c+"/"+fontsize); //test
//Set font size to fill width of parent element
$(this).css("font-size",fontsize+"px");
$(this).find("a").css("position", old_pos);
$(this).find("a").css("width", old_w);
});
This resizes my fonts in a masonry-style grid

how do i get the x and y position directly under the left bottom side of the input rectangle?

I'm thinking of implementing a custom auto-complete feature so basically my idea now is that i will make an abs positioned div and give it the position here:
(image) http://i.stack.imgur.com/3c5BH.gif
So my question is with a variable referencing the textbox, how do i get the x and y position directly under the left bottom side of the input rectangle?
My script must work in latest versions of IE / FF / Safari / Opera / Chrome
I know i can use a library to do it, but no i'm interested in learning how do they do it (or maybe better ways)?
This question is a lot more complicated than it seems and involves getting the position of the element relative to the document. The code to do so can be pulled from the jquery source (http://code.jquery.com/jquery-1.6.1.js -- search for "jQuery.fn.offset")
in jQuery:
var node = $('#textbox'),
pos = box.offset(); // the complicated piece I'm using jQuery for
node.top += node.height(); // node.offsetHeight without jQuery
node.left += node.width(); // node.offsetWidth without jQuery
The answer can be extremely simplified if you don't care about FF2 or Safari3:
var box = document.getElementById('yourTextBox').getBoundingClientRect(),
left = box.left,
bottom = box.bottom;
x = x offset
y = y offset - ( textbox height +
padding-top + padding-bottom )
Good comments! For my scenario, there is always an offset parent (which is why I use position - http://api.jquery.com/position/). In hopes that it might help someone else wanting a quick fix, here's the code:
// I have a parent item (item) and a div (detail)
// that pops up at the bottom left corner of the parent:
var jItem = $(item);
var pos = jItem.position();
var marginTop = parseInt(jItem.css('margin-top'));
if (isNaN(marginTop)) {
marginTop = 0;
}
$(detail).css("top", pos.top + jItem.outerHeight() + marginTop)
.css("left", pos.left);
$(detail).show();
Just give the box a defined width and height. Then, get its top and left property and add it with the width and height. Simple. I am gonna give you Pseodocode.
<STYLE>
object{width: 100px; height: 20px;}
</STYLE>
<SCRIPT>
x = object.left;
y = object.top;
x = x + object.width;
y = y + object.height;
</SCRIPT>

Categories

Resources