Determination of algorithms used for percentage based rounding divs on browsers and CSS Frameworks

Posted on August 28, 2009 by Fernando Trasviña

Categories: ElasticGeneral

Introduction

In current HTML codification trends, developers prefer construct HTML documents, pages and applications based on columns, against the table technique of row based design. This techniques commonly include the use of floated divs.

With the complexity and inconsistency between browsers implementation of standards, and in order to provide more solid code bases, many developers targeted this layout problems with CSS Frameworks, almost all of them based on columns, and with a very different set of approaches and features. The second generation of CSS Frameworks are a great advance in CSS code standardization, they are providing a richer set of features and a great flexibility to accomplish high complexity designs.

The first generation of CSS Frameworks provided fixed width capabilities, and very limited or null nesting capabilities. This frameworks increased the time of development but only at the very high level of design, leaving the developers alone for most of the job. Even when they helped a lot, there was not a complete solution.

In the second generation of CSS Frameworks we have complex nesting, layout width variability, and support for fixed, liquid, and elastic layouts, But the transition has not been by any means smooth.

Going from fixed, to liquid layouts present a very complex challenge to framework developers. The resulting width of columns are commonly float values, W3C has not created a description or a standard solution to solve this problem, leaving the solution up to the browser developers.

The purpose of this study is determine what solutions we currently have, from Browsers and CSS Frameworks.

Hypothesis

Making a wide set of test, varying the number of columns of a block, the algorithm used to get to the rendered layout can be determined by the analysis of the rendered pixels with an image editor.

This study will help to determine if the current Elastic CSS Framework Engine is a good solution for sub pixel rounding, and how this engine compares to the browsers and other frameworks.

Experiment Design

A set of blocks of same width, every block, will vary the number of columns from 1 to 10, and an extra block of 15 columns, to test a non continuous block.

Every block is paired with a set of columns equal to the test created with Elastic CSS Framework to serve as control. All tests have a background of alternating pixel colors to simplify measurements.

All testing blocks must lead to a float measure for columns in order to test the rounding of those float values (183px).

The test were run on mayor browsers, and a screen shot will be taken.

Every column will have a 2px padding to provide clear separation between columns, and a solid background color to be measured with image editing software.

Measures will be done with gimp, and then the results will be analyzed in order to try to infer the underlying algorithm.

Experimentation

Test were run on different platforms, and browsers.

  • Safari 4.0 Final (Mac)
  • Firefox 3.0 (Linux)
  • Opera 9.64 (Linux)
  • Internet Explorer 7 (Windows)

Results

Special considerations, Elastic rounding algorithm leads to -1px on test for 10 columns and 1 column, this did not affected the analysis, and corrections were made at measurement.

Try the test case on your browser

Column 1 2 3 4 5 6 7 8 9 10
10 Columns 14.7 14.7 14.7 14.7 14.7 14.7 14.7 14.7 14.7 14.7
Elastic 2.0 15 15 15 15 15 15 15 14 14 14
Firefox 3.0, 3.5 15 14 15 15 15 14 15 15 14 15
Safari 14 14 14 14 14 14 14 14 14 14
Opera 14 14 14 14 14 14 14 14 14 14
Internet Explorer 15 15 15 15 15 15 15 15 15 15
Column 1 2 3 4 5 6 7 8 9 10
9 Columns 16.78 16.78 16.78 16.78 16.78 16.78 16.78 16.78 16.78
Elastic 2.0 17 17 17 17 17 17 17 16 16
Firefox 3.0, 3.5 17 17 16 17 17 17 16 17 17
Safari 16 16 16 16 16 16 16 16 16
Opera 16 16 16 16 16 16 16 16 16
Internet Explorer 17 17 17 17 17 17 17 17 17
Column 1 2 3 4 5 6 7 8 9 10
8 Columns 19.38 19.38 19.38 19.38 19.38 19.38 19.38 19.38
Elastic 2.0 19 19 19 19 19 20 20 20
Firefox 3.0, 3.5 19 20 19 19 20 19 20 19
Safari 19 19 19 19 19 19 19 19
Opera 18 18 18 18 18 18 18 18
Internet Explorer 19 19 19 19 19 19 19 19
Column 1 2 3 4 5 6 7 8 9 10
7 Columns 22.71 22.71 22.71 22.71 22.71 22.71 22.71
Elastic 2.0 23 23 23 23 23 22 22
Firefox 3.0, 3.5 23 22 23 23 23 22 23
Safari 22 22 22 22 22 22 22
Opera 22 22 22 22 22 22 22
Internet Explorer 23 23 23 23 23 23 23
Column 1 2 3 4 5 6 7 8 9 10
6 Columns 27.17 27.17 27.17 27.17 27.17 27.17
Elastic 2.0 27 27 27 27 27 28
Firefox 3.0, 3.5 27 27 27 28 27 27
Safari 27 27 27 27 27 27
Opera 25 25 25 25 25 25
Internet Explorer 27 27 27 27 27 27
Column 1 2 3 4 5 6 7 8 9 10
5 Columns 33.4 33.4 33.4 33.4 33.4
Elastic 2.0 33 33 33 34 34
Firefox 3.0, 3.5 33 34 33 34 33
Safari 33 33 33 33 33
Opera 33 33 33 33 33
Internet Explorer 33 33 33 33 33
Column 1 2 3 4 5 6 7 8 9 10
4 Columns 42.75 42.75 42.75 42.75
Elastic 2.0 43 43 43 42
Firefox 3.0, 3.5 43 43 42 43
Safari 42 42 42 42
Opera 42 42 42 42
Internet Explorer 43 43 43 43
Column 1 2 3 4 5 6 7 8 9 10
3 Columns 58.3 58.3 58.3
Elastic 2.0 58 58 59
Firefox 3.0, 3.5 58 59 58
Safari 58 58 58
Opera 57 57 57
Internet Explorer 58 58 58
Column 1 2 3 4 5 6 7 8 9 10
2 Columns 89.5 89.5
Elastic 2.0 90 89
Firefox 3.0, 3.5 90 89
Safari 89 89
Opera 89 89
Internet Explorer 90 90
Column 1 2 3 4 5 6 7 8 9 10
1 Column 183
Elastic 2.0 183
Firefox 3.0, 3.5 183
Safari 183
Opera 183
Internet Explorer 183

Discussion

Even though this study only reveals one part of the algorithm of render engines, its a problem that should appear in a wide number of cases
of web development. Further studies could reveal other parts of this algorithm.

Browsers

Firefox

var containerWidth;
var columnPercentageWidth;
var columns;
var columnWidth = Math.round(containerWidth * ( columnPercentageWidth / 100 ));
var column;
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}
var computedWidth    = columnWidth * columns.length;
var difference       = containerWidth - computedWidth;
var positionDivision = columns.length / (Math.abs(difference) + 1);
var increment        = (difference > 0);
var direction        = -1;
if(difference !== 0)
for(var i = 1; i <= (Math.abs(difference)); i++){
if(direction == -1){
column = columns[columns.length - Math.floor( positionDivision * Math.round(i/2) )];
}
else{
column = columns[Math.floor( positionDivision * Math.round(i/2) ) - 1];
}
if(increment){
column.style.width = columnWidth + 1;
}
else{
column.style.width = columnWidth - 1;
}
direction = direction * -1;
}

Safari

var columns;
var columnWidth = Math.floor( containerWidth * ( columnPercentageWidth / 100 ) )
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}

Opera

var columns;
var columnWidth = Math.floor( containerWidth * ( Math.floor( columnPercentageWidth ) / 100 ) );
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}

Internet Explorer

var columns;
width = Math.round( containerWidth * ( columnPercentageWidth / 100 ) )
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}

Elastic CSS Framework

var containerWidth;
var columnPercentageWidth;
var columns;
var columnWidth = Math.round(containerWidth * ( columnPercentageWidth / 100 ));
var column;
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}
var computedWidth    = columnWidth * columns.length;
var difference       = containerWidth - computedWidth;
var positionDivision = columns.length / (Math.abs(difference) + 1);
var increment        = (difference > 0);
var direction        = -1;
if(difference !== 0)
for(var i = 1; i <= (Math.abs(difference)); i++){
column = columns[columns.length - i )];
if(increment){
column.style.width = columnWidth + 1;
}
else{
column.style.width = columnWidth - 1;
}
}

The code presented here may not be the actual code, because it was not copied, was infered. Also code must have extra considerations for more cases

This results are valid for the cases considered in the study, and are confirmed by the control case. Combinations of diferent types of blocks may lead to a different set of results, but that is outside of the scope of this study.

How other css frameworks handle pixel rounding

CSS Frameworks based on grid systems (blueprint, 960gs, bluetrip, and similar) are based on divisions that lead to integer results for every column, this avoids falling in the problem of pixel rounding and do not need a solution, but a limitant on this frameworks is that they do not support liquid layouts. Implementing liquid layouts on this type of frameworks would force to find a pixel rounding solution.

CSS Frameworks based on column combinations that support liquid layouts, have different solutions to deal with this.

YUI uses a set of classes that define different width and float left or right according to the context of its container and the browser.
This still presents the problem that the computed values varies according to the algorithm implemented by the browser. Scaling this to more than 4 columns would lead to a high multiplicity of exceptions on css code.

OOCSS uses a class “last” that its not floated, contrary to the rest of the columns in the unit, this causes that the last column gets reduced
by the space of the rest of the columns, but also depends on the computed withs of the algorithm implemented by the browser. scaling this to more columns would reveal the algorithm errors on browsers leading to more evident problems in the final width of columns.

Elastic provides a JavaScript Based override to the computations making a completely predictable width in all browsers. even though is a little slower because overrides the native render engine computations, it can scale to many columns, and never blocks the flow of content.

Conclusions

  • Every Browser implements a different solution for sub pixel rounding.
  • Firefox is the only browser that does a complete use of the available space.
  • Elastic CSS Framework is the only Framework that rounds widths consistently accross browsers.
  • Other CSS Frameworks will vary their accuracy depending on the browser they are rendered.
  • Only Internet Explorer leads to a sum of column widths grater to their container, showing the column jump bug.

Personal opinion.

Browsers should be more open in their implementations, that way they would help the creation of a standard in rounding widths, and other areas.

An open documentation of this solutions would help web developers understand better the problems, and solutions in browsers, and this would help to advance the standardization and development to a faster ratio.

A standardization on this subject would simplify the advance and improve speed of Elastic CSS Framework Engine performance leaving this operations to a lower level. Also will open the path to a collaboration between framework authors instead of a competition.

If anyone would like to join efforts with Elastic CSS Framework core team, we are open to collaborate with others to improve the features of frameworks and maybe make recommendations to browsers and W3C.

This will open a discussion inside Elastic CSS Framework to see if the current algorithm should be changed to match Firefox, this also is an invitation to everyone to participate.

If you enjoy this post, please share it.
  • Digg
  • Reddit
  • DZone
  • del.icio.us
  • StumbleUpon
  • Technorati
  • LinkedIn
  • TwitThis
  • E-mail this story to a friend!

2 Comments »

Copyright © 2009 Elasticss.com. All rights reserved.
Made with Elastic