Place and fit n of elements between two existing elements - javascript

Have two elements with fixed width (in %).
First element positioned left: 0, second element positioned right: 0;
Need to append some N of elements between these two elements, so each of the new appended elements have same width (depending on available space between main elements).
http://jsfiddle.net/hXUyh/1/
The problem is that new elements are positioned NOT accurately (crossing each other or leaving some extra space between) and NOT consistently with different browser's window sizes.
Please help.
I understand that different browsers will give different output, but this script will be limited to Google Chrome use only.

Try this:
$(document).ready(function() {
for (var i = 0; i < 9; i++) {
$('<div/>').appendTo('body')
}
$(window).resize(function() {
var firstWidth = $('#element-0').width();
var r = ($(window).width() - (firstWidth * 2) - 2) / 9;
$('div').slice(2, 11).each(function(i) {
$(this).css({
left: i == 0 ? firstWidth : firstWidth + r * (i),
width: r
})
})
}).resize()
});​
http://jsfiddle.net/yav9Q/

I had similar requirements not too long ago. Without questioning or changing your strategy/code, here is a fiddle showing as close as I can get it:
http://jsfiddle.net/hXUyh/3/
(Note that I haven't catered for resizing as your original code didn't)
The issue that the width() jquery function will round. So your maths will always be a little bit off. I've improved this by using calculations on window.innerWidth, it will be a little bit misaligned because of the floating point widths. Using floating point widths for pixel perfect alignment is not the way to go.
If you want perfect alignment, use padding. Here is an example using the smallest padding possible: http://jsfiddle.net/hXUyh/8/
The maths is much easier if you don't need a border.

Related

How to obtain effective iframe height [duplicate]

How do you get the rendered height of an element?
Let's say you have a <div> element with some content inside. This content inside is going to stretch the height of the <div>. How do you get the "rendered" height when you haven't explicitly set the height. Obviously, I tried:
var h = document.getElementById('someDiv').style.height;
Is there a trick for doing this? I am using jQuery if that helps.
Try one of:
var h = document.getElementById('someDiv').clientHeight;
var h = document.getElementById('someDiv').offsetHeight;
var h = document.getElementById('someDiv').scrollHeight;
clientHeight includes the height and vertical padding.
offsetHeight includes the height, vertical padding, and vertical borders.
scrollHeight includes the height of the contained document (would be greater than just height in case of scrolling), vertical padding, and vertical borders.
It should just be
$('#someDiv').height();
with jQuery. This retrieves the height of the first item in the wrapped set as a number.
Trying to use
.style.height
only works if you have set the property in the first place. Not very useful!
NON JQUERY since there were a bunch of links using elem.style.height in the top of these answers...
INNER HEIGHT:
https://developer.mozilla.org/en-US/docs/Web/API/Element.clientHeight
document.getElementById(id_attribute_value).clientHeight;
OUTER HEIGHT:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.offsetHeight
document.getElementById(id_attribute_value).offsetHeight;
Or one of my favorite references: http://youmightnotneedjquery.com/
I use this to get the height of an element (returns float):
document.getElementById('someDiv').getBoundingClientRect().height
It also works when you use the virtual DOM. I use it in Vue like this:
this.$refs['some-ref'].getBoundingClientRect().height
For a Vue component:
this.$refs['some-ref'].$el.getBoundingClientRect().height
You can use .outerHeight() for this purpose.
It will give you full rendered height of the element. Also, you don't need to set any css-height of the element. For precaution you can keep its height auto so it can be rendered as per content's height.
//if you need height of div excluding margin/padding/border
$('#someDiv').height();
//if you need height of div with padding but without border + margin
$('#someDiv').innerHeight();
// if you need height of div including padding and border
$('#someDiv').outerHeight();
//and at last for including border + margin + padding, can use
$('#someDiv').outerHeight(true);
For a clear view of these function you can go for jQuery's site or a detailed post here.
it will clear the difference between .height() / innerHeight() / outerHeight()
style = window.getComputedStyle(your_element);
then simply: style.height
Definitely use
$('#someDiv').height() // to read it
or
$('#someDiv').height(newHeight) // to set it
I'm posting this as an additional answer because theres a couple important things I just learnt.
I almost fell into the trap just now of using offsetHeight. This is what happened :
I used the good old trick of using a debugger to 'watch' what properties my element has
I saw which one has a value around the value I was expecting
It was offsetHeight - so I used that.
Then i realized it didnt work with a hidden DIV
I tried hiding after calculating maxHeight but that looked clumsy - got in a mess.
I did a search - discovered jQuery.height() - and used it
found out height() works even on hidden elements
just for fun I checked the jQuery implementation of height/width
Here's just a portion of it :
Math.max(
Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]),
Math.max(document.body["offset" + name], document.documentElement["offset" + name])
)
Yup it looks at BOTH scroll and offset. If that fails it looks even further, taking into account browser and css compatibility issues. In other words STUFF I DONT CARE ABOUT - or want to.
But I dont have to. Thanks jQuery!
Moral of the story : if jQuery has a method for something its probably for a good reason, likely related to compatibilty.
If you haven't read through the jQuery list of methods recently I suggest you take a look.
I think the best way to do this in 2020 is to use vanilla js and getBoundingClientRect().height;
Here's an example
let div = document.querySelector('div');
let divHeight = div.getBoundingClientRect().height;
console.log(`Div Height: ${divHeight}`);
<div>
How high am I? 🥴
</div>
On top of getting height this way, we also have access to a bunch of other stuff about the div.
let div = document.querySelector('div');
let divInfo = div.getBoundingClientRect();
console.log(divInfo);
<div>What else am I? 🥴</div>
I made a simple code that doesn't even need JQuery and probably gonna help some people.
It gets the total height of 'ID1' after loaded and use it on 'ID2'
function anyName(){
var varname=document.getElementById('ID1').offsetHeight;
document.getElementById('ID2').style.height=varname+'px';
}
Then just set the body to load it
<body onload='anyName()'>
document.querySelector('.project_list_div').offsetHeight;
Hm we can get the Element geometry...
var geometry;
geometry={};
var element=document.getElementById(#ibims);
var rect = element.getBoundingClientRect();
this.geometry.top=rect.top;
this.geometry.right=rect.right;
this.geometry.bottom=rect.bottom;
this.geometry.left=rect.left;
this.geometry.height=this.geometry.bottom-this.geometry.top;
this.geometry.width=this.geometry.right-this.geometry.left;
console.log(this.geometry);
How about this plain JS ?
So is this the answer?
"If you need to calculate something but not show it, set the element to visibility:hidden and position:absolute, add it to the DOM tree, get the offsetHeight, and remove it. (That's what the prototype library does behind the lines last time I checked)."
I have the same problem on a number of elements. There is no jQuery or Prototype to be used on the site but I'm all in favor of borrowing the technique if it works. As an example of some things that failed to work, followed by what did, I have the following code:
// Layout Height Get
function fnElementHeightMaxGet(DoScroll, DoBase, elementPassed, elementHeightDefault)
{
var DoOffset = true;
if (!elementPassed) { return 0; }
if (!elementPassed.style) { return 0; }
var thisHeight = 0;
var heightBase = parseInt(elementPassed.style.height);
var heightOffset = parseInt(elementPassed.offsetHeight);
var heightScroll = parseInt(elementPassed.scrollHeight);
var heightClient = parseInt(elementPassed.clientHeight);
var heightNode = 0;
var heightRects = 0;
//
if (DoBase) {
if (heightBase > thisHeight) { thisHeight = heightBase; }
}
if (DoOffset) {
if (heightOffset > thisHeight) { thisHeight = heightOffset; }
}
if (DoScroll) {
if (heightScroll > thisHeight) { thisHeight = heightScroll; }
}
//
if (thisHeight == 0) { thisHeight = heightClient; }
//
if (thisHeight == 0) {
// Dom Add:
// all else failed so use the protype approach...
var elBodyTempContainer = document.getElementById('BodyTempContainer');
elBodyTempContainer.appendChild(elementPassed);
heightNode = elBodyTempContainer.childNodes[0].offsetHeight;
elBodyTempContainer.removeChild(elementPassed);
if (heightNode > thisHeight) { thisHeight = heightNode; }
//
// Bounding Rect:
// Or this approach...
var clientRects = elementPassed.getClientRects();
heightRects = clientRects.height;
if (heightRects > thisHeight) { thisHeight = heightRects; }
}
//
// Default height not appropriate here
// if (thisHeight == 0) { thisHeight = elementHeightDefault; }
if (thisHeight > 3000) {
// ERROR
thisHeight = 3000;
}
return thisHeight;
}
which basically tries anything and everything only to get a zero result. ClientHeight with no affect. With the problem elements I typically get NaN in the Base and zero in the Offset and Scroll heights. I then tried the Add DOM solution and clientRects to see if it works here.
29 Jun 2011,
I did indeed update the code to try both adding to DOM and clientHeight with better results than I expected.
1) clientHeight was also 0.
2) Dom actually gave me a height which was great.
3) ClientRects returns a result almost identical to the DOM technique.
Because the elements added are fluid in nature, when they are added to an otherwise empty DOM Temp element they are rendered according to the width of that container. This get weird, because that is 30px shorter than it eventually ends up.
I added a few snapshots to illustrate how the height is calculated differently.
The height differences are obvious. I could certainly add absolute positioning and hidden but I am sure that will have no effect. I continued to be convinced this would not work!
(I digress further) The height comes out (renders) lower than the true rendered height. This could be addressed by setting the width of the DOM Temp element to match the existing parent and could be done fairly accurately in theory. I also do not know what would result from removing them and adding them back into their existing location. As they arrived through an innerHTML technique I will be looking using this different approach.
* HOWEVER * None of that was necessary. In fact it worked as advertised and returned the correct height!!!
When I was able to get the menus visible again amazingly DOM had returned the correct height per the fluid layout at the top of the page (279px). The above code also uses getClientRects which return 280px.
This is illustrated in the following snapshot (taken from Chrome once working.)
Now I have noooooo idea why that prototype trick works, but it seems to. Alternatively, getClientRects also works.
I suspect the cause of all this trouble with these particular elements was the use of innerHTML instead of appendChild, but that is pure speculation at this point.
offsetHeight, usually.
If you need to calculate something but not show it, set the element to visibility:hidden and position:absolute, add it to the DOM tree, get the offsetHeight, and remove it. (That's what the prototype library does behind the scenes last time I checked).
Sometimes offsetHeight will return zero because the element you've created has not been rendered in the Dom yet. I wrote this function for such circumstances:
function getHeight(element)
{
var e = element.cloneNode(true);
e.style.visibility = "hidden";
document.body.appendChild(e);
var height = e.offsetHeight + 0;
document.body.removeChild(e);
e.style.visibility = "visible";
return height;
}
If you are using jQuery already, your best bet is .outerHeight() or .height(), as has been stated.
Without jQuery, you can check the box-sizing in use and add up various paddings + borders + clientHeight, or you can use getComputedStyle:
var h = getComputedStyle(document.getElementById('someDiv')).height;
h will now be a string like a "53.825px".
And I can't find the reference, but I think I heard getComputedStyle() can be expensive, so it's probably not something you want to call on each window.onscroll event (but then, neither is jQuery's height()).
With MooTools:
$('someDiv').getSize().y
If i understood your question correctly, then maybe something like this would help:
function testDistance(node1, node2) {
/* get top position of node 1 */
let n1Pos = node1.offsetTop;
/* get height of node 1 */
let n1Height = node1.clientHeight;
/* get top position of node 2 */
let n2Pos = node2.offsetTop;
/* get height of node 2 */
let n2Height = node2.clientHeight;
/* add height of both nodes */
let heightTogether = n1Height + n2Height;
/* calculate distance from top of node 1 to bottom of node 2 */
let actualDistance = (n2Pos + n2Height) - n1Pos;
/* if the distance between top of node 1 and bottom of node 2
is bigger than their heights combined, than there is something between them */
if (actualDistance > heightTogether) {
/* do something here if they are not together */
console.log('they are not together');
} else {
/* do something here if they are together */
console.log('together');
}
}
Have you set the height in the css specifically? If you haven't you need to use offsetHeight; rather than height
var h = document.getElementById('someDiv').style.offsetHeight;

JS function - Math optimization, off by 1 in some cases

I'm still wet behind the ears with web dev, not the best at math, and have problems moving on when something is still broken. Hopefully you guys can help.
Quick: I'm using Jquery to make some (dynamic in number) divs in my header overlap by 30%, filling the entire width of the container. My current iteration rounds up one too many times, so my last element goes beneath the rest.
I have X elements filling the full width of my header container. Each element overlaps by 30% on either side. In an equation, I can work out the math no problem. Ensuring pixel precision with these numbers has proven more difficult. This is what I'm using to determine the width of each element.
width of element = [container width] / ((.7 * ([# of elements] - 1)) + 1)
left margin of element = [width of element] * .3
I make variables I call extraWidth and extraMargin which are the width and margin % 1 respectively. The default element width I use now is width-(width%1). For every element, I add the extraWidth and extraMargin to running total variables. Any time the total of either of these variables exceeds .5, that particular element has its width or margin set 1 higher than the default.
So I don't run on any longer, here's a JSFiddle with everything necessary to see what I'm dealing with. It runs fine most of the time, but at certain widths I'm 1 pixel too wide.
p.s.
Ran the JSFiddle, didn't work the same way as my live sandbox site, so check that out here. I feel like I included all the necessary bits, but I can't say for sure. On my Chrome, when window size is 575px (among many other widths) it's messed up.
EDIT
It should be noted that I'm making changes to my live site without updating this post. I'm not deleting any functions just yet though, just making new ones/minor alterations to existing ones.
Recursion! Recursion was the most elegant answer (which appears to work in ALL cases) I could come up with.
Iterating through my jQuery object one element at a time and calculating the width and margin based on the remaining container width rather than the whole container width makes this much easier to calculate.
function circleWidth(circles, containerWidth) {
var width = containerWidth / ((.7 * (circles.length - 1)) + 1);
var pxWidth = Math.round(width);
var margin = width * .3;
var pxMargin = Math.round(margin);
$(circles[0]).css({
'width': pxWidth + "px",
'margin-left': "-" + pxMargin + "px"
});
containerWidth -= (pxWidth - pxMargin);
if (circles.length > 1) {
circleWidth(circles.slice(1), containerWidth);
}
}
function circleSize(circles, containerWidth) {
var height = Math.ceil(containerWidth / ((.7 * (circles.length - 1)) + 1));
circles.each(function() {
$(this).css({
'height': height + "px"
});
});
circleWidth(circles, containerWidth);
$(circles[circles.length]).css({
'margin-left': $(circles[0]).css('margin-left')
});
$(circles[0]).css({
'margin-left': 0
});
}
Here's the fiddle with my final result. I'm sure I still have some optimization to do, but at least it's working now.
You have 2 choices:
Calculate pixelMargin as next integer. like:
var pixelMargin = Math.ceil(circleMargin);
or you can use pixelMargin in %.
1st one worked for me.

EPUB3 Reflowable-Fixed Layout Dynamic Sizing of Text to Div

I have created this jsFiddle:
https://jsfiddle.net/j994tnu2/4/
if(textH < (parentH - deviation) || textH > (parentH + deviation)) {
text.style.transform = "scale(1, " + frameScale + ')';
//alert("transform");
}
https://jsfiddle.net/j994tnu2/5/
if(textH < (parentH - deviation) || textH > (parentH + deviation)) {
//text.style.transform = "scale(1, " + frameScale + ')';
//alert("transform");
}
Version 4 has 1 line uncommented which allows for a tranform: scale() of the div directly containing the text.
Version 5 has this 1 line commented which disallows this to happen.
My concern is that the way I've coded the text to resize is...
textScale1 = 0.78;
textScale2 = 1;
textScale3 = 1.4;
//textScale4 = fontSize2 / fontSize;
//applies the master frameScale once to a single style of a class
fontSize2 = Math.round(10 * fontSize2 * frameScale) / 10;
//uses the relative textScales to this element to style the rest
fontSize = Math.round(10 * fontSize2 / textScale1) / 10;
lineH = fontSize;
margin = Math.round(10 * fontSize2 / textScale2) / 10;
lineH2 = fontSize2;
margin2 = Math.round(10 * fontSize2 / textScale3) / 10;
by manually checking the font-size and margins of every element and changing them to a scale both relative to themselves in the text AND relative to the outer div container size. This is actually the good part which makes the text stay true to itself relative to the original format. However,
The problem I have is the difference between the onload = function and the addEventListener(resize, function). They are coded exactly the same but have "different" results.
If you resize the window you'll see that after about 3 resizes, the text fits the container on an absolute font-size level much more closely and has much less (or none at all) transform: scale() stretching or squashing.
But every time the onload = function gets called, the text will always be way too big or small for the container and will always get stretched or squashed by an unacceptable amount.
How can I code this up to make the font-sizes in the onload = function be true to the starting outer div height?
Thanks for looking into it.
EDIT: It's interesting. Commenting out the onload=function and letting the resize function do the first resize, you will get the exact same result of the onload=function. Which, consistency is good. But why does subsequent resizing increase the accuracy of the font-sizes? Even if I resize up and then back down to near the same spot the text will look less squished and more true to its proportions. The initial resize sucks. Why? How is it possible that it gains in accuracy over time?
So I've kept working at it and saw that the ratio of the outer text div to the inner text div (frameScale in the jsFiddle) would determine the percent deviation at the end. This is what I mean:
However far from 1.0, that frameScale would deviate would determine according to some odd exponential function how far the resultant frameScale was from 1. So if you started from 1 (meaning the outer text div was just as large as the inner text div) then the resultant ratio would also be one. If it was 1.3 then the ratio plummeted to 0.84. If it was 1.6 then it went to 0.72. If it was 2.00 then it went to 0.5 and so on. I couldn't figure it out so I decided to do a workaround.
If resizing it multiple times made the font-sizes more true then I decided to just resize it with the resizeListener function I was already calling. All I needed to do was resize to the grandparent element in the first part of the function and then resize to the parent element in the second part. The one kicker was that the grandparent-to-child height ratio could not be the same as the frameScale (parent-to-child). So I did this:
if(masterScale = frameScale) {
masterScale = masterScale - 0.5;
}
For whatever reason, 0.5 seems to work well. Maybe this will fail in many different situations but for now it's a good workaround. All I did was resize the container twice. Here is the jsFiddle:
https://jsfiddle.net/j994tnu2/6/
EDIT: This doesn't answer the question though. Why is the beginning frameScale's deviation from 1 determine increasing deviations from 1 after the transformation? If I wanted it perfect I could create an if() else if() tower adjusting for this all the way up to some ratio that wouldn't occur naturally:
1.0-1.04 >>> 1.0
1.05-1.29 >>> 0.94
1.3-1.34 >>> 0.84
1.35-1.6 >>> 0.8
1.61-??? >>> 0.7
1.99-??? >>> 0.49
For whatever reason, subtracting the amount needed to make 1.0 for the resultant ratio from the beginning ratio will adjust the resultant ratio to 1; which doesn't make sense. Here is a jsFiddle doing this very thing and with much better results than resizing to the grandparent element:
https://jsfiddle.net/j994tnu2/9/

Link two HTML divs' dimensions to each other?

If I have div A and div B, is there a way to say A.width = b.width = MAX(a.width, b.width) ? That is, whichever has the largest inner content would dictate how large both are.
The actual problem I'm trying to solve is with columns - left, middle, and right. I want the left and right to be the same fixed width (but this could vary depending on their content).
It is not possible to use CSS to achieve this. However, if there is a way to do it with a JS-based solution. Here I am using jQuery. Let's say you have two divs, with classes a and b respectively.
$(function() {
function equalizeSize($ele) {
if($ele.length > 1) {
// Let CSS automatically calculate natural width first
$ele.css({ width: 'auto' });
// And then we fetch the newly calculated widths
var maxWidth = Math.max.apply(Math, $ele.map(function(){ return $(this).outerWidth(); }).get());
$ele.css({ width: maxWidth });
}
}
// Run when DOM is ready
equalizeSize($('.a, .b'));
// Run again when viewport has been resized, which **may** affect your div width.
// This is optional, but good to have
// ps: You might want to look into throttling the resize function
$(window).resize(equalizeSize($('.a, .b')));
});
See proof-of-concept fiddle here: http://jsfiddle.net/teddyrised/N4MMg/
The advantages of this simple function:
Allows you to dictate what elements you want to equalize widths with.
Uses the .map() function to construct an array, which we then use Math.max.apply to get the maximum value in the array
Forces automatic calculation of width when the function first fires (especially when resizing the viewport)
Allows you to call to recalculate the size again, using the handler equalizeSize() when you change the content in the divs... you can call the function again, say, after an AJAX call that appends content to either element.
It is not very clear what you want from the description. but I can rewrite your code this way.
var properWidth = Math.max($("#a").width(), $("#b").width());
$("#a").css("width", properWidth + "px");
$("#b").css("width", properWidth + "px");
I am not sure if it is this kind of solution you want.
I'm not sure there is a way to do it like that. But why not make a default function to set the size:
function changeSize(w, h)
{
A.setAttribute('style', 'width:'+w+'; height:'+h);
b.setAttribute('style', 'width:'+w+'; height:'+h);
}
Working fiddle: http://jsfiddle.net/kychan/ER2zZ/

CSS - Wrap text along angle

I'm trying to get text to wrap along an angle. This illustrates what I'm trying to do better than I can describe it:
http://bdub.ca/angle.png http://bdub.ca/angle.png
I found a solution here but it uses a heck of a lot of empty floated divs to create the effect. I will need to do this a bunch of times on a page so it would be better to have a solution that is lighter in weight. JavaScript is okay if it's something that I can just run on page load to spare the DOM from an overload of extra elements.
My brainstorming for a JS solution got as far as trying to figure out how to wrap each line in a span and set the left margins of the spans successively larger. The caveat is that the text is a paragraph that will auto wrap to fit in the container - I unfortunately can't ask my client to insert line breaks from Wordpress - so to wrap each line in a span would involve somehow detecting the automatic line breaks using javascript.
Any suggestions?
It's very tricky in that you can't legitimately (w3c standards; monitor screen resolution size and privacy) detect the actual width of characters. You could set the font-size to a specific width and insert a line-break yourself when it comes close to the width. (in b4 monitor)
so css: .paraIncrement { font-size: 12pt; }
I'm a little rusty with javascript so let's psudo-code this one:
outputstr[] = array();
int index = 0;
int startmax = 80;
int innerCount = 0;
for (int i = 0; paragraph.length; i++) {
outputstr[index] += paragraph[i];
innercount++;
if (innercount == startmax) {
startmax -= 5; // how much you want the indent to shrink progressively.
innercount = 0;
index++;
}
}
for (int i = 0; i < output.length(); i++) {
echo '<span style="margin-left: '+(indentValue*i)+';">'+output[i]+'</span>';
}
This section expects the maximum length of the start to be 80 characters long, decrementing by 5 each time. Also if you want to be sure it doesn't break early, ensure the the css.overflow is set to hidden/visible.
Having an blank image (jagged like steps) you want inline on the left of the paragraph should do it.

Categories