 |
| |
| Table of Contents |
| - Abstract |
| - Introduction |
| - CSS Scripting Layout Properties |
| - CSS Layout Objects and Interfaces |
| - Resolving Constraints |
| - CSS Scripting Layout – Security issues |
| - CSS Scripting Layout - Sample Implementation |
| Abstract top |
| This document describes a new set of CSS properties and object specifications that together compose a more powerful means to describe complex arbitrary layout criteria that are reusable and extensible. The new properties are ECMAScript (JavaScript) expressions that are encapsulated in a construct as a layout policy. A layout policy is referenced in CSS rules. The expressions composing a layout policy are woven together virtually in a constraint resolving system to perform a specified layout. New object specifications and a set of global objects defined in the constraint resolution script runtime environment enable powerful operations to be expressed succinctly resulting in more readable and compact layout specifications. |
| Introduction top |
| Many in the web development community are dissatisfied with the layout capabilities in the current CSS standard and often resort to various client-side JavaScript solutions to fulfill their layout needs. There are several proposals before the W3C CSS Working group that aim to broaden CSS layout capabilities and reduce or eliminate the need for client-side JavaScript. These proposals employ well-defined strategies each with their own set of specific properties. If adopted, these proposals will increase substantially the size and complexity of CSS implementations as well as CSS syntax.
We propose a more general-purpose CSS layout solution that brings scripting unobtrusively into CSS. This solution enables a wide number of layout strategies to be defined in terms suitable to each. This general-purpose solution avoids the complexities that a multitude of independent solutions saddles on CSS implementers and developers.
|
| CSS Scripting Layout Properties top |
A Scripting layout is encapsulated in a CSS layout-policy @-rule. A layout-policy @-rule defines a name that is referenced in layout-policy properties within rules. The body of a layout-policy @-rule is composed of properties with String values that are JavaScript expressions.
@layout-policy name
{
initial-script:”arbitrary JavaScript”
container-script:”arbitrary JavaScript”
container-width:”…”
container-height:”…”
rectangle-attributes:”JavaScript Associative Array”
left:”arbitrary JavaScript”
horizontal-center:”…”
right:”…”
width:”…”
top:”…”
vertical-center:”…”
bottom:”…”
height:”…”
}
initial-script is arbitrary JavaScript that is executed to define values, functions, or any other JavaScript object that may then be referenced in the expressions of other constraint properties. It is executed only once per layout request.
container-script is arbitrary JavaScript that is executed to define values, functions, or any other JavaScript object that may then be referenced in the expressions of other constraint properties. It may be executed multiple times per layout request depending on the number of cycles required to resolve the constraints.
container-width and container-height properties are JavaScript expressions that must compute to a JavaScript Number, the value of which determines the width or height of the container. These properties may also be ordinary length or percentage values.
rectangle-attributes defines a JavaScript associative array. This associative array defines a set of name-value pairs whereby the name defines a new arbitrary attribute applicable to each child element of the container. The attribute value is a JavaScript expression.
The other constraint properties are JavaScript expressions and apply to each of the child elements of a container. These properties must compute to a JavaScript Number, the value of which determines the corresponding aspect of the child element. These properties may also be ordinary length or percentage values.
Constraint and rectangle attribute JavaScript expressions are evaluated lazily, at most once per distinct reference per layout resolution execution cycle. Each subsequent reference uses the result of the first evaluation. A reference is distinct on each child element on which the expression applies.
layout-policy @-rules are referenced by name in CSS rules.
A layout-policy property is set in a rule that is applied to a container html element in which case the layout policy constraints are applied to the container and its immediate child elements.
.container
{
layout-policy:”policy1 policy2”
initial-script:”arbitrary JavaScript”
container-script:”arbitrary JavaScript”
container-width:”…”
container-height:”…”
}
layout-policy values may reference more than one policy. In this case subsequent policies override and augment values from preceding policies.
Other properties may be included in a rule to override or augment corresponding layout-policy @-rule values.
Child elements may override or augment the constraint values of a layout policy.
.child
{
rectangle-attributes:”JavaScript Associative Array”
left:”arbitrary JavaScript”
…
}
A rectangle-attributes specification in a successive policy reference or on a rule overrides and augments the value in the initial policy. In other words, the rectangle-attributes value used in the computation is the amalgam of all the rectangle-attributes specified.
An overriding constraint with a value of none rescinds any prior constraint value rendering it unconstrained.
|
| CSS Layout Objects and Interfaces top |
| The expressions of the new CSS properties may reference new global objects defined in the CSS Layout JavaScript runtime environment.
A container object supports the CSSContainer interface that extends the Rectangle interface. A container object represents the container on which a layout policy is defined.
Rectangle
{
readonly attribute double left;
readonly attribute double right;
readonly attribute double horizontal_center;
readonly attribute double top;
readonly attribute double bottom;
readonly attribute double vertical_center;
readonly attribute double width;
readonly attribute double height;
readonly attribute double preferred_width;
readonly attribute double preferred_height;
readonly attribute double current_width;
readonly attribute double current_height;
double em(in double x);
double ex(in double x);
double inch(in double x);
double cm(in double x);
double mm(in double x);
double pc(in double x);
double pt(in double x);
}
The preferred values refer to the sizes of a rectangle’s unconstrained values. The current values refer to the sizes of a rectangle prior to the application of any constraint.
CSSContainer : Rectangle
{
readonly attribute CSSRectangleList children;
readonly attribute long numChildren;
}
A CSSContainer maintains an ordered list of child elements. The individual child elements are represented in the global execution environment by objects that support the CSSRectangle interface that also extends the Rectangle interface. CSSRectangle objects support referencing rectangle-attributes properties by name. The object returned represents the computational result of the attribute value’s expression. CSSRectangle objects may be referenced by name from the CSSContainer returning either a CSSRectangleList or CSSRectangle. The CSSRectangle name value is the id of the HTML element that it represents.
CSSRectangle: Rectangle
{
readonly attribute unsigned long position;
readonly attribute String name;
readonly attribute CSSRectangle predecessor;
readonly attribute CSSRectangle successor;
}
CSSRectangle objects may be referenced abstractly in expressions through global objects rectangle, predecessor, and successor. rectangle represents the CSSRectangle object on which behalf a constraint or attribute expression is executing. predecessor, and successor represent the preceding and succeeding CSSRectangle objects of rectangle in the container’s ordered list either of which may be null. These values change throughout execution as expressions are evaluated.
A rectangles global object supports the CSSRectangleList interface. This object represents the ordered list of elements in a container. CSSRectangleList objects are immutable.
CSSRectangleList objects support indexing values to return corresponding CSSRectangle objects.
A sublist of a CSSRectangleList may be constructed from an existing CSSRectangleList by referencing it as a function with integer arguments:
CSSRectangleList(start, end, stride, count)
start and end indicate a range of index values. start may be greater than end indicating that the values are set in reverse order.
The lesser of start and end must be between zero and one less than the CSSRectangleList length.
The greater of start and end must be between one and the CSSRectangleList length.
stride indicates how many values to skip between successive values and count indicates how any values to include each stride. count must be less than or equal to stride.
Both stride and count default to one.
A new CSSRectangleList may also be constructed from an existing CSSRectangleList by referencing it as a function with an Array of integer values. The integer values represent the indices of values to include in the new CSSRectangleList.
Additionally, CSSRectangleList objects support referencing rectangle-attributes properties by name. A CSSRectangleListValue is returned to represent these values.
CSSRectangleList
{
readonly attribute unsigned long length;
readonly attribute CSSRectangleListValue position;
readonly attribute CSSRectangleListValue left;
readonly attribute CSSRectangleListValue right;
readonly attribute CSSRectangleListValue horizontal_center;
readonly attribute CSSRectangleListValue top;
readonly attribute CSSRectangleListValue bottom;
readonly attribute CSSRectangleListValue vertical_center;
readonly attribute CSSRectangleListValue width;
readonly attribute CSSRectangleListValue height;
readonly attribute CSSRectangleListValue preferred_width;
readonly attribute CSSRectangleListValue preferred_height;
readonly attribute CSSRectangleListValue current_width;
readonly attribute CSSRectangleListValue current_height;
CSSRectangleListValue em(in double x);
CSSRectangleListValue ex(in double x);
CSSRectangleListValue inch(in double x);
CSSRectangleListValue cm(in double x);
CSSRectangleListValue mm(in double x);
CSSRectangleListValue pc(in double x);
CSSRectangleListValue pt(in double x);
CSSRectangleListIterator iterator();
CSSRectangleList remove(in int x);
CSSRectangleList remove(in int x, int length);
CSSRectangleList remove(in CSSRectangle x);
CSSRectangleList append(in CSSRectangle x);
CSSRectangleList append(in CSSRectangleList x);
long indexOf(in CSSRectangle x);
boolean contains(in CSSRectangle x);
}
A CSSRectangleListIterator provides a simple means to iterate through a CSSRectangleList.
CSSRectangleListIterator
{
readonly attribute unsigned long index;
readonly attribute CSSRectangle next;
readonly attribute boolean more;
}
A CSSRectangleListValue represents a set of values from a CSSRectangleList. CSSRectangleListValue objects support indexing values to return objects representing the corresponding values in the list.
CSSRectangleListValue : CSSRectangleListFilter
{
readonly attribute double min;
readonly attribute double max;
readonly attribute double sum;
readonly attribute double avg;
readonly attribute double stddev;
readonly attribute double variance;
readonly attribute CSSRectangleList sort;
readonly attribute CSSRectangleListFilter exclude;
readonly attribute CSSRectangleListFilter truncate;
readonly attribute CSSRectangleListFilter cumulate;
readonly attribute CSSRectangleListValueRect rect;
}
A CSSRectangleListFilter represents a set of operations that may be executed on a CSSRectangleListValue. These operations return a CSSRectangleListFilterOp.
CSSRectangleListFilter
{
CSSRectangleListOp eq;
CSSRectangleListOp ne;
CSSRectangleListOp le;
CSSRectangleListOp lt;
CSSRectangleListOp ge;
CSSRectangleListOp gt;
}
A CSSRectangleListFilterOp represents a set of operations that may be executed on a CSSRectangleListFilter. These operations return a new CSSRectangleList.
CSSRectangleListFilterOp
{
readonly attribute CSSRectangleList left;
readonly attribute CSSRectangleList right;
readonly attribute CSSRectangleList horizontal_center;
readonly attribute CSSRectangleList top;
readonly attribute CSSRectangleList bottom;
readonly attribute CSSRectangleList vertical_center;
readonly attribute CSSRectangleList width;
readonly attribute CSSRectangleList height;
readonly attribute CSSRectangleList preferred_width;
readonly attribute CSSRectangleList preferred_height;
readonly attribute CSSRectangleList current_width;
readonly attribute CSSRectangleList current_height;
}
CSSRectangleListFilterOp objects may be referenced as a function with a single numeric argument.
Additionally, CSSRectangleListFilterOp objects support referencing rectangle-attributes properties by name. A CSSRectangleList is returned to represent the value of the operation.
A CSSRectangleListValueRect represents a set of operations that return a CSSRectangle object.
CSSRectangleListValueRect
{
readonly attribute CSSRectangle min;
readonly attribute CSSRectangle max;
}
The following examples illustrate valid usage:
| container.em(1) |
The number of pixels in 1em of the container’s font |
| rectangles.width.max |
the maximum width of all the rectangles in the CSSRectangleList |
| rectangles.width.rect.max |
the CSSRectangle that has the maximum width of all the rectangles in the list |
| rectangles.width.sum |
the sum of the widths of the rectangles in the list |
| rectangles.width.gt(100) |
A new CSSRectangleList including only those CSSRectangle objects with a width greater than 100px |
| rectangles.preferred_width. gt.current_width |
A new CSSRectangleList including only those CSSRectangle objects with a preferred width greater than their current width. |
| rectangles.width.exclude. gt(100) |
A new CSSRectangleList excluding those CSSRectangle objects with a width greater than 100px |
| rectangles.width.truncate. gt(100) |
A new CSSRectangleList truncated at the position of the first CSSRectangle that has a width greater than 100px |
| rectangles.width.cumulate. gt(100) |
A new CSSRectangleList truncated at the position of the first CSSRectangle that has a width that when summed with all preceding CSSRectangle widths is greater than 100px |
| rectangles(0, rectangles.length, 2) |
A new CSSRectangleList composed of every other CSSRectangle in the list |
| rectangles(rectangles.length, 0) |
A new CSSRectangleList in reverse order |
|
| Resolving Constraints top |
| The constraint resolver executes the initial-script JavaScript to initialize the execution environment. The initial-script values referenced in the container’s layout-policy references are executed in the order presented followed by the initial-script value of the container itself.
After initialization the constraints for all the contained elements are resolved deterministically according to how dependencies among the constraints are expressed in the constraint values.
For instance, the top side of an element may be affixed to the bottom side of its predecessor according to the following constraint:
top:”predecessor.bottom”
In this case, the execution of the top constraint is interrupted in order to determine the bottom constraint of the predecessor. The predecessor may or may not have an explicit bottom constraint. If the constraint exists, it is evaluated, the result returned, and the top constraint execution is resumed. If the bottom constraint does not exist then its value is determined via other related constraints in the vertical dimension, e.g. a top and height constraint. Once all constraints are satisfied a layout computation is complete.
The constraint resolver strategy involves the repeated computation of container size and child rectangle configurations within the container. The child rectangle geometries are held constant at their current values during the computation of the container size. Conversely, the container size is held constant during the computation of the child rectangle configurations.

A global Boolean value, sizing, is defined in the runtime environment to distinguish between the computation of the container size and the child rectangle configurations.
The constraint resolver uses a set of heuristics to determine when to terminate. The primary heuristic is to stop when the last container size computation is a repeat of some prior computation, but only when all child rectangles’ sizes are at least as large as their preferred sizes, i.e. their content does not overextend their bounds. However, this second heuristic is relaxed in the case where all rectangle preferred sizes are a repeat of some prior computation. In this case, it is probably impossible to configure the child rectangles without some overextension.
In under-constrained or abstractly constrained cases multiple solutions may be indicated by a particular layout specification and set of content. In this case, the solution with the least area container in which all child rectangles fit their bounds is chosen. In cases where child rectangles must overextend then the configuration with the largest area container prevails.
This constraint resolution strategy enables layout solutions for more abstractly constrained specifications heretofore unrealizable in CSS while retaining the ability to resolve more typically constrained layout specifications quickly and efficiently.
A detailed discussion of size negotiation among elements is beyond the scope of this paper, but the subject is worthy of thorough examination. Geometry management as prescribed by the X Toolkit Intrinsics XtQueryGeometry1 is a good reference for this subject.
In a conventional layout specification, constraints are explicit and convergence to a solution occurs in one or two iterations. In these instances layouts are resolved quickly consuming little CPU time.
More interestingly, specifications with more abstract constraints may be realized. These make use of feedback from suboptimal early generation solutions to improved succeeding generation solutions converging to a final solution in the manner of a genetic algorithm. Example specifications follow that illustrate layout policies that include these features. Moreover, these examples will show how layouts can adapt to changes in screen real estate from window resize events.
|
| CSS Scripting Layout – Security issues top |
The global object for the CSS Layout JavaScript runtime environment is an ordinary Object. Not using the browser window as the global object reduces or eliminates the possibility of any serious cross-site scripting (XSS) attack. As such, the CSS Layout JavaScript runtime environment does not include any of the HTML DOM objects or any client-side JavaScript Browser objects, including:
- Window
- Navigator
- Screen
- History
- Location
- Document
Likewise, the Asynchronous JavaScript and XML (AJAX) XMLHttpRequest object is unavailable in this runtime environment.
|
| CSS Scripting Layout - Sample Implementation top |
| To validate the CSS Scripting Layout specification a sample implementation was coded as an extension to Google Chrome. A 12.7MB zipped windows XP compatible executable of this implementation is located at the eFORCE site here. In this implementation scripting layout is limited to the elements of container elements that are positioned absolutely. |
- Example 1 – Introductory Concepts
- Example 2 – Three-Column Presentation
- Example 3 – HTML Table comparison
- Example 4 – Advanced Concepts – Near Optimal Least Area Layout |
| Comments on Layout in the Current CSS Standard
Dissatisfaction with CSS layout capability has been expressed by many in the Web Development community10,11,12,13,14,15,16,17,18,19,20 including members of the W3C CSS WG.21,22,23 The CSS Scripting Layout Model described here resolves many, if not most, of these legitimate concerns.
CSS, apparently, was conceptualized to enable a newspaper-like layout in a Web page.24,25 Whatever the case, in regard to layout, it should not be considered controversial to state that CSS was influenced primarily by the demands of print media and little on User Interface demands. This is understandable since, at the time, the WWW was dominated by static print media. However, rendering Web content now involves much more than mere typography. The Web has evolved into a GUI rich environment in which JavaScript Libraries like YUI26, Dojo Toolkit27, Ext JS28 , and others29 that enable layout features not easily achievable or impossible to achieve in CSS.
Interestingly, thirty-two leaders in the fields of web design and development interviewed for a Teach the Web monograph ranked layout third in importance among 62 skills that students should know by the time they graduate.30 This is further testimony to the inadequate state of CSS regarding layout and a need for tools and, moreover, new standards that address its shortcomings.
There are several layout related proposals31,32,33,34,35 in various states of development before the CSS Working Group and others36 that are not. These proposals appear to address various shortcomings, but do so at the cost of greatly increasing complexity with the introduction of a multitude of new properties that are largely mutually exclusive. The layout specification described here could mitigate the need for some of these proposals.
|
| Conclusion
Layout is inherently complex. Layout designs must adapt to any imaginable change in screen size, configuration, or composition in real-time. The possibilities are limitless. It is an impossible task for any single high-level specification, or even a small set of specifications, to cope with every conceivable circumstance.
The W3C's Web Content Accessibility Guidelines' checkpoint 3.3, a priority-2 checkpoint, states "use style sheets to control layout and presentation."37 With this directive in mind it is incumbent on the W3C to provide lower-level tools to enable the broadest set of layout strategies and handle unforeseeable eventualities. As a practical matter, the most suitable tools for this purpose must incorporate aspects of actual programming languages. We believe that the combination of a scripting language and the Object Interfaces presented here meets this challenge most effectively in conformance with W3C's design principles.38
|
|
|
|
|
| |
| The HTML in Example 1 produces the screen shot in Figure 1.
The rendering of this example demonstrates many of the features of CSS Script Layout.
Example 1 – Nested Containers
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Nested Containers</title>
<style type="text/css">
@layout-policy pack_column {
initial-script: "\
var margin=container.em(0.5);\
";
rectangle-attributes: "{\
'topOffset':0\
}";
horizontal-center:"container.width/2";
top: "rectangle.topOffset+margin\
+(predecessor ? predecessor.bottom : 0)";
container-height: "margin*(rectangles.length+1)\
+rectangles.height.sum+rectangles.topOffset.sum";
container-width: "2*margin+rectangles.width.max";
}
@layout-policy override {
initial-script: "\
var margin=container.em(0.25)\
";
horizontal-center:none;
left:"margin";
}
@layout-policy pack_row {
initial-script: "\
var margin=0;\
";
vertical-center:"container.height/2";
left:"margin+(predecessor ? predecessor.right : 0)";
container-width:"margin*(rectangles.length+1)\
+rectangles.width.sum";
container-height:"2*margin+rectangles.height.max;";
}
.body {
layout-policy: 'pack_column';
position:relative;
border:1px black dashed;
padding:0px;
}
.container {
layout-policy: 'pack_row';
initial-script: "margin=container.ex(2);";
position:absolute;
border:1px black solid;
padding:0px;
}
.container1 {
layout-policy: 'pack_column override';
width:130px;
position:absolute;
border:1px black dotted;
margin:0px;
padding:0px;
}
.text {
position:absolute;
border:1px black dashed;
padding:0px;
}
.extension {
rectangle-attributes: "{\
'topOffset':'container.em(1)+margin'\
}";
width:200px;
padding:10px;
}
</style>
</head>
<body class='body'>
<span class='text'>test nesting containers</span>
<span class='container'>
a nested container that overrides the default margin.<br>
this text is in a relative span and<br>is not constrained by the container.
<span class='text'>
this span of text is centered vertically
</span>
<button style='position:absolute;'>button</button>
<span class='container1'>
relative text
<span class='text extension'>
this span has an explicit padding and width specification and a topOffset rectangle attribute.
</span>
<span class='text' style='width:150px'>
notice also the override policy specification overrides the margin and alignment...
</span>
<span class='text'>hence the spans are now left aligned and the margin is smaller.
</span>
</span>
</span>
</body>
</html>
|
Figure 1 - Nested Containers
This layout renders in less than 32 milliseconds on a Dell LATITUDE D630 Laptop (Intel Core 2 Duo T7500 2.20GHz, 2.49 GB RAM) running MS Windows XP V2002 Service Pack 3.
|
|
|
|
| |
| The Common Three-Column Presentation 2,3,4 HTML in Example 2 produces the screen shot in Figure 2.
The rendering of this example demonstrates how elements may be referenced by their HTML id in the script. Additionally, in this example there is no dependency on the order of the elements in the HTML as to where they are rendered. This is an important point for those concerned with Search Engine Optimization (SEO).
The totality of the layout here is distributed among the rules of each element in the window. Notable here is that the desired result is clearly evident from the constraint specifications within each rule and presented rationally without resorting to hacks or tricks and without any compromises. All elements are free to resize as required by their content.
Example 2 – Common Three-Column Presentation
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Three Column Layout</title>
<style type="text/css">
@layout-policy entire_window {
initial-script: "\
var margin=4;\
var content_area=container.content_area;\
var header=container.header;\
var header_content=container.header_content;\
var footer=container.footer;\
var footer_content=container.footer_content;\
var primary_navigation=container.primary_navigation;\
var secondary_navigation=container.secondary_navigation;\
var min_content_width = 200;\
var max_content_width = 600;\
";
container-script: "\
var content_width = container.width-2*margin-\
primary_navigation.width-secondary_navigation.width;\
";
container-height:100%;
container-width:100%;
}
#body {
position:relative;
layout-policy: 'entire_window';
border:0px black solid;
margin:0px;
padding:0px;
}
#content_area {
top:"header.bottom";
bottom:"footer.top";
left:"primary_navigation.right";
width:"content_width<min_content_width?min_content_width:\
content_width>max_content_width?max_content_width:content_width";
position:absolute;
border:1px black solid;
}
#primary_navigation {
left:"margin";
width:"rectangle.ex(10)+8+container.width*0.1;";
top:"header.bottom";
bottom:"footer.bottom";
position:absolute;
border:1px black solid;
}
#secondary_navigation {
top:"header.bottom";
bottom:"footer.bottom";
left:"content_area.right";
width:"rectangle.ex(8)+8+container.width*0.1;";
position:absolute;
border:1px black solid;
}
#header {
top:"margin";
height:"header_content.height+2*margin";
left:"margin";
right:"secondary_navigation.right";
position:absolute;
border:1px black solid;
}
#header_content {
width:"header.width-2*margin";
vertical-center:"header.vertical_center";
horizontal-center:"header.horizontal_center";
position:absolute;
border:1px black solid;
}
#footer {
height:"footer_content.height+2*margin";
top:"header.bottom+Math.max(content_area.preferred_height,\
primary_navigation.preferred_height-footer.height,\
secondary_navigation.preferred_height-footer.height)";
left:"primary_navigation.right";
right:"secondary_navigation.left";
position:absolute;
border:1px black solid;
}
#footer_content {
width:"footer.width-2*margin";
horizontal-center:"footer.horizontal_center";
vertical-center:"footer.vertical_center";
position:absolute;
border:1px black solid;
}
</style>
</head>
<body id='body' class='body'>
<span id='content_area'>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae
ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt
explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut
odit aut fugit, sed quia consequuntur magni dolores eos qui ratione
voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum
quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam
eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat
voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam
corporis suscipit laboriosam, nisi ut aliquid ex ea commodi
consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate
velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum
fugiat quo voluptas nulla pariatur?
</span>
<span id='primary_navigation'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
massa lacus, consectetur vitae commodo sed, aliquet eget sapien.
Quisque erat odio, convallis non aliquam vitae, pulvinar a diam.
In hac habitasse platea dictumst. Aenean non tempor ipsum. Sed scelerisque
fringilla ligula at laoreet. Cras posuere metus at ligula posuere in
consequat lorem malesuada. Sed id lobortis nulla.
</span>
<span id='secondary_navigation'>
Fusce enim sem, ornare in ornare nec, ornare sed neque. Ut sed felis
orci, in pretium lectus. Cras nec felis sed nibh venenatis dignissim in
sit amet massa. Cras sodales mollis arcu, quis sagittis tellus euismod
eu. Nulla facilisi. Aliquam ultrices consequat rutrum. Quisque pharetra
adipiscing porta. Nullam sem urna, fringilla tempus aliquet a, ornare
non risus.
</span>
<span id='header_content'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum
vestibulum fringilla interdum. Fusce suscipit vehicula nunc, nec
sagittis risus mollis non. Cras non rhoncus magna. Vestibulum ante
ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
Nullam eleifend sapien velit. Cum sociis natoque penatibus et magnis
dis parturient montes, nascetur ridiculus mus. Quisque gravida erat vel
eros iaculis ac rhoncus nibh tincidunt.
</span>
<span id='footer_content'>
Fusce quis lacus ligula, a dictum metus. Aenean risus odio, sagittis ac
posuere vel, viverra et erat. Aenean eros lorem, fringilla ut aliquam
id, pulvinar et nulla. Pellentesque imperdiet eros non odio faucibus
fringilla. Nam ut nulla at tellus vestibulum mattis.</span>
<span id='header'</span>
<span id='footer'></span>
</body>
</html>
|
Figure 2 - Common Three Column Presentation
The Three-Column layout renders in 16 milliseconds on a Dell LATITUDE D630 Laptop (Intel Core 2 Duo T7500 2.20GHz, 2.49 GB RAM) running MS Windows XP V2002 Service Pack 3.
|
|
|
|
|
|
| |
| The HTML in Example 3 produces the screen shot in Figure 3. In the first row the layout policy accounts for and minimizes a cell’s total area rather than relying on a cell’s width as in the HTML table below it. Deficiencies with table layout are described in Poorly Laid-out Tables.5
Example 3 – Script vs. HTML Table
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Script vs. HTML Table</title>
<style type="text/css">
@layout-policy pack_column {
initial-script: "\
var margin=1;\
var space=(rectangles.length+1)*margin;\
";
width:"container.width";
height:"rectangle.preferred_height";
top:"margin+(predecessor ? predecessor.bottom : 0)";
container-height:"space+rectangles.preferred_height.sum";
container-width:"rectangles.preferred_width.max";
}
@layout-policy least_area_horizontal {
initial-script: "\
var margin=2;\
var space=(rectangles.length-1)*margin;\
var governor=-0.24;\
var accelerator=2;\
";
container-script: "\
var reserved=rectangles.pliant.eq(false);\
var pliant=rectangles.pliant.ne(false);\
var usable_width=container.width-space-reserved.preferred_width.sum;\
var area_ratio=usable_width/pliant.area.sum;\
";
rectangle-attributes: "{\
'current_area':'rectangle.current_width*rectangle.current_height',\
'preferred_area':'rectangle.preferred_width*rectangle.preferred_height',\
'area':'rectangle.preferred_area*accelerator+rectangle.current_area*governor',\
'max_width':'rectangle.preferred_width>=rectangle.width?rectangle.preferred_width:rectangle.width',\
'pliant':'rectangle.pliant!==false&&rectangle.preferred_width<=rectangle.current_width'\
}";
top:0;
height:"container.height";
width:"rectangle.pliant?rectangle.area*area_ratio:rectangle.preferred_width";
left:"predecessor ? predecessor.right+margin : 0";
container-width:"rectangles.max_width.sum+space";
container-height:"rectangles.preferred_height.max";
}
.body {
padding:0px;
margin:0px;
layout-policy: 'pack_column';
border:1px black solid;
position:relative;
container-width:100%;
}
/* script layout rules */
.text {
margin:0px;
position:absolute;
border:1px black solid;
padding:0px;
}
.row {
layout-policy: 'least_area_horizontal';
position:absolute;
border:2px black dotted;
margin:0px;
padding:0px;
}
/* html table rules */
.table {
position:absolute;
border:2px black dotted;
margin:0px;
padding:0px;
}
.ttext {
vertical-align:top;
margin:0px;
border:1px black solid;
padding:0px;
}
</style>
</head>
<body id='body' class='body'>
<div id='top' class='row'>
<span id='text2' class='text'>
<img src="small.gif"><img src="small.gif"><img src="big.gif"><img src="big.gif">
</span>
<span id='text1inner' class='text'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum
vestibulum fringilla interdum. Fusce suscipit vehicula nunc, nec
sagittis risus mollis non. Cras non rhoncus magna. Vestibulum ante
ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
Nullam eleifend sapien velit. Cum sociis natoque penatibus et magnis
dis parturient montes, nascetur ridiculus mus. Quisque gravida erat vel
eros iaculis ac rhoncus nibh tincidunt.
</span>
<span id='text1inner' class='text'>
Fusce quis lacus ligula, a dictum metus. Aenean risus odio, sagittis ac
posuere vel, viverra et erat. Aenean eros lorem, fringilla ut aliquam
id, pulvinar et nulla. Pellentesque imperdiet eros non odio faucibus
fringilla. Nam ut nulla at tellus vestibulum mattis. Fusce at tincidunt
nunc. Aliquam pharetra faucibus nisl, auctor dignissim tortor vehicula
rutrum. Pellentesque tincidunt mi ut elit congue sit amet eleifend dui
euismod. Donec luctus, lorem et placerat pulvinar, justo nunc dapibus
lorem, id porta velit diam sed diam. Nulla et erat libero. Donec auctor
sapien in dolor pretium ornare.
</span>
</div>
<table id='table' class='table'>
<tr class='ttext'>
<td id='text2' class='ttext'>
<img src="small.gif"><img src="small.gif"><img src="big.gif"><img src="big.gif">
</td>
<td id='text1inner' class='ttext'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum
vestibulum fringilla interdum. Fusce suscipit vehicula nunc, nec
sagittis risus mollis non. Cras non rhoncus magna. Vestibulum ante
ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
Nullam eleifend sapien velit. Cum sociis natoque penatibus et magnis
dis parturient montes, nascetur ridiculus mus. Quisque gravida erat vel
eros iaculis ac rhoncus nibh tincidunt.
</td>
<td id='text1inner' class='ttext'>
Fusce quis lacus ligula, a dictum metus. Aenean risus odio, sagittis ac
posuere vel, viverra et erat. Aenean eros lorem, fringilla ut aliquam
id, pulvinar et nulla. Pellentesque imperdiet eros non odio faucibus
fringilla. Nam ut nulla at tellus vestibulum mattis. Fusce at tincidunt
nunc. Aliquam pharetra faucibus nisl, auctor dignissim tortor vehicula
rutrum. Pellentesque tincidunt mi ut elit congue sit amet eleifend dui
euismod. Donec luctus, lorem et placerat pulvinar, justo nunc dapibus
lorem, id porta velit diam sed diam. Nulla et erat libero. Donec auctor
sapien in dolor pretium ornare.
</td>
</tr>
</table>
</body>
</html>
|
Figure 3 - Script vs. HTML Table
The layout in Figure 3 renders in less than 32 milliseconds on a Dell LATITUDE D630 Laptop (Intel Core 2 Duo T7500 2.20GHz, 2.49 GB RAM) running MS Windows XP V2002 Service Pack 3.
|
|
|
|
|
|
| |
| The HTML in Example 4 produces the screen shots in Figures 4 and 5. The Near Optimal Least Area layout nests containers to a depth of four with varying layout policies for each. Notable here is that in the execution of the layout policies the constraint resolver searches for and converges on solutions that minimize the area taken by the rectangles for a given width of the window. There is no guarantee that the most optimal layout is found, but only one that approaches it which is likely acceptable if not imperceptible from the optimum to the average viewer.
A description of the actual layout policies and how they function is beyond the scope of this paper. More to the point is that layouts may be developed and shared among developers without the need for end users to understand the execution details. De facto or even de jure standards could emerge from this process.
Example 4 – Near Optimal Least Area
|
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>Near Optimal Least Area</title>
<style type="text/css">
@layout-policy pack_column {
initial-script: "\
var margin=1;\
var space=(rectangles.length+1)*margin;\
";
width:"container.width";
height:"rectangle.preferred_height";
top:"margin+(predecessor ? predecessor.bottom : 0)";
container-height:"space+rectangles.preferred_height.sum";
container-width:"rectangles.preferred_width.max";
}
@layout-policy proportional_width {
initial-script: "\
var margin=2;\
var governor=-0.25;\
var accelerator=2;\
var space=(rectangles.length-1)*margin;\
";
container-script: "\
var reserved=rectangles.pliant.eq(false);\
var pliant=rectangles.pliant.ne(false);\
var usable_width=container.width-space-reserved.preferred_width.sum;\
var area_ratio=usable_width/pliant.area.sum;\
";
rectangle-attributes: "{\
'area':'rectangle.preferred_width*rectangle.preferred_height*accelerator+\
rectangle.current_width*rectangle.current_height*governor',\
'max_width':'rectangle.preferred_width>=rectangle.width?rectangle.preferred_width:rectangle.width',\
'pliant':'rectangle.pliant!==false&&rectangle.preferred_width<=rectangle.current_width'\
}";
top:0;
height:"container.height";
width:"rectangle.pliant?rectangle.area*area_ratio:rectangle.preferred_width";
left:"predecessor ? predecessor.right+margin : 0";
container-width:"rectangles.max_width.sum+space";
container-height:"rectangles.preferred_height.max";
}
@layout-policy proportional_height {
initial-script: "\
var margin=2;\
var governor=-0.25;\
var accelerator=2;\
var space=(rectangles.length-1)*margin;\
";
container-script: "\
var reserved=rectangles.pliant.eq(false);\
var pliant=rectangles.pliant.ne(false);\
var usable_height=container.height-space-reserved.preferred_height.sum;\
var area_ratio=usable_height/pliant.area.sum;\
";
rectangle-attributes: "{\
'area':'rectangle.preferred_width*rectangle.preferred_height*accelerator+\
rectangle.current_width*rectangle.current_height*governor',\
'pliant':'rectangle.pliant!==false&&rectangle.preferred_height<=rectangle.current_height'\
}";
top:0;
width:"container.width";
height:"rectangle.pliant?rectangle.area*area_ratio:rectangle.preferred_height";
top:"predecessor ? predecessor.bottom+margin : 0";
container-height:"rectangles.preferred_height.sum+space";
container-width:"rectangles.preferred_width.max";
}
.body {
padding:0px;
margin:0px;
layout-policy: 'pack_column';
border:1px black solid;
position:relative;
container-width:100%;
}
.column {
position:absolute;
padding:0px;
margin:0px;
layout-policy: 'proportional_height';
border:2px black solid;
}
.text {
margin:0px;
position:absolute;
border:1px black solid;
padding:0px;
}
.row {
layout-policy: 'proportional_width';
position:absolute;
border:2px black dotted;
margin:0px;
padding:0px;
}
</style>
</head>
<body id='body' class='body'>
<div id='top' class='row'>
<span id='textimagemixlong' class='text'>
<img src="small.gif" border="0"> <img src="small.gif" border="0">
<img src="small.gif" border="0"> <img src="small.gif" border="0">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse
massa lacus, consectetur vitae commodo sed, aliquet eget sapien.
Quisque erat odio, convallis non aliquam vitae, pulvinar a diam.
<img src="small.gif" border="0"> <img src="small.gif" border="0">
<img src="small.gif" border="0"> <img src="small.gif" border="0">
In hac habitasse platea dictumst. Aenean non tempor ipsum. Sed
scelerisque fringilla ligula at laoreet.
</span>
<span id='text2' class='text'>
<img src="small.gif"> <img src="small.gif">
<img src="small.gif"> <img src="small.gif">
<img src="big.gif"> <img src="big.gif">
<img src="big.gif"> <img src="big.gif">
</span>
<span id='text1inner' class='text'>
Cras posuere metus at ligula posuere
<img src="small.gif" border="0">
in consequat
<img src="small.gif" border="0">
lorem malesuada.
<img src="small.gif" border="0">
Sed id lobortis nulla.
<img src="small.gif" border="0">
Fusce enim sem, ornare in ornare nec, ornare sed neque. Ut sed felis
orci, in pretium lectus. Cras nec felis sed nibh venenatis dignissim in
sit amet massa. Cras sodales mollis arcu, quis sagittis tellus euismod eu.
</span>
<div id='inner' class='column'>
<span id='text1inner' class='text'>
Nulla facilisi. Aliquam ultrices
<img src="small.gif" border="0">
consequat
<img src="small.gif" border="0">
rutrum.
<img src="small.gif" border="0">
Quisque pharetra
<img src="small.gif" border="0">
adipiscing porta. Nullam sem urna, fringilla tempus aliquet a, ornare non risus.
</span>
<span id='text2inner' class='text'>
<img src="small.gif" border="0"> This is a nice simple paragraph of text,
<img src="small.gif" border="0">
which has been chosen to height as the other cell, since they are about equally long.
</span>
</div>
</div>
<div id='bottom' class='row'>
<span id='textbr' class='text'>
This paragraph has encoded *<br>
line-breaks after each asterisk *<br>
to confound the layout. Content *<br>
management systems may include *<br>
content with embedded html or *<br>
images that confound sensitive *<br>
css specifications. Content *<br>
authors often are unaware of where*<br>
their content is consumed.*<br>
</span>
<span id='textimagemixlong' class='text'>
<img src="small.gif" border="0"> <img src="small.gif" border="0">
<img src="small.gif" border="0"> <img src="small.gif" border="0">
Sed ut perspiciatis unde omnis iste natus error sit voluptatem
accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae
ab illo inventore veritatis et quasi architecto beatae vitae dicta
<img src="small.gif" border="0"> <img src="small.gif" border="0">
<img src="small.gif" border="0"> <img src="small.gif" border="0">
sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur
aut odit aut fugit,
</span>
<div id='innerbottom' class='column'>
<div id='innerbottomrow' class='row'>
<span id='smallinner' class='text'>
<img src="small.gif" border="0"> <img src="small.gif" border="0">
<img src="small.gif" border="0"> <img src="small.gif" border="0">
</span>
<span id='text1inner' class='text'>
sed quia consequuntur magni
<img src="small.gif" border="0">
dolores eos
<img src="small.gif" border="0">
qui ratione
<img src="small.gif" border="0">
voluptatem sequi
<img src="small.gif" border="0">
nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit
amet, consectetur, adipisci velit, sed quia non numquam eius modi
tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.
Ut enim ad minima
</span>
<span id='text2inner' class='text'>
<img src="small.gif" border="0">
veniam, quis
<img src="small.gif" border="0">
nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut
aliquid ex ea commodi consequatur? Quis autem
</span>
</div>
<span id='text2inner' class='text'>
<img src="small.gif" border="0">
vel eum iure reprehenderit qui in ea
<img src="small.gif" border="0">
voluptate velit esse quam nihil molestiae consequatur, vel illum qui
dolorem eum fugiat quo voluptas nulla pariatur?
</span>
</div>
</div>
</body>
</html>
|
Figure 4 - Near Optimal Least Area Layout
Example 4 is an extreme case demonstrating the ability of the containers to cooperate in the layout resolution. Note that no explicit heights or widths are specified. In this instance the layout adjusts to the content and vice versa in a virtuous cycle.
In situations where content is generated by a CMS team that is separate from the Web design team6 or where content may be reused in many applications not known in advance7,8 it may be necessary to leave element sizes unspecified and accommodate their natural sizes in a self-organizing layout. Otherwise poor renderings may result as demonstrated in the HTML table in Example 3.
In Figure 5 a resize widening the window results in the layout adjusting accordingly.

Figure 5 - Near Optimal Least Area Layout. Layout adjusts to increased window width.
The layout in Examples 4 and 5 renders in somewhere between 0.2 and 2.0 seconds, usually toward the lower end, depending on the window size as it unpredictably effects the rate of convergence. This may be acceptable performance for some applications.9
|
|
|
|
|
|
|
|
|
 |