Firefox sum of widths with jquery exceeds expected value - javascript

Firefox/Safari/Chrome all latest stable releases on macOS as of 12 May.
jQuery v3.1.0
I've a jquery script that dynamically resizes images (specifically, a container div which has an img element filling it 100%) inside a collection container div to give a masonry wall-like effect. The logic, heavily summarized, is that I calculate images at their preferred size for the current window width and add them up (including gutters) until they exceed that window width. I then subtract the last element and scale the remainder up to fit.
This works fine in Chrome and Safari. Firefox works on most rows but on occasion the last element gets bumped to the next row and throws the whole composition off. The culprit, on inspection, of the guilty rows appears to be that the sum of all the row elements exceeds the collection container by 1 pixel at most, never more. It's usually a fraction of a pixel. My hunch is that it's a rounding or decimal place issue; or an issue related to the floating point that I've seen come up in my research, like here.
Code:
Forgive me if I leave out anything pertinent here in the following code. I feel as though the relevance is in the calculations of the various elements widths through jquery (particularly, my scaleFactor result) so that should be enough and leaves this a bit more legible:
Calculations made in the script:
// Once it's been determined that a row has the required number of elements to fill it:
var windowwidth = $(window).width();
var gutter = 23;
var scaleFactor = (windowWidth - (sum of total gutters in row)) / (sum of total element widths in row);
// Looping through each element, resize them as such:
var w = $(this).width();
var h = $(this).height();
// Set new width and height
$(this).css({'width': (w * scaleFactor) + 'px'});
$(this).css({'height': (h * scaleFactor) + 'px'});
Result:
Chrome and Safari produce a row where the sum of all gutters and elements is consistently just under the window width by a fraction, giving visually perfect spacing, like so:
(23) + 936.094 + (23) + 415.859 + (23) // gives a sum of 1,420.953 for a window width of 1,421
Firefox, on inspecting a row where the last element was bumped off to the next row, gives the same example as follows:
(23) + 938.9 + (23) + 417.117 + (23) // gives a sum of 1,425.017 for a window width of 1,425
Additional Info:
I've given the img elements, their container divs and the content
container div box-sizing values of border-box in css to try and rule
out any styling discrepencies in the width values on each browser.
None had any padding or borders, regardless.
The content container div I mention here holds all the elements being arranged into rows. It is the width of the window less one gutter, which it uses as a margin. I've accounted for it in all calculations and am pretty confident I've done so appropriately as the sums would be out by a lot more than a fraction of a pixel if I've overlooked something more. I deliberately scale the elements to the window width, not the content container (its main purpose is to provide an opposing margin to the side each element has to give consistent and equal gutters around each element) but have tested with scaling to the content container and it gives the same outcome.
I've also confirmed that the width values I'm using here: (sum of
total element widths in row) is consistent across all browsers and
was sourced from jquery's width() function for each element. The
value for the window size on all browsers differs slightly - I
imagine due to scrollbar differences - but I can only assume it's
accurate. I've used other methods to get the window width without
scrollbars and they confirmed (to the best of my knowledge) that the
value being used is correct.
I've attempted rounding various parts and this confirmed my
presumption that it would not fix the problem when the sum of widths
exceeds the container by more than 0.5.
The measurements taken are done after all content is finished loading
and scrollbars are in place and accounted for, using the $( window
).on( "load", function() function.
Optimal Solution:
Ideally, I'm hoping I've overlooked an aspect of the calculation in the code, above, that can be adjusted to ensure that the same variables would produce the same results across browsers.
It's likely wishful thinking so my plan b would be an elegant means of compensating for this. Currently I could compare the window width with the sum total after the calculation and scale it back. Or I could forego precise gutters and aim to scale to the window width, less one pixel.
The problem with the first idea is that it seems like a poor design to patch a simple calculation that should work. It wastes lines of code and overhead and I avoid bloat as best as I can. The second solution annoys the perfectionist in me and could have long-term complications if I scale the project in a manner that's reliant on certain believed truths, such as even gutters throughout the layout.
Update:
I hadn't ruled out rounding various floats in the calculation just yet but was doing it mostly by guessing. My main lead was attempting to do on the widthBefore var as it was sometimes a float, whereas all the other vars in scaleFactor were integers. Round, as before, didn't work. To my surprise, floor didn't work. I thought it would leave minutely visible edge differences in spacing but it still left rows exceeding the window width. To my further surprise ceiling worked. I can't actually explain why Firefox needed me to round this value with ceiling to work but it's doing well with everything I throw at it so far. I still have to see what the repercussions are for gutter sizes now.

I can't fully explain the logic behind it. Likely, it would require someone knowing the subtler differences of maths calculations between the browsers, but rounding widthBefore by ceiling kept the same results in Chrome and Safari and, as per my question, provided the same results in Firefox.

Related

Where is Element.clientWidth with fractional number?

MDN says that Element.clientWidth "will round the value to an integer" and I should use Element.getBoundingClientRect() instead for a fractional value. While the part with the fractional value is correct, that function has a different meaning and cannot be compared.
clientWidth gives me the inner visible width without the vertical scrollbar. getBoundingClientRect gives me the outer width including the vertical scrollbar. These are entirely different sizes.
Is this intentional? Does the MDN article know about that difference? Should that restriction be added to the cross-link? What else can I do to get the desired value, which is the visible width with a fractional value?
My problem is that I'm writing code that hides elements that won't fit in the current view, depending on the view width and the elements' width. Therefore I sum up all elements' widths and compare it with the space I have. At certain window sizes, that can lead to the element width sum of 945.4 (that fits in) and the clientWidth giving me 945 (same value but rounded down, so it's less). getBoundingClientRect however gives me 951.2 in this case which is more than is actually available for the content. In consequence, a horizontal scrollbar appears which I don't want.

Get actual content height with Javascript (or jQuery)

There are many ways of getting the height of a document, but due to different browser implementations most of them (I believe) return the highest of a number of values ... which is fine most of the time.
In my case I have a number of elements on the page that I know to be a smaller height than the window and viewport heights. What I'm trying to get is the actual height of all the rendered elements.
Things that don't work (with testing in Firefox):
$(document).height(); // gives the window height
document.body.scrollHeight; // gives 7, its always 7 I don't know why
document.body.offsetHeight; // also gives 7
document.documentElement.clientHeight; // sometimes gives window height
document.documentElement.scrollHeight; //gives window height
document.documentElement.offsetHeight; generally gives a value in the range of 23
At present I'm thinking that the way around this might be to insert a div with height: 0 at the bottom of my page and grab $(div).offset().top, but I feel that this is highly likely to go wrong at some point in the future.
So before I do that ...
Is there a way of knowing the content height when it's less than the window height?
EDIT:
People have asked for clarification. Heres a jsFiddle example of what I want / the results I'm getting.
https://jsfiddle.net/8Lu2zcw8/1/
Running that results in the same value for Win Height: and Doc Height being written out to the console.
EDIT2:
My issue was due to the body not wrapping the content correctly due to floated and absolutely positioned elements, as pointed out by #tim-vermaelen in the comments to his solution.
I suggest you use $(document.body).height().
In CSS you have to put:
html,
body { height: 100%; }
This will only give correct results in case of body padding, margin and borders of the body element are 0. When direct children are floating or put on position absolute, the height of these elements doesn't count. Hence for floating elements you always clearfix the parent to solve these wrapping issues.
If not you can use $(document.body).outerHeight(includeMargin)
$(document).height() will give you content size, not window's. If it gives you window's size, then you probably messed up your CSS. Also, you can try $('body').height()

Uniformly distribute elements

I need to calculate the position of the elements with variable width and height within an element with a fixed width.
It's like the Pinterest board, but with variable element width (it's not determined by block's content, in my case there's two sizes of the block - 170x140 or 140x170), so i cannot use columns.
Here's the visual example of the desired result
Floating is not an option as it produces gaps that are too large.
display:inline and display:inline-block also does not produce the required result.
It seems like there's a mathematical algorithm, how each box position is calculated?
Calculations will be done in Javascript.

Javascript mobile - calculate element dimensions in pixels based on screen dimensions

I want to calculate the dimensions of certain elements (img, ul, div, etc.) based on screen size. I can't to use percent values. I need pixel values. I also don't want to 'hardcode' everything using media queries and a new set of images for every resolution or screen size.
I thought about making this using screen size. I only need width calculation. So I add the initial width of my images and some initial space between them -> total width, and I then get scaling factor using: screenwidth / totalwidth
Now I scale all of my images and also the space between with this factor.
It's a very simple layout, only a few images and HTML elements. So this scaling should not be expensive.
It would work if the devices gave me reliable width measure for the screen. But depending of the device, I get a different meaning of this value. I'm using screen.width
In some cases screen.width is what the currently width is - in portrait it's a small value, in landscape a large one. But in other ones, width is always the same - the value which is defined as device's width.
So how do I scale my layout according to what's currently screen width in a consistent way with rotation, and without CSS % values? Is this an acceptable way to do layout scaling or am doing no-go?
Edit: I have to add more details after trying Jasper's solution. The images are used in a slider. The slider is basically an UL and each LI contains an image with float:left - so all the images are appended horizontally one after the other. With overflow hidden and stuff only the current slide is visible. Now, the official width of the UL is the sum of the width of all contained LIs. And this means, at least with my current state of knowledge, that I can't use percentage size for the LI elements, because if I did, this will be % of this total width of the UL, which is very large, and I end with immense LI elements/images.
Isn't there any reliable way to get current screen width for all devices ? I already have working code, I only need that the value of screen width is correct.
New update
Look here is a similar approach to what I'm trying to do:
http://ryangillespie.com/phonegap.php#/phonegap.php?
Entry of June 18, 2011 "One Screen Resolution to Rule Them All"
I tried also with exactly that example, copy pasting it in my code. But it doesn't work either. window.outerWidth has the same problems as I'm describing for screen.width (as well as JQuery $('body').width()). It works as long as the device isn't rotated - it initializes well. But at the first rotation, depending of the device, I get problems. In some it works as expected, in others it interchanges the values, so that I get large width in portrait mode and short in landscape, in others it gives fixed width and height all time, in others it doesn't rotate at all....
This is most likely accomplish-able with CSS alone (which is usually good for performance):
img {
width : 100%;
height : auto;
}
That will keep all the image's aspect ratios but re-size them to 100% width. Now that width is set based on the image's parent element(s) width. If you are using jQuery Mobile then the data-role="content" elements have a 15px padding, so to remove that you can just add a container to the image elements that removes the padding:
HTML --
<div class="remove-page-margins">
<img src="http://chachatelier.fr/programmation/images/mozodojo-mosaic-image.jpg" />
</div>
CSS --
.remove-page-margins {
margin : 0 -15px;
}​
And walaa, you've got responsive images without loads of code or overhead.
Here is a demo using a container and not using a container: http://jsfiddle.net/EVF4w/
Coincidentally I found that this works:
$(window).resize(function() {
updateScaling($('body').width());
});
This is always called and passes correct width. As far as I remember it also works with screen.width
In updateScaling I calculate a scalingFactor and adjust my elements.
I tried out responsive CSS, media queries and so on, but at some point it didn't make sense anymore, because I have anyways to recalculate the margin of slider's UL based on current slide and new width - and other stuff which needs script. So I made everything with script.
I removed window.onorientationchange.

How do you find the largest font size that won't break a given text?

I'm trying to use CSS (under #media print) and JavaScript to print a one-page document with a given piece of text made as large as possible while still fitting inside a given width. The length of the text is not known beforehand, so simply using a fixed-width font is not an option.
To put it another way, I'm looking for proper resizing, so that, for example, "IIIII" would come out in a much larger font size than "WWWWW" because "I" is much skinnier than "W" in a variable-width font.
The closest I've been able to get with this is using JavaScript to try various font sizes until the clientWidth is small enough. This works well enough for screen media, but when you switch to print media, is there any guarantee that the 90 DPI I appear to get on my system (i.e., I put the margins to 0.5in either side, and for a text resized so that it fits just within that, I get about 675 for clientWidth) will be the same anywhere else? How does a browser decide what DPI to use when converting from pixel measurements? Is there any way I can access this information using JavaScript?
I would love it if this were just a CSS3 feature (font-size:max-for-width(7.5in)) but if it is, I haven't been able to find it.
The CSS font-size property accepts length units that include absolute measurements in inches or centimeters:
Absolute length units are highly dependent on the output medium, and
so are less useful than relative units. The following absolute units
are available:
in (inches; 1in=2.54cm)
cm (centimeters; 1cm=10mm)
mm (millimeters)
pt (points; 1pt=1/72in)
pc (picas; 1pc=12pt)
Since you don't know how many characters your text is yet, you may need to use a combination of javascript and CSS in order to dynamically set the font-size property correctly. For example, take the length of the string in characters, and divide 8.5 (assuming you're expecting US letter size paper) by the number of characters and that gives you the size in inches to set the font-size to for that chunk of text. Tested the font-size with absolute measurements in Firefox, Safari, and IE6 so it should be pretty portable. Hope that helps.
EDIT: Note that you may also need to play around with settings such as the letter-spacing property as well and experiment with what font you use, since the font-size setting isn't really the width of the letters, which will be different based on letter-spacing, and font, proportional to length. Oh, and using a monospace font helps ;)
I don't know of a way to do this in CSS. I think your best bet would be to use Javascript:
Put the text in a div
Get the dimensions of the div
Make the text smaller if necessary
Go back to step 2 until the text is small enough
Here's some sample code to detect the size of the div.
Here's some code I ended up using, in case someone might find it useful. All you need to do is make the outer DIV the size you want in inches.
function make_big(id) // must be an inline element inside a block-level element
{
var e = document.getElementById(id);
e.style.whiteSpace = 'nowrap';
e.style.textAlign = 'center';
var max = e.parentNode.scrollWidth - 4; // a little padding
e.style.fontSize = (max / 4) + 'px'; // make a guess, then we'll use the resulting ratio
e.style.fontSize = (max / (e.scrollWidth / parseFloat(e.style.fontSize))) + 'px';
e.style.display = 'block'; // so centering takes effect
}

Categories