IE properly measures top and left values for absolutely positioned elements from the edges of its positioned parent, but bottom and right are handled incorrectly. Bottom and right are always measured from the edge of the body element (IE 5.X) or the document (IE 6.x). This is fine if the element is justified on the right side of the page, but not good when it is in a column on the left side of the page. The example shows this scenario. The simple right: 0; won't work. To get the correct position, we are forced to calculate the offset from the parent's left edge.
Fortunately, by setting the position attribute of the parent element to relative, the calculation is simplify. All positioning values are from the child's parentNode. And, IE provides us with their proprietary expression() function for CSS. The expression function gives the ability to use calculated values in CSS. This function is the perfect hack because other browsers ignore it.
This technique is for IE 6 and lower. IE 7 supports compliant technique.
Use conditional comments
<!--[if lt IE 7]>
<style type="text/css">
.flush-right{
position:absolute;
top: position-static;
left: expression((parentNode.offsetWidth - offsetWidth) + 'px');
}
</style>
<![endif]-->
to separate IE7 and compliant browser code from IE6 and IE 5 code.
The calculation is similar to the one used with JavaScript positioning; although, the child element's current position is not used. We need to subtract the width of the positioned element from the width of the parent element and we have the needed value. All we do is add this line to the CSS:
left: expression((parentNode.offsetWidth - offsetWidth) + 'px');
...and our IE CSS is ready.
CSS: .relPos { position: relative; text-align: left; } .flush-right{ position:absolute; top: position-static; left: expression((parentNode.offsetWidth - offsetWidth) + 'px'); } .float-left { width: 48%; float: left; } .float-right { width: 48%; float: right; }
A cross browser version for flush-right
might be the following:
.flush-right{ position:absolute; /* All browsers */ top: auto; /* Standards browsers */ top: position-static; /* IE all others ignore this as invalid*/ right: 0; /* All except IE */ left: expression(parentNode.offsetWidth - offsetWidth + 'px'); /* IE only */ }
The same issue on calculating the position exists here as in Positioning with JavaScript. Some tag types cannot be the offset parent, at least in IE 5.0, and you'll have to calculate the position from a common offset parent. The formula is:
left: expression(offsetLeft - parentNode.offsetLeft + 'px');
Using this, however, becomes more complicated as you'll need to determine the version of IE being used. An alternative is to enclose everything in a positioned div or span.
The CSS above used top: auto for standards
compliant browsers followed by the non-standard
top: position-static; for IE.
The CSS is no longer standards
compliant,
but this deviation from standards is needed for 80% of the users
and doesn't impact compliant
browsers.
HTML: <div class="float-left"> <p class="relPos float-left">Lorem ipsum... <img class="flush-right" src="eyes.gif" width="24" height="13" border="0" /> </p> </div> <div class="float-right"> <p class="relPos">Lorem ipsum... <img class="flush-right" src="devil.gif" width="64" height="64" border="0" /> </p> </div>
The code above will render with the two column layout below. The text of the paragraphs was left out of the code sample to fit the page.
Putting the paragraphs (p) in floated divisions (div) is not required for all browsers. But, Opera and older IE browsers do not consider floated paragraphs (p) as positioning containers. They will, however, use them if they are in floated divisions (div). Using a parent division frequently solves problems.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Mauris metus. Sed vitae quam. Morbi risus mi, laoreet ut, consequat ac, rutrum vel, mi. Praesent ultrices nonummy quam. Mauris iaculis. Maecenas posuere, purus nec tincidunt pulvinar, tellus ligula tincidunt nisl, sed dictum lectus sem id nisl. Nunc quis nisl. Vivamus id orci sit amet lacus aliquam pulvinar. Aliquam cursus scelerisque sapien. Donec eleifend felis. In hac habitasse platea dictumst. Proin venenatis vulputate tellus.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Mauris metus.
Sed vitae quam. Morbi risus mi, laoreet ut, consequat ac, rutrum vel, mi. Praesent ultrices nonummy quam.
Mauris iaculis. Maecenas posuere, purus nec tincidunt pulvinar, tellus ligula tincidunt nisl,
sed dictum lectus sem id nisl. Nunc quis nisl. Vivamus id orci sit amet lacus aliquam pulvinar.
Aliquam cursus scelerisque sapien. Donec eleifend felis. In hac habitasse platea dictumst.
Proin venenatis vulputate tellus.
Now we have covered 90% or more of the browsers in use.
Using parentNode.offsetWidth doesn't always yield correct results. The offsetWidth includes borders, but we want the width inside of the borders otherwise the positioned element will be on top of the right border.
An element's clientWidth property normally returns the internal width that we need, and left: expression((parent.clientWidth - offsetWidth) + 'px'); would be the correct expression. However, in this context clientWidth returns zero in IE.
The IE property clientLeft normally provides the width of the left border and could be subtracted from the offsetWidth, but in this context it too returns 0.
So if you have borders thicker than 1px you'll have to hard code the amount to subtract like this:
left: expression((parentNode.offsetWidth - offsetWidth - (5 * 2) ) + 'px');
While this covers the browsers used by the majority of internet users, it doesn't work in all browsers, and there are some pitfalls. Konqueror, the only other browser available to me for testing, has a problem with the vertical positioning; this is covered at the end. Next, let's consider some details and caveats to prevent problems.