Blog Entry - 16th September 2007 - Programming - CSS

Overflowing with Good Ideas


1. Introduction

I have been trying to create a multi-level tree. Nothing unusual about that.

I have the following requirements:-

  1. Each entry in the tree would appear on its own line, and, critically, text lines should not wrap, except at explicit line-breaks.
  2. Each group of entries would be wrapped in a bordered div element.
  3. The bordered div element should at least fit its contents. I.e. the text should not overflow the border of the div.
  4. The div and any parent boxes should be as wide as, but no wider than, the longest (non-wrapped) text line inside.
  5. These rules should apply even where the tree is inside a div that has overflow:auto; set on it.

Here is a screen-shot of an example (except for rule 5).

I thought this would be a hard problem. But in fact it was relatively easy to solve, because there is already a feature of the CSS specification called "shrink-to-fit", which does a lot of the work for me.

In looking at this, I had occasion to try to fathom the CSS2 Visual Formatting Model. Not an easy read at all, and I still don't understand it. Or at least, my understanding fades very quickly after reading it. No wonder it is so hard to implement.

I thought I would share with you the results of my study. My explanations need quite a lot of polishing, but I don't have the time, so I thought would share with

2. The Main Requirements

To re-iterate, the two main requirements are:-

  • there should be no word wrapping, and lines should not break, except at a br, p etc
  • the borders of each of the parent div elements should shrink-wrap to the longest text line, with no overflow.

I am not here going to talk about border, padding, or content boxes. Just the basic calculation principles for "content width". I am also not going to try to deal with all the subtle rendering quirks which browsers may show.

3. Solution

Lets get to the solution first, because the rest of this blog entry is so long.

My preferred solution uses a float and in this example uses white-space: nowrap (and the deprecated nowrap element property for IE) to force lines not to be wrapped.

The shrink to fit rules for floated elements then do the job of shrink-wrapping the box borders onto these unwrapped lines.


<h2>Solution - Float Left</h2>

<div style="border:1px solid blue; padding:10px; width:200px; height:150px; overflow:auto; white-space:nowrap;" noWrap>

	<div style="border:1px solid purple; padding:10px; float:left;">
		<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
			<div style="border:1px solid purple; padding:10px;">
				<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
			</div>
		</div>
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
		</div>
	</div>

</div>

editpop-upiframe

One slight niggle is that because the element is taken out of the flow, the padding seems a bit messed-up, particularly on (gasp) Opera.

For instance, it seems that Opera's padding is too much or too little on the right, and when a vertical overflow happens, it seems to loose track of the padding at the bottom when a scrollbar is added.

This may be standards compliant, but it looks a little scruffy.

I have tackled this with a bit of a hack: a white bordered div rather than padding, for top left and right borders, and a separate cleared div at the bottom. This is the only cross-browser answer I could find, and it may be a problem if you need transparent borders, as I think IE has an issue with that.

Any better ideas?


<h2>Solution - Float Left - White Border / Div At Bottom</h2>

<div style="border:1px solid blue; padding:0px; width:200px; height:150px; overflow:auto; white-space:nowrap;" noWrap>

	<div style="border:solid white; border-width:10px 10px 0px 10px; float:left;">
	<div style="border:1px solid purple; padding:10px;">
		<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
			<div style="border:1px solid purple; padding:10px;">
				<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
			</div>
		</div>
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	</div>
	<div style="clear:both; height:10px; overflow:hidden;"></div>
</div>

editpop-upiframe

4. Normal boxes

4.1 Formatting Rules

The starting point for understanding the solution is to understand the rules that apply to normal boxes(called Block-level, non-replaced elements in normal flow).

They obey (or should obey) two basic rules:-

  • They don't fit the width of their content, they are either the width of (or a pecentage of the width of) their containing element, or the fixed width you have set for them.
  • If any text content within them cannot be word-wrapped to fit within the parent width, or any box content has a fixed width greater than the parent width, it will overflow (and look ugly).

The one notorious exception to these rules is Internet Explorer : if Internet Explorer cannot fit content within a box, it will expand that box until it can fit that content. More on this later.

To quote Section 10.3.3 of the CSS2 Specification:

The following constraints must hold among the used values of the other properties:-

'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + scrollbar width (if any) = width of containing block

If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + scrollbar width (if any) (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero.

If all of the above have a computed value other than 'auto', the values are said to be "over-constrained" and one of the used values will have to be different from its computed value. If the 'direction' property of the containing block has the value 'ltr', the specified value of 'margin-right' is ignored and the value is calculated so as to make the equality true. If the value of 'direction' is 'rtl', this happens to 'margin-left' instead.

If there is exactly one value specified as 'auto', its used value follows from the equality.

If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality.

If both 'margin-left' and 'margin-right' are 'auto', their used values are equal. This horizontally centers the element with respect to the edges of the containing block.

The "scrollbar width" value is only relevant if the user agent uses a scrollbar as its scrolling mechanism. See the definition of the 'overflow' property.

Very difficult to comprehend, I found.

In order to comprehend it, I found this table useful, as it is important to understand the range of possible values each item in the formula supports. When I refer to fixed, I mean that it does not depend on any other box's width; em widths are relative to the calculated font-size, so treat them as fixed for these purposes.

style possible widths can have auto?
margin-left auto; fixed; or % of width of containing block yes
border-left-width fixed no
padding-left fixed; or % of width of containing block no
width auto; fixed; or % of width of containing block yes
padding-right fixed; or % of width of containing block no
border-right-width fixed no
margin-right auto; fixed; or % of width of containing block yes
scrollbar 16px(IE); 15px(Others) n/a

The basic calculation is top down:-

  1. Calculate the width and height of the viewport (the window)
  2. Calculate the width of the html within the viewport (ignore if you are IE in quirks mode).
  3. Calculate the width of the body within html (or viewport if you are IE in quirks mode).
  4. For each child-box of body, calculate its width
  5. For each of those child-boxes, get its child-boxes
  6. For each of those child-boxes, calculate its width, then goto 5.

As noted, the calculated width is going to either be fixed or or width of (or percentage of width of) its parent box (which itself ay be fixed or dependent on its parent box).

4.2 Overflow

If a box cannot be made to fit, then an overflow will happen (i.e. the box will overflow the calculated width of the parent box).

Overflow can happen in two ways:-

  1. The box width is calculated as wider than its container, that box will overflow its container.
  2. You have some non-breaking text within that box, which is longer than the width of the box, that text will overflow its box.

To quote the CSS spec again:-

Generally, the content of a block box is confined to the content edges of the box. In certain cases, a box may overflow, meaning its content lies partly or entirely outside of the box, e.g.:

A line cannot be broken, causing the line box to be wider than the block box.

A block-level box is too wide for the containing block. This may happen when an element's 'width' property has a value that causes the generated block box to spill over sides of the containing block.

An element's height exceeds an explicit height assigned to the containing block (i.e., the containing block's height is determined by the 'height' property, not by content height).

A box is positioned absolutely.

It has negative margins.

4.3 Example - Normal Box

The starting point is that the browser will try to fit everything into the parent box.

Here is an example for equal or lesser widths:-


<h2>Example - Static - Width Auto</h2>
<h3>Expands to fit viewport</h3>

<div style="border:1px solid blue; padding:10px;">

	<div style="border:1px solid purple; padding:10px;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<h2>Example - Static - Inner Width Percentage</h2>
<h3>Expands to fit viewport</h3>

<div style="border:1px solid blue; padding:10px;">

	<div style="border:1px solid purple; padding:10px; width:50%;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<h2>Example - Static - Inner Width Fixed</h2>
<h3>Expands to fit viewport</h3>

<div style="border:1px solid blue; padding:10px;">

	<div style="border:1px solid purple; padding:10px; width:200px;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

editpop-upiframe

4.4 Example - Normal Box - Overflow

Here are some overflow examples.

Remeber that Internet Explorer does not do overflow, and will expand the box and parent boxes if minimum width of the box is greater than the parent.


<h2>Example - Static - Width 200px - Inner Box 250px</h2>
<h3>Box overflows for all browsers except IE, and text overflows box!</h3>
<h3>IE expands each box separately to fit</h3>

<div style="border:1px solid blue; padding:10px; width:200px;">
	<div style="border:1px solid purple; padding:10px; width:250px;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand.Expand.Expand.Expand.Expand.Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand.Expand.Expand.Expand.</p>
	</div>
	</div>
</div>

editpop-upiframe

Here is how it looks on Opera and IE:-

4.5 Example - Normal Box - Overflow Auto

When you set overflow:auto on an element then scrollbars will be added and the overflowing box will be hidden. However, importantly for my problem:-

  • If the box width is auto then it is calculated based on the actual visible width of its parent, and not the potential width if the inner box were allowed to expand to the potential unlimited width afforded by overflow:auto. See example 1 below. This is logical as normal boxes depend on a container box for width auto calculations, so if you take that away, what do you have : infinity.
  • Unbreakable text will still overflow its box.

<h2>Example - Static - Width 200px/Overflow Auto - Inner Box Auto</h2>
<h3>Box overflows for all browsers except IE, and text overflows box!</h3>
<h3>IE expands each box separately to fit</h3>

<div style="border:1px solid blue; padding:10px; width:200px; overflow:auto;">
	<div style="border:1px solid purple; padding:10px;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand.Expand.Expand.Expand.Expand.Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand.Expand.Expand.Expand.</p>
	</div>
	</div>
</div>

<h2>Example - Static - Width 200px/Overflow Auto - Inner Box 250px</h2>
<h3>Box overflows for all browsers except IE, and text overflows box!</h3>
<h3>IE expands each box separately to fit</h3>

<div style="border:1px solid blue; padding:10px; width:200px; overflow:auto;">
	<div style="border:1px solid purple; padding:10px; width:250px;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand.Expand.Expand.Expand.Expand.Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand.Expand.Expand.Expand.</p>
	</div>
	</div>
</div>

editpop-upiframe

This is how these look on Opera. I don't give examples for IE as all boxes expand.

5. Progress So Far Towards Solution

What we have learned so far is that:-

The overflow rules prevent use of normal boxes as a solution for my problem, because boxes do not fit to contents.

Internet Explorer can get towards my solution, because its boxes do expand; the only down-side is that boxes expand to their content size and not the largest of the sub-boxes, so the result is boxes of staggered widths.

6. Forcing lines not to break

One of the first aspects that is going to be required in any solution, is a means of stopping word-wrap. This is needed because:-

  • Internet Explorer's non-standard overflow behaviour still tries to wrap words to the minimum possible.
  • All of the other solutions specified below (which use a shrink to fit rule in the CSS2 rules) will shrink to fit to the smallest possible size, if a containing box is too small, and we want the "smallest possible" that in which words are not wrapped.

I started a quick look at this issue on various search engines, and it feels like the tip of the iceberg.

There are potential issues with all of the options below, including failure to validate, lack of consistent cross-browser support or implementation, etc, etc, so the options below are really to experiment with, and perfection (however I may define it) may not be achievable.

The results are pretty good in the most recent browsers as far as I can tell.

6.1 white-space: nowrap;

This is the standards way of doing it.

Setting white-space to nowrap means that lines are only broken at explicit line ends. E.g. br, end of a p. Lines are not word-wrapped.

I am not sure how widely this is supported yet, but it is the future.

A visit to Quirksmode will probably tell.


<div style="border: 1px solid blue; overflow:auto; width:200px; height:100px;">
<p style="white-space: nowrap">Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap.</p>
</div>

editplay

6.2 noWrap

The second way is the deprecated noWrap attribute.

This is a deprecated property which Microsoft at least continues to apply to BODY, DD, DIV, DT, TD, TH

HTML 4 only applies it to TD, and TH, and deprecates it in favour of the CSS 'white-space' attribute.

As noted, Internet Explorer still supports this on DIV elements, and it seems to work quite well. It is also inherited by all children, so you only need to use it once on the outer div. Needless to say you can only use this for Internet Explorer.


<div style="border: 1px solid blue; overflow:auto; width:200px; height:100px;">
<div nowrap><p>Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap.</p></div>
</div>

editplay

6.3 &nbsp;

The next way is &nbsp; (non-breaking space).

I am not sure if all browsers will always obey this, and I am not sure I would recommend it, given the availability of other options stated above.


<div style="border: 1px solid blue; overflow:auto; width:200px; height:100px;">
<p>Please&nbsp;do&nbsp;not&nbsp;wrap.&nbsp;Please&nbsp;do&nbsp;not&nbsp;wrap</p>
</div>

editplay

6.4 <nobr>

I dont know much about this element. I think it is old, and non-standard, but it is an option, although it does not validate.


<div style="border: 1px solid blue; overflow:auto; width:200px; height:100px;">
<p><nobr>Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap. Please do not wrap.</nobr></p>
</div>

editplay

6.5 Conclusion

For my purposes I am going to stick with the first two options, which should hopefully cover IE and other browsers. For belt and braces I might add the nobr element.

7. Shrink To Fit - display:table;

7.1 Explanation of Shrink To Fit Rules

So we can now force lines to their full length. Now we need to force boxes (and their parent boxes) to stretch to their contents (except for IE of course) to get over the overflow problem.

I looked on the internet, and the solution I came accross was to use display:table.

What it does is to cause browsers to treat the element and each sub-child as cell / column (I am not sure which, perhaps both) and to apply the table formatting rules. Extracted below:-

[1] Calculate the minimum content width (MCW) of each cell: the formatted content may span any number of lines but may not overflow the cell box. If the specified 'width' (W) of the cell is greater than MCW, W is the minimum cell width. A value of 'auto' means that MCW is the minimum cell width.

[2] Also, calculate the "maximum" cell width of each cell: formatting then content without breaking lines other than where explicit line breaks occur.

[3] For each column, determine a maximum and minimum column width from the cells that span only that column.

[4] The minimum is that required by the cell with the largest minimum cell width (or the column 'width', whichever is larger). The maximum is that required by the cell with the largest maximum cell width (or the column 'width', whichever is larger).

[5] For each cell that spans more than one column, increase the minimum widths of the columns it spans so that together, they are at least as wide as the cell. Do the same for the maximum widths. If possible, widen all spanned columns by approximately the same amount.

[6]This gives a maximum and minimum width for each column.

The width of the table is then calculated as:-

[A]If the 'table' or 'inline-table' element's 'width' property has a specified value (W) other than 'auto', the property's computed value is the greater of W and the minimum width required by all the columns plus cell spacing or borders (MIN). If W is greater than MIN, the extra width should be distributed over the columns.

If the 'table' or 'inline-table' element has 'width: auto', the computed table width is the greater of the table's containing block width and MIN. However, if the maximum width required by the columns plus cell spacing or borders (MAX) is less than that of the containing block, use MAX.

It is the latter calculation - - which is the key - it is the shrink to fit calculation.

It is quite difficult to follow, but as best as I can make out, there are three values:-

  1. The width (PARENT WIDTH) of the table's parent box.
  2. The minimum (TABLE MIN) width required by the table (word wrapping where possible).
  3. The maximum (TABLE MAX) width required by the table (effectively assuming white-space:nowrap)

The width of 1 (PARENT WIDTH) determines whether 2 (TABLE MIN) or 3 (TABLE MAX) is used.

  • If 1 (PARENT WIDTH) is greater than 3 (TABLE MAX), then use 3 (TABLE MAX).
  • If 1 (PARENT WIDTH) is less than 3 (TABLE MAX) but greater than 2 (TABLE MIN), then use 1 (PARENT WIDTH).
  • If 1 (PARENT WIDTH) is less then 2 (TABLE MIN), then use 2 (TABLE MIN).

7.2 Examples (Not IE)

Lets use some examples (not using tables) to illustrate.

<h2>Rule 1: PARENT WIDTH wider than TABLE MAX</h2>

<div style="border:1px solid blue; padding:10px; width:700px;">

	<div style="border:1px solid purple; padding:10px; display:table;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>

<h2>Rule 2: PARENT WIDTH less than TABLE MAX, but greater than TABLE MIN</h2>
<h3>Expands to PARENT WIDTH</p>

<div style="border:1px solid blue; padding:10px; width:300px;">

	<div style="border:1px solid purple; padding:10px; display:table;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>

<h2>Rule 3: PARENT WIDTH less than TABLE MAX and less than than TABLE MIN</h2>
<h3>Should overflow to TABLE MIN</h3>

<div style="border:1px solid blue; padding:10px; width:100px;">

	<div style="border:1px solid purple; padding:10px; display:table;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>

<h2>Solution: Force TABLE MIN be same as TABLE MAX</h2>
<h3>Should overflow to TABLE MAX</h3>

<div style="border:1px solid blue; padding:10px; width:200px;">

	<div style="border:1px solid purple; padding:10px; display:table; white-space:nowrap;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>

<h2>Solution: Force TABLE MIN be same as TABLE MAX</h2>
<h3>overflow:auto;</p>

<div style="border:1px solid blue; padding:10px; width:200px; overflow:auto;">

	<div style="border:1px solid purple; padding:10px; display:table; white-space:nowrap;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

editpop-upiframe

7.3 Conclusion

This option solves the overflow problem simply for all browsers (except IE). It ensures that the width of the element is never less than its minimum width to ensure no overflow.

However, minimum width is not enough for my use, I want the minimum width to be the maximum width. So the solution is to use white-space:nowrap to ensure that the minimum width is the same as the maximum width. See the example above.

8. Progress So Far Towards Solution

8.2 Solution So Far

<h2>Solution: Force TABLE MIN be same as TABLE MAX</h2>
<h3>overflow:auto;</p>

<div style="border:1px solid blue; padding:10px; width:200px; overflow:auto;">

	<div style="border:1px solid purple; padding:10px; display:table; white-space:nowrap; width:1px;" nowrap>
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

editpop-upiframe

What we have learned so far is that:-

8.2 Internet Explorer

For Internet Explorer, we can force boxes and parent boxes to expand to the width of the longest line inside, simply by setting nowrap and width:1px on a containing div element.

This is not perfect because the result is staggered : individual child boxes only expand to the width of their line, rather than to the width that the largest line causes.

8.3 Other Browsers

For other browsers, if they support display:table, there is a shrink-to-fit calculation which does the job, if we set white-space:nowrap on the parent element.

8.4 Any Better?

Can we do any better, because IE is neither one thing nor the other.

9. Float and Absolute : Shrink-To-Fit

9.1 The same rules apply as for tables

At first I thought this was going to be a hard problem to solve to get any further on.

Then I bothered to read Chapter 10 of CSS2 Specification, and low it showed me (and in fact reminded me) that shrink-to-fit applies as well to: floats, and absolutely positioned elements.

And better still, Internet Explorer does it properly (almost! - there is the infamous 3 pixel text jog on floated elements - and it still does not do overflow - but does it really matter?).

Effectively shrink to fit applies to:-

  • tables (or elements with display:table set, for browsers that implement it)
  • elements with float:left (or right) set
  • elements with position:absolute

To quote from 10.3.5 of the CSS2 Specification:-

10.3.5 Floating, non-replaced elements

If 'width' is computed as 'auto', the used value is the "shrink-to-fit" width.

Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm. Roughly:

10.3.7 Absolutely positioned, non-replaced elements

'left' and 'width' are 'auto' and 'right' is not 'auto', then the width is shrink-to-fit. Then solve for 'left'

'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'

Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm.

So we have exactly the same calculations as for a table, and we are in effect home and dry.

9.2 Float Examples


<h1>FLOAT EXAMPLES</h1>
<h2>Rule 1: PARENT WIDTH wider than TABLE MAX</h2>
<h3>Expands to TABLE MAX</h3>

<div style="border:1px solid blue; padding:10px; width:700px;">

	<div style="border:1px solid purple; padding:10px; float:left;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Rule 2: PARENT WIDTH less than TABLE MAX, but greater than TABLE MIN</h2>
<h3>Expands to PARENT WIDTH</p>

<div style="border:1px solid blue; padding:10px; width:300px;">

	<div style="border:1px solid purple; padding:10px; float:left;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Rule 3: PARENT WIDTH less than TABLE MAX and less than than TABLE MIN</h2>
<h3>Should overflow to TABLE MIN</h3>

<div style="border:1px solid blue; padding:10px; width:100px;">

	<div style="border:1px solid purple; padding:10px; float:left;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Solution: Force TABLE MIN be same as TABLE MAX</h2>
<h3>Should overflow to TABLE MAX</h3>

<div style="border:1px solid blue; padding:10px; width:200px;">

	<div style="border:1px solid purple; padding:10px; float:left; white-space:nowrap;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Solution: Force TABLE MIN be same as TABLE MAX</h2>
<h3>overflow:auto;</p>

<div style="border:1px solid blue; padding:10px; width:200px; overflow:auto;">

	<div style="border:1px solid purple; padding:10px; float:left; white-space:nowrap;" nowrap>
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

editpop-upiframe

The only problem (apart from IE) is that Opera does not respect the padding-bottom when overflow:auto.

9.3 position:absolute Examples

Here are some examples with position:absolute.

The containing box needs position:relative to position correctly. Overflows will result unless overflow:auto is set, so some examples now have an explicit height.


<h1>POSITION:ABSOLUTE EXAMPLES</h1>
<h2>Rule 1: PARENT WIDTH wider than TABLE MAX</h2>
<h3>Expands to TABLE MAX</h3>

<div style="border:1px solid blue; padding:10px; width:700px; height:160px; position:relative;">

	<div style="border:1px solid purple; padding:10px; position:absolute;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Rule 2: PARENT WIDTH less than TABLE MAX, but greater than TABLE MIN</h2>
<h3>Expands to PARENT WIDTH</p>

<div style="border:1px solid blue; padding:10px; width:300px; height:160px; position:relative;">

	<div style="border:1px solid purple; padding:10px; position:absolute;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Rule 3: PARENT WIDTH less than TABLE MAX and less than than TABLE MIN</h2>
<h3>Should overflow to TABLE MIN</h3>

<div style="border:1px solid blue; padding:10px; width:100px; height:160px; position:relative;">

	<div style="border:1px solid purple; padding:10px; position:absolute;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Solution: Force TABLE MIN be same as TABLE MAX</h2>
<h3>Should overflow to TABLE MAX</h3>

<div style="border:1px solid blue; padding:10px; width:200px; height:150px; position:relative;">

	<div style="border:1px solid purple; padding:10px; position:absolute; white-space:nowrap;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Solution: Force TABLE MIN be same as TABLE MAX</h2>
<h3>overflow:auto;</p>

<div style="border:1px solid blue; padding:10px; width:200px; height:150px; overflow:auto; position:relative;">

	<div style="border:1px solid purple; padding:10px; position:absolute; white-space:nowrap;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

<br>
<div style="clear:both"><!--Clear the float for the next example--></div>

<h2>Solution: Force TABLE MIN be same as TABLE MAX</h2>
<h3>overflow:auto; Bordered div for padding</p>

<div style="border:1px solid blue; width:200px; height:150px; overflow:auto; position:relative;">

	<div style="border:10px solid white; position:absolute; white-space:nowrap;">
	<div style="border:1px solid purple; padding:10px; position:absolute; white-space:nowrap;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>
	</div>

</div>

editpop-upiframe

9.4 Perfection?

Not really.

The only remaining problem, is that all of the browsers differ in how they handle padding etc inside boxes with overflow:auto; set.

The only solution I have so far, is that in section 2.

10. Conclusion

Hurrah, shrink to fit was there all along.

I have set out my preferred solution in section 2.

I would like to propose that for Section 10.3.3 Block-level, non-replaced elements in normal flow that there be another value for the width element, called shrink-to-fit (or as someone else has suggested content) which triggers the shrink-to-fit algorithm. I suppose that this is already present with the display:table option, but not quite so explicit.

11. Further Ideas

11.1 % of content

If you apply a percentage width within a float, you can effectively get a kind of % of content measure, box-model issues aside.


<h1>FLOAT EXAMPLES</h1>
<h2>Rule 1: PARENT WIDTH wider than TABLE MAX</h2>
<h3>Expands to 50% of TABLE MAX effectively</h3>

<div style="border:1px solid blue; padding:10px; float:left;">

	<div style="border:1px solid purple; padding:10px; width:50%;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>

editpop-upiframe

11.2 JavaScript to Calculate Content Width

You can go even further and use JavaScript to calculate the maximum width of an object with unbroken lines.


<html>
<head>
<script>

function go()
{
	var element = document.getElementById("testBox");
	var maxWidth = HTMLElement_GetContentMaximumWidth(element);
	element.style.width = maxWidth;
}

function HTMLElement_GetContentMaximumWidth (
	element /*: HTMLElement*/,
	box /*: enum_BOX */
) /*: int*/
{
	var width /*: int*/ = 0;
	var elementStyle /*: CSSStyleDeclaration*/ = element.style;
	var previousPosition /*: String*/ = elementStyle.position;
	var previousWidth /*: String*/ = elementStyle.width;
	var previousWhitespace /*: String*/ = elementStyle.whiteSpace;
	var previousNoWrap /*: Boolean*/ = !!(element.noWrap);

	elementStyle.position = "absolute";
	elementStyle.width = "auto";
	elementStyle.whiteSpace = "nowrap";
	element.noWrap = true;

	width = element.offsetWidth;

	elementStyle.position = previousPosition || "";
	elementStyle.width = previousWidth || "";	
	elementStyle.whiteSpace = previousWhitespace || "";	
	element.noWrap = previousNoWrap;	

	width = isNaN(width) ? element.offsetWidth : width; 

	return width;
}

</script>
</head>
<body>

<p onclick="go()">Click here to set the following box to content width</p>

<div id="testBox">

	<div style="border:1px solid purple; padding:10px;">
	<div style="border:1px solid purple; padding:10px; margin-bottom:10px;">
		<div style="border:1px solid purple; padding:10px;">
			<p style="margin:0px;">Expand. Expand. Expand. Expand. Expand. Expand.</p>
		</div>
	</div>
	<div style="border:1px solid purple; padding:10px;">
		<p style="margin:0px;">Expand. Expand. Expand. Expand.</p>
	</div>
	</div>

</div>


</body>
</html>

editpop-upiframe

Thats enough, for now...


Comment(s)


No comments made on this entry.


Leave a comment ...


{{PREVIEW}} Comments stopped temporarily due to attack from comment spammers.


Other Recent Entries in CSS