Something that jumped at me, recently, was a rendering dilemma that browsers have to encounter, and gracefully handle, on a day-by-day basis with little, to no, standardization.
Take the following page for example. You have 4 floated divs, each with a width of 25%, contained within a parent div of width 50px. Here’s the question: How wide are each of the divs?
The problem lies in the fact that each div should be, approximately, 12.5px wide and since technology isn’t at a level where we can start rendering at the sub-pixel level we tend to have to round off the number. The problem then becomes: Which way do you round the number? Up, down, or a mixture of the two? I think the results will surprise you, as they did me.
Opera 9 |
Safari 3 |
IE 6 |
IE 7 |
Firefox 3 |
Firefox 2 |
We have three completely different camps here:
- Round the numbers down – Both Opera and Safari round down the widths of all the divs to 12px. This leaves a 2px gap (note the green) to the right of all the divs. If you’ve ever wondered why your nicely-aligned navigation doesn’t fill up the full contents of a container in these browsers, now you know why. On the plus side, at least you know what the width of these containers will all be the same, no matter what.
- Round the numbers up – Both Internet Explorer 6 and 7 round the widths of all the divs up to 13px. Doing this causes the floated divs to immediately wrap, breaking layouts. This is obviously wrong as it causes many safely-numbered layouts to break for no obvious reason.
- Round some numbers up and some down – Both Firefox 2 and 3 mix the rounding of the div widths to 12px and 13px. The mix of rounding is done as to provide an even result at the end (making it flush with the far edge). The obvious side effect is that the divs no longer have a consistent width to them (even though an equal width was specified by the CSS). Additionally, the reported (via a JavaScript computed style call, like offsetWidth) width of the element remains at its reported 12.5px not providing the user with any indication of which way the rounding is occurring. And to add another confusing wrench in the works: The order of which divs have a width of 13px or 12px has been flip-flopped in Firefox 3. This was done to improve efficiency and speed and seemingly little, to no, effect on general web site rendering.
I was talking this over with some Mozilla developers and David Baron explained the situation quite well:
We’re trying to meet a bunch of constraints that can’t all be satisfied at the same time (the proof is left as an exercise to the reader, though I may have actually written it out once in a Bugzilla comment):
1. 4 adjacent objects of width/height 25% (for example) starting at one edge of a container should end exactly at the other edge; there should never be an extra pixel in the container and they should never be wrapped due to being a pixel to wide
2. objects that are logically adjacent should always touch visually; there should never be a pixel gap or a pixel of overlap due to rounding error
3. objects given the same width should occupy the same number of pixels
4. object boundaries should always (visually) be aliased to a specific pixel boundary in the display (they should never be blurred)
The one [Mozilla] sacrifices is typically (3), except for borders where we sacrifice (1) by rounding the widths to pixel boundaries much earlier.
The especially strange part, in all of this, is that there’s really no right, or wrong, here. How this behavior is supposed to play out by the rendering engine isn’t dictated by the CSS specification, having it be left up to the implementation to render as it sees fit. Obviously the four guidelines outlined by David, above, could serve browsers well but they are forced to sacrifice at least one of them in order to meet most of them.
The whole situation is quite fuzzy and frustrating – I’m not sure what the best action is, moving forward, but at the very least it’s in the open now where we can think about it some more.
Zsolt (January 22, 2008 at 2:53 am)
In Opera 9.5beta build 9656 (which isn’t the latest) the numbers are rounded up and it looks more like IE.
Cloudream (January 22, 2008 at 3:02 am)
The demo page souce code is wrote in IE’s view style.(13px instead of 25%)
lockoom (January 22, 2008 at 3:11 am)
Are you sure your data are ok? Both my Operas – 9.25.8827 and 9.5.9745 on WinXP Pro rounds up. The page renders exactly like IE.
FF 2.0.0.11 and Safari nightly (webkit-27494, yeah quite old), both rounds up but rendering is different:
http://img151.imageshack.us/my.php?image=ff20011vs1.jpg
OS dependent ?
John Resig (January 22, 2008 at 3:12 am)
@Zsolt & Cloudream & lockoom: Sorry guys, I was messing with the demo and forgot to revert it afterwards. It should be fixed now (and I assume that the Opera beta behaves normally as well).
deadcabbit (January 22, 2008 at 3:19 am)
Thanks for this article, it’s good to see someone took his time and tested it. Once a designer really made me mad, when he said my layout is one pixel off when he switches to full screen – of course it was caused by rounding :)
Mark Kawakami (January 22, 2008 at 3:21 am)
Hey, shouldn’t these tests be performed in Standards Compliance mode as well as quirks mode?
lockoom (January 22, 2008 at 3:23 am)
Yes, it’s ok now. Rendering in Opera stable and weekly is the same.
Mathieu 'p01' Henri (January 22, 2008 at 3:52 am)
The offsetWidth and offsetLeft report interesting results:
browser : oW @ oL, ... -> furthest pixel from the left edge
Op9.5 b9721: 12 @ 0, 12 @ 12, 12 @ 24, 12 @ 36 -> 48
FF3b2 : 13 @ 0, 13 @ 13, 13 @ 25, 13 @ 38 -> 51
IE7 : 13 @ 0, 13 @ 13, 13 @ 26, 13 @ 0 -> 49 but wraps
Mathieu 'p01' Henri (January 22, 2008 at 3:54 am)
Note that by furthest pixel from the left edge, I meant, the “naive” ( or is it really naive ) result of the offsetWidth + offsetLeft of the last DIV.
Paolo Bonzini (January 22, 2008 at 4:52 am)
I’d try also with 13.5px; it might be that Opera and Safari are “rounding to even” which is what many functions in the C library do by default.
Tom B (January 22, 2008 at 5:39 am)
I encountered a similar rounding issue with css centering.
When setting
background:url() center something
or when usingmargin:x auto
and when one of the two elements you are trying to align has an odd width and the other an even width, each browser uses it’s own rounding method to calculate the center.I saw this when I created a design that needed to be accurate by the pixel and on every browser, and sometimes (in Opera for example) in the same browser but in different widths of the viewport, I had an offset of one pixel.
Ian M (January 22, 2008 at 5:49 am)
This would ideally be standardized in CSS itself.
Funnily enough, there’s an RFC put out by one of the WASP guys just now asking for suggestions for CSS3 – I suggest you add it here:
http://www.webstandards.org/2008/01/18/tell-the-css-wg-what-you-want-from-css3/
Michael_H (January 22, 2008 at 7:09 am)
The whole rounding issuse troubles me a little, for the simple reason that there is already a long standing accepted mathematical method of rounding numbers. If it is 5 or above round it up or if it is below 5 round it down.
I would content that if anything was added to CSS3, it should be that this simple rounding method should be used along with a specification. Yes that may leave us with yet more legacy problems but we will need to correct inconsistencies in the specification at some point .
Cheers
Michael_H
Anon (January 22, 2008 at 7:30 am)
There’s a nice explanation by Robert over at mozillazine about Firefox way of rendering. Doesn’t explain the odd values in javascript/DOM though.
http://weblogs.mozillazine.org/roc/archives/2008/01/subpixel_layout.html
Jeremy (January 22, 2008 at 9:07 am)
Great post John. It would be interesting to see some data about sub-pixel font-size rendering in different browsers. I tend to stay away from sub-pixel sizes if at all possible.
Peter_L (January 22, 2008 at 9:20 am)
Michael_H said:
“The whole rounding issuse troubles me a little, for the simple reason that there is already a long standing accepted mathematical method of rounding numbers…”
Michael – don’t you see that if you adopt this rounding scheme you get the Internet Explorer result, which is atrocious and should never be allowed to happen. Floats being made collectively larger than their container, against the expressed wish of the web-developer, is catastrophically worse than shaving a little off by rounding down.
Adopting standard rounding would be stupid, it doesn’t sounds like you’ve understood the issues here.
Simon Kaegi (January 22, 2008 at 10:03 am)
Interesting post.
It’s not just a rounding problem for .5. We also need to formalize what happens when you divide 14 pixels across 10 cells.
Anon (January 22, 2008 at 10:29 am)
I’d like to see a browser sacrificing David Baron’s number 4 above…
Maciek (January 22, 2008 at 10:53 am)
I ran into this problem with scriptaculous while working on the blinds/accordions on http://www.nasa.gov. The difference in subpixel calculations between IE and the other browsers caused some animations to do a “pixel-wiggle” in a really obvious way. Expanding elements with other elements underneath them were particularly affected — it was really obvious they were wiggling up and down by one pixel (which makes me surprised this issue hasn’t been covered before). Some debugging in Firebug revealed similar results to yours: Browsers were applying a mix of ceiling, floor, round, semi-round.. etc.
My quick+dirty hack was to clone scriptaculous’ Effect.Morph into a new class called RoundedMorph which would use Math.round to force all pixel sizes to the pixel grid instead of the subpixel grid. It doesn’t seem expensive or problematic at all, but it is a worrisome ambiguity in CSS.
Brian Cully (January 22, 2008 at 11:55 am)
Actually, I think there is a solution, which comes from some work in font rasterization. See the work here: http://www.antigrain.com/research/font_rasterization/
Andrew Dupont (January 22, 2008 at 1:37 pm)
John, what happens in IE7/Opera/FF3 when you zoom the page to 200%? Does it scale the rounded-off version, or does it realize that 12.5px = 25px?
I can’t be bothered to check this for myself, and since you’ve done all the legwork… :-)
Andrew Dupont (January 22, 2008 at 1:40 pm)
As a follow-up: Firefox 3, at least, renders things correctly at 200% (red border visible on all 4 boxes).
Boris (January 22, 2008 at 2:35 pm)
> I’d like to see a browser sacrificing David Baron’s number 4
> above…
The effect of doing that in this case would be that the boundary pixels of the “test” floats would be partially transparent. In other words, instead of the red lines you see now in Firefox you would instead see partially transparent red at all the boundaries, with green shining through.
For a simpler case:
<div style="width: 9px; background: red">
<div style="width: 50%; float: left; background: green"></div>
<div style="width: 50%; float: left; background: green"></div>
</div>
would have a 1px thick brownish stripe down the middle. That’s pretty undesirable as well. If there were no background on the parent, the white default background would be shining through, giving a lighter green stripe. Similar issues would arise with adjacent borders if the widths are not integer pixels, etc, etc.
Dropping constraint 3 basically makes simple cases work sanely. “Broken” complex cases can be produced no matter what approach one takes.
The solution is really to stop trying to outsmart the browser and just use subpixel positioning in your code and not worry about it. If something is claiming to be 12.5px wide, and you want to create something else that’s the same width, just set it to be 12.5px wide.
This does cause problems with APIs like offset*, which only return integer pixels. As roc says, you want to either use computed style or the bounding rect APIs instead.
Taka (January 22, 2008 at 4:52 pm)
Well, it is an interesting problem.
But it should not be solved by any standards. The situation is a mistake of the designer. Basic math teaches you that you can not divide 50 into four equal integers. A designer should be aware of this and should avoid this situation. Just make sure that the content of a container can be divided evenly.
If there are 4 boxes in a container, make sure that the width of the container is a multiple of 4.
Boris (January 22, 2008 at 5:44 pm)
Taka, what if the container is the viewport? Or if the width of the container is determined by some text? Or if its width is a percentage width?
If the container is some sort of fixed-width design element, what you propose works. But then of course you get layouts that don’t flow well at all.
Michael Plant (January 22, 2008 at 6:52 pm)
I’m going with TAKA on this one!
Sounds like this is a flaw in your transition from the initial design to layout. I don’t think we can say any designer would hand me a design and there be a box that is 50px wide and needs to be divided into 4 sections at 25%! What exactly would possess a developer to write a div width of 50px and then set 4 inner divs – or whatever – at 25% widths? I would hope he knows that 50 divided by four doesn’t equal even pixels and find the proper widths. Obviously in a pixel divided environment this would be a problem. I think this is a great example of why not to use percentages when they don’t divide evenly! (in case anyone didn’t already know that) Thanks for the good read!
Michael_H (January 23, 2008 at 5:49 am)
Peter_L wrote:
“Michael – don’t you see that if you adopt this rounding scheme you get the Internet Explorer result, which is atrocious and should never be allowed to happen. Floats being made collectively larger than their container, against the expressed wish of the web-developer, is catastrophically worse than shaving a little off by rounding down.
Adopting standard rounding would be stupid, it doesn’t sounds like you’ve understood the issues here.”
It wouldn’t be the first time that I have misunderstood an issue but my comments aren’t usual classed as stupid.
However my misunderstanding of the issue leads me to believe that any designer should have a correct set of specifications to work with and in my very humble opinion the rounding of element width/height should not be left to best interpretation of browser designers. This can only (and has) lead to a variation in implementation… bad. If we cannot agree to use the accepted mathematical method of rounding, perhaps you could enlighten as to how the rounding should implemented?
Personally I agree with others who have suggested that, if the layout is correctly implemented then the rounding problem will be a non issue (Taka and Michael Plant)… but then I my not be understanding the issue correctly.
I shall watch this blog with great expections
Humbly yours
Michael_H
Jake Archibald (January 23, 2008 at 7:49 am)
Michael_H, Taka, Michael Plant:
Although John’s example uses a fixed-width parent element, this problem is most noticeable when using a fluid-width parent element.
From a design point of view, I’d expect 4 elements of 25% width to equal the width of the parent, but I do see the problem of them differing by a pixel.
Rounding up is the wrong answer, the other two are acceptable compromises.
Jake.
Michael_H (January 23, 2008 at 8:17 am)
Jake Archibald wrote:
From a design point of view, I’d expect 4 elements of 25% width to equal the width of the parent,
Michael_H writes:
And from a design point of view I would agree but at this stage of development, computer do not process floating numbers accurately. Instead the computer returns an estimation of a computed floating point number. In other words rounding takes place and when your four elements do not divide equally into the size of the parent element some kind of rounding must take place. We just don’t seem to agree on how the rounding should be achieved.
Jake Archibald wrote:
but I do see the problem of them differing by a pixel.
Rounding up is the wrong answer, the other two are acceptable compromises.
Michael_H writes:
I am not advocating *rounding up* as the answer. What I did say was the I felt that adopting the mathematical method of rounding up or down, depending on the values, would be a way forward.
But at the end of the day I really do not care which way is best as long as it is implemented as standard across browsers. As you say the other solutions are both compromises and no matter which method is implemented as a standard as a compromise it will always have flaws.
Cheers
Michael_H
Michael_H (January 23, 2008 at 8:31 am)
Jake Archibald wrote:
Michael_H, Taka, Michael Plant:
Although John’s example uses a fixed-width parent element, this problem is most noticeable when using a fluid-width parent element.
Until computer technology advances we are stuck with this bug (for the want of a better term). Even if ’rounding down’ is adopted, as a lesser of two evils, your layout will still not be pixel perfect. I would therefore content that the designer should design the layout to avoid the possibility of uneven number division where possible.
With that said, I would like to thank John for researching and creating this article, which should prove valuable to many designers (both new and old) when designing their sites.
Cheers
Michael_H
Boris (January 23, 2008 at 12:36 pm)
I’m still missing something in these last few comments. Given that in a browser which drops constraint 3 everything works as long as _no_code_ rounds (that is, as long as the web page uses fractional pixels as needed), what exactly is the problem? The only issue I see is that the offset* APIs round, and the solution there is to not use them….
Michael_H (January 23, 2008 at 1:27 pm)
Boris wrote:
I’m still missing something in these last few comments. Given that in a browser which drops constraint 3 everything works as long as _no_code_ rounds (that is, as long as the web page uses fractional pixels as needed), what exactly is the problem?
Michael_H writes:
There is no problem Boris. I was merely offering my views, which can be ignored at will…. I will not be offended
I agree that dropping constraint 3 is one way of solving the issue but it still does not specify how the browser designers should implement the rounding. I have been suitable convinced that simply adopting the mathematical method will not serve as a solution to maintaining the integrity of a layout. I still however do not believe that it should be left open to interpretation, as this will only lead to inconsistencies in display across browsers.And yes as web designers/developer we have little alternative only to accept what is on offer but someone mentioned commenting on the CSS3 draft, which I feel should give guidance on this issue.
Boris wrote:
The only issue I see is that the offset* APIs round, and the solution there is to not use them….
Michael_H writes:
Agreed
OK, I think I have said enough for now, so I’ll step aside and make room for others.
Cheers
Michael_H
Jörn Zaefferer (January 23, 2008 at 2:51 pm)
Thanks John for bringing this out in the light. Now I finally may be able to get rid of pixel-flickering on animated accordions…
Currently the result is this:
step: function(now) {
var current = (hideHeight - now) * difference;
if ($.browser.msie || $.browser.opera) {
current = Math.ceil(current);
}
settings.toShow.height( current );
},
Robert Nyman (January 23, 2008 at 3:19 pm)
Thanks for posting this! Very interesting read, and it explains some of the demons I’ve had to fight.
Jack Sleight (January 24, 2008 at 9:56 am)
Excellent article. I’ve been aware of this for a while (particularly in IE) but never really looked into it. I certainly didn’t realise Opera and Safari always round down, that seems terribly wrong, and it’s certainly something that needs standardizing.
I think Firefox’s behaviour is the most logical, as long as the different widths are consistently different ie. if you have four columns at 25% and under that two columns at 50%, the middle lines should line up perfectly.
If anyone’s interested, I recently found a good way to “fix” IE’s behaviour without having to adjust the width values: http://jacksleight.com/blog/2008/01/20/ie-floated-columns-totaling-100-percent-bug
Brian (February 1, 2008 at 2:47 pm)
A similar problem occurs with em rounding and em-based grids during font resizing.
Thomas Baekdal (February 1, 2008 at 7:00 pm)
A simple way to fix this is to set both the “left” and “right” CSS property instead of using “width” – like this:
http://www.baekdal.com/x/round.asp
Works in Firefox, Safari, Opera and IE7
It will keep the boxes inside the parent container, and touch the edge in both sides. It doesn’t work in IE6, but that can be fixed using conditional formatting <!–[if IE 6.0]>
But, I agree generally agree with Taka, as a web designer, you should not get yourself in this situation in the first place.
matt (February 2, 2008 at 12:01 am)
@ Thomas: not really, becsue then you have to position the element absolutely which is not at all flexible and is not really reliable method for layouts… perhaps application layouts, or specific needs, etc… (like this simple box illustration).. but for a layout in general, absolute positioning is never a “simple way” to fix anything… ;) hehehe
Thomas Baekdal (February 2, 2008 at 6:57 am)
@Matt: Well there are probably situations where absolute positioning will get in the way, but I am using “absolute” for 99% of all the work I do, and I rarely experience any problems. I find it much more flexible than using floats.
matt (February 2, 2008 at 1:17 pm)
@thomas: how does one resolve things like an element to clear the abolutely positioned column? say you want a footer to drop below two absolute containers…. absolute elements do not “push” other content because they do not “take up space” in the physics of the DOM… without javascript, i’m hard pressed to come to a solution to this problem…. would be very interested to hear your insight on techniques…
Adrian Turner (February 2, 2008 at 9:01 pm)
Thanks for the post. This explains why I’ve been so fustrated with my slicing across browsers.
Chris Hester (February 4, 2008 at 5:59 am)
Great article! It should be interesting to see if IE8 also rounds up.
Firefox 3 changes slightly as you zoom in, as does IE7 (less so).
BTW, when I test in IE7 on XP I only see one line of green, with the last blue div overlapping the text. Did you update the demo after making the screenshots?
Chris Hester (February 4, 2008 at 6:01 am)
Ah wait, sorry, I was testing it with an XHTML Strict doctype added.
Niels Matthijs (February 5, 2008 at 6:24 am)
Good post!
I’ve hit this problem quite a few times myself, drawing the same conclusions you did.
My view on things is that there should be a way to compute all values relative to the main container. Meaning that whatever the rounding method is, all elements together fill up the entire width of the container.
It’s something I’ve been thinking about myself (and should write about), but the way widths are currently handled is a bit of a drag. If you have a div of 50px wide and you have a left column of 15px there is no real need to state the width of the right col. There needs to be a way to just “fill it up”. Same goes for the procentual widths in your example.
It doesn’t really matter how the first three cols are rounded, but the fourth col should just fill up the remaining space. You will get some minor layout differences, but none as worse as those above.
In a related matter, I once wrote an article how to try and fill up that remaining space using css techniques that are currently available. Ain’t easy to do cross-browser :)
http://www.onderhond.com/blog/work/spreading-list-items
keyrocks (February 5, 2008 at 10:15 am)
Would it not be more practical to adjust the width of the outer container so that it become divisible in whole numbers rather than fractions, then divide the innards equally in whole numbers? Would this not eliminate the need to deal with rounding up or down issues?
It seems to have worked for me in previous situations of this type.
Chris Akers (February 6, 2008 at 2:42 pm)
This problem is especially apparent in liquid layouts. Right now I’m fighting with Firefox because I have a 25% column and in that column I have a 100% table. The computed-style width for the column is 310.733px. The computed-style width for the table is 311px. Guess what that means?! Horizontal scrollbar! Yea!
That’s not the worst part though. The scrollbar thumb is the entire width of the scrollbar. So it doesn’t even allow a 1px scroll. It just sits there, mocking me…
And this problem happens every fourth pixel when changing width. So these scrollbars appear and disappear when resizing the window nearly causing epileptic seizures…
In the past I have used the following logic:
div1 = round(50/4) = 13 [37 left]
div2 = round(37/3) = 12 [25 left]
div3 = round(25/2) = 13 [12 left]
div4 = round(12/1) = 12 [ 0 left]
The benefits of this method is that there will be no more than a 1px difference in the width of the widest and skinniest divs no matter how many divs are in the equation. Also, any 100% width items inside the divs will have a concrete width off which to calculate their width.
I guess I’ll have to pull out that old code now…
Paul Walker (March 6, 2008 at 1:55 am)
IEs fix for the problem breaks layouts terribly, so its out.
Safari & opera, and firefox’s methods all seem ok to me – assuming Firefox is in examples in which there are an odd number of elements, firefox is rounding down more elements than it is up, for otherwise it would turn out like IE.
I’d prefer sacrificing #4: object boundaries should always (visually) be aliased to a specific pixel boundary in the display (they should never be blurred) but anything other than 1 seems OK to me.
Sindre Aa (March 25, 2008 at 7:38 am)
There is a bigger problem with opera (9.26 on mac). It rounds down percentage-widths, before calculating the actual width of the element. The roundings become very large and apparent on large sizes. Take this example:
<div style="width: 1000px"><div style="width: 99.9%;">I am 990px wide?</div></div>
The inner block above should (obviously) be 999px wide. Safari and Firefox displays this correctly. Opera however round the percentage down to 99% before it calculates the width, so inner block is rendered as being 990px. This is a big problem when dealing with elements of dynamic width in a dynamic layout…
gregg (May 28, 2008 at 4:07 am)
had a look at what you say for ie6, but that is one of my browsers, and well if i set a parent div to 50px; and add 4 divs of 25% inside that – all floated left; well the fit perfectly, that is until i screw with the margins,padding,borders.
parag desai (June 10, 2008 at 8:30 am)
You can try to fix problem by using Other Alternate Browsers, if the problem is really with your Internet Browser .