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.
Related
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/
I've used this script to get a width and then apply it to the same element:
$(document).ready(function(){
$(".equal-height").each(function(){
var slideSize = $(this).outerWidth();
console.log(slideSize);
$(this).css({ "height" : slideSize + "px" });
});
});
However, for some reason, there are elements that sometimes don't get a matching width and height. As you can see in this fiddle: https://jsfiddle.net/cpfgtuzo/5/ the last element has the correct dimensions, but the rest are all higher than their width. I'm trying to get them all to be square and I need to support old browsers too.
This only seems to be an issue when the size of the window is smaller and the four items stack in into a 2 column.
After your comments on other answer,
I've got numerous elements on the page that use this script and not all are the same size (there are smaller sets of squares too)
And
but the resulting box isn't coming out square. If you inspect your fiddle, the boxes are ~10px too tall
This solution seems to overcome those problems, Let me know if this helps, Working Fiddle
$(document).ready(function () {
$(".equal-height").each(function () {
var slideSize = $(this).outerWidth();
console.log(slideSize);
$(this).css({ "height": slideSize + "px", "width": slideSize + "px" });
//setting the width along with height
});
});
Hmm I think there is a bug with jQuery, when it is calculating the width of each element.
Take this jsfiddle as a workaround.
// Height = Width
$(document).ready(function() {
var width = $('.wide-content').width();
// prevent jQuery from calculating the width of children
$('.wide-content').hide();
// $(".equal-height").width() is now 50
var slideSize = $(".equal-height").width() * width / 100;
$(".equal-height").each(function() {
$(this).css({
"height": slideSize + "px"
});
});
$('.wide-content').show();
});
Same height on all elements
I'd just use the first box to calculate the height for the other elements - atleast this would ensure the same height. Also it would get rid of that loop..
$(".equal-height").css({"height" : $(".equal-height").eq(0).outerWidth()});
One line to rule them all
But, since this did not fit the scope..
Making squares of differently sized elements
After some messing about it's clear that the widths are returned wrong only after you start tampering with the height. This can easily be tested by commenting out the css-command that sets the height and observing the output.
The solution seems to be to set a predefined height:
.link-quarters {
width: 25%;
height: 100px;
float: left;
...
This will return the exact same width on all elements (the important part) and you can then set the corresponding height programtically without issues.
There's still an issue with rounding though, as the percentage-width might result in decimal numbers that gets rounded by the browser (or js).
https://jsfiddle.net/cpfgtuzo/17/
I've been working on a project and I've noticed some inconsistency in bootstrap's behavior that I would like to solve.
When a popover (or tooltip, whatever, they're basically the same) is nearing the edge of the screen - if it's a right-sided one, when nearing the edge - it will contract so as not to go offscreen (it only works up to a point, but that's usually enough).
This doesn't happen when the placement is to the left.
i.e.:
right placement:
Normal width:
Close to the edge:
left placement:
Normal width:
close to the edge:
These images are from a small DEMO I wrote to illustrate the problem.
I've messed around with the source code, so far to no avail. I can't seem to place my finger on what exactly causes this behavior in the first place.
Any ideas?
p.s.
I'm using Bootstrap 3.1.1. The new 3.2 does not solve the issue (and I would like to avoid upgrading at this point).
Major Update!
After some digging, I figured out that this has nothing to do with bootstrap - it's simple css - it seems that when you position an element absolutely and push it to the sides it will try and stay withing the screen.
I never knew about this behavior, and it happens automatically - but only to the the direction you're pushing - i.e. a div pushed from the left will contract when reaching the right edge of the screen and vice versa.
It just so happens that popovers are only positioned with the left assignment - which is why we're seeing the inconsistend behavior - when it's pushed to the right it contracts but not the other direction.
So the solution is to assign right instead - sounds simple?
Not so much. I took the source code and manipulated it a bit, adding these lines (somewhere arond line 250 in the jsfiddle):
if (placement == 'left' && offset.left < 0) {
var right = ( $(window).width() + 10 ) - ( offset.left + actualWidth );
offset.left = 0;
$tip.offset(offset);
$tip.css({ 'right': right });
}
Seems reasonable, right? If the offset to the left is less than 0 (i.e., it goes offscreen) then calculate the window width and remove from that the left offset and the width of the popover (actualWidth) itself and you get the distance from the right.
Then, make sure to reset the offset left and apply the right positioning. But... it only sorta works - which is to say it only works the second time around.
Check it out for yourself! Hover once, and it's misplaced, pull the mouse to the side and try again and suddenly it's positioned correctly. What the hell?
edit
Ok this seems to come up a lot, so I'll make it clear:
I know about auto placement. I don't want it. I want to control where the popover goes, letting it decide automatically is not a solution to the problem, it's merely avoiding it
Ok, I've gotten a little closer.
Once you assign the right in css, the actualWidth and actualHeight will change, so you need to update those variables. Around line 253 in your jsfiddle:
if (placement == 'left' && offset.left < 0) {
var right = ( $(window).width() + 10) - ( offset.left + actualWidth );
offset.left = 0;
$tip.offset(offset);
$tip.css({ 'right': right });
actualWidth = $tip[0].offsetWidth;
actualHeight = $tip[0].offsetHeight;
}
This works the first time you hover, but every time after that, it sets the top to be too high, so you can't read the top of the tooltip.
UPDATE:
It appears that having the right value set is messing up the positioning in the applyPlacement function. To fix this, clear the right value before setting the initial actualWidth and actualHeight (around line 225):
$tip.css({ 'right': '' });
// check to see if placing tip in new offset caused the tip to resize itself
var actualWidth = $tip[0].offsetWidth
var actualHeight = $tip[0].offsetHeight
I believe this has a lot to do with the browser/client that accesses the webpage. For instance, in order to display the tip's on the proper side (not bunched up or illegible off the the left or right), determine the offsetLeft & offsetTop of the object element with javascript and place it accordingly. You could have different pages for different resolutions.
CSS example for a screen width from 400-720 pixels:
#media screen and (min-width:400px) and (max-width:721px)
some pseudo code:
if (this.offsetLeft < 200) //if there's not enough room to display the tip
tip.offsetLeft += 200;
I think you've basically got it, it's working fine for me.
Just add in the minimum width detection so that it doesn't go too small.
if (/bottom|top/.test(placement)) {
var delta = 0
if (offset.left < 0) {
delta = offset.left * -2
offset.left = 0
$tip.offset(offset)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
}
this.replaceArrow(delta - width + actualWidth, actualWidth, 'left');
} else {
if (placement == 'left' && offset.left < 0) {
var right = ( $(window).width() + 10) - ( offset.left + actualWidth );
offset.left = 0;
$tip.offset(offset);
$tip.css({ 'right': right });
}
this.replaceArrow(actualHeight - height, actualHeight, 'top');
}
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.
Having a bit of an issue please see the following code:
window.onload = function () {
var imgHeight = $("#profile_img").height();
var infoPanels = imgHeight - 6;
//include borders and margin etc..
var infoPanelsHeight = infoPanels / 4;
$('.resize').css("height",infoPanelsHeight + "px");
$('.resize2').css("height",infoPanelsHeight + "px");
}
What i’m trying to do is find the height of an image (floated:left), then divide it by 4 and use the outcome to set the height of 4 divs (floated:right), so they equal the height of the image in total.
I’m using this on a resizing project of mine but because the image height depends on the viewing window (in this case a mobile screen), the number is very rarely rounded up correctly so the divs are always out by 1-4 px.
So for a work around I want to find the height of the image, then if the height isn’t dividable by 4 adjust so it is... resize the image then resize the divs using the new image height.
So my question is how do i check the height of the image, if it isn’t dividable by 4 then make it so it is?
I’m using jquery and javascript generally.
Thanks for your help in advance.
Sam Tassell.
I would try:
if (imgHeight % 4 != 0) { // checks if the imgHeight is not dividable by 4
$("#profile_img").attr("height") = Math.floor(imgHeight / 4) * 4; // set lowest height that is dividable by 4
}
Note:
The image may become a little blurry because the result depends on your browser capabilities.
% is called modulus operator
You need the mod operator %:
imgHeight % 4
if the result is not '0' then you know imgHeight is not divisible by 4.