RE: [cssom-view] padding edge / border edge for offsetTop/offsetLeft?

Hi Sylvain, and sorry about the delay.

> > 1) IE does it this way.
> This is probably the main source of confusion on my side; my 
> strict-mode tests with IE7/IE8 show parent outer-border-edge 
> to element outer-border-edge; same with Opera. Safari and 
> Firefox behave as currently specified in CSSOM-View.

Ok, I haven't seen this myself but it is possible that you are
putting IE in another "mode" than I do. But could it be possible
that your own example actually isn't getting the offsetParent
you expect so the parent border is added because the parent
element is actually traversed on the way to the real offsetParent?

As has already been discussed, IE's hasLayout affects the
offsetParent chain. IE's offset algorithm is also partly broken
when hasLayout is not in effect. I guess this is because an
element with hasLayout=false delegates its geometry calculations
to an ancestor with hasLayout=true (this could recurse all the
way up to BODY) and this opens up to bugs. In this respect I'd
say that triggering hasLayout is what gets you closest to an
expected (and standard) behaviour as elements will avoid IE's
"layout optimization" and do full geometry calculations
on each element level.

Below are a few tests for illustration. You find the code at the
end of this mail. Each test table shows offsetLeft/offsetParent
in different situations.

[1. code unchanged = all DIV.position=static]
               IE7        IE7        CB
DIV.hasLayout  false      true       
div1           4/BODY     4/BODY     BODY
div2           39/BODY    33/div1    div1
div3           67/BODY    12/div2    div2

hasLayout=false:
In the table above you can see that for hasLayout=false all the
DIVs have an offsetLeft including all borders/padding/margin
down to each element's border edge but this is because
offsetParent=BODY for all the elements. BODY is the first 
ancestor with hasLayout=true and IE doesn't seem to want to do
offset calcs against non-hasLayout nodes, which makes sense
when knowing about hasLayout. The padding-edge to border-edge
rule seems ok.

hasLayout=true:
When triggering hasLayout through "div {zoom:1}" you can see
that the closest parent is chosen as offsetParent as each parent
now hasLayout so IE can calc offsets against them. The 
offsetParent coincides with the Containing Block (CB) and the
offsets are padding-edge to border-edge.

[2. code + div2.pos=rel]
               IE7        IE7        CB
DIV.hasLayout  false      true       
div1           4/BODY     4/BODY     BODY
div2           39/HTML    39/HTML    div1
div3           67*/div2   12/div2    div2
*=bug

hasLayout=false:
When making div2 a positioning context we can see that this
makes div3 choose div2 as its offsetParent. Though, offsetLeft
still reports offset against BODY (67)! This is a bug and my 
guess is it's because the offsetParent doesn't have hasLayout.
(Also note that div2 itself now uses HTML instead of BODY as its
offsetParent. It seems IE skips over BODY and refers directly to
HTML for elements with "top" positioning contexts.) 
The padding-edge to border-edge rule seems ok.

hasLayout=true:
With hasLayout on div2, div3 will now get a correct offset.
div2 will include div1's border in its own offset as the
offsetParent is now HTML.
(Note that div2.offsetParent now deviates from CB.)
The padding-edge to border-edge rule seems ok.

[3. code + div1.pos=rel and div3.pos=abs,left/top=0]
               IE7        IE7        CB
DIV.hasLayout  false      true       
div1           4/HTML     4/HTML     BODY
div2           39/div1    33/div1    div1
div3           10/div1    4/div1     div1

hasLayout=false:
Just as in the previous test we have an offsetParent (div1)
without hasLayout. This means both div2 and div3 will report 
an offset relative to BODY (bug) so will include div1's margin
and border.
The padding-edge to border-edge rule seems ok.

hasLayout=true:
The offsetParent div1 now hasLayout and offsets are correct.
The padding-edge to border-edge rule seems ok.

So, to sum up; if you want to avoid bugs and surprises I'd
suggest you trigger hasLayout on your offsetParents. Let me 
know if you had other cases that don't follow the scheme
presented here!
You can read more on what properties trigger hasLayout here:
http://www.brunildo.org/test/hh.html
http://onhavinglayout.fwpf-webdesign.de/hack_management/
(Personally I tend to trigger hasLayout anyway in many cases
for the main sections of pages, through overflow settings and
such.)

> > 2) It's compatible with CSS 2.1's positioning with left/top
> >    attributes for position:absolute elements.
> >    I e, if you have an absolutely positioned element that
> >    you assign
> >      left:10px
> >    to, then offsetLeft will also return 10 as the offset is
> >    measured from the same points (padding edge to border edge)
> >    as used by CSS positioning. Of course assuming here that the
> >    offsetParent and Containing Block elements are the same,
> >    which they usually are in standards-mode IE, but not in the
> >    current CSSOM proposal.
> This is good background, thank you.

Oops, my bad here: double-checking the CSS spec shows that
absolute positioning uses padding edge to *margin* edge as offset
points (have I been dreaming or what?). So it doesn't seem we can
say there is a correlation here. The padding edge to border edge
scheme I guess was invented as a zero offset means the elements'
borders "touch". Anyone, please correct me if I'm wrong here.
The relevant links are here:
http://www.w3.org/TR/CSS21/visuren.html#position-props
http://www.w3.org/TR/CSS21/visudet.html#containing-block-details

Best regards
Mike



<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
  <style type="text/css">
    * { border: 0px solid gray; margin: 0; padding: 0; }
    #div1 { margin: 4px; border-width: 2px; padding: 1px; }
    #div2 { margin: 32px; border-width: 16px; padding: 8px; }
    #div3 { margin: 4px; border-width: 2px; padding: 1px; }
    /* Uncomment to trigger hasLayout: */
    /*div { zoom: 1; }*/
    /* Uncomment for test/table 2: */
    /*#div2 { position: relative; }*/
    /* Uncomment for test/table 3: */
    /*#div1 { position: relative; }
    #div3 { position: absolute; left: 0px; top: 0px; }*/
  </style>
</head>
<body>
  <div id="div1">
    <div id="div2">
      <div id="div3">div3</div>
      div2
    </div>
    div1
  </div>
  <script type="text/javascript">
    function printOffsetData( elem ) {
      document.write( "Element: " + 
        (elem.id || elem.tagName) + "<br>" );
      document.write( "offsetLeft: " + 
        elem.offsetLeft + "<br>" );
      document.write( "offsetParent: " + 
        (elem.offsetParent && 
        (elem.offsetParent.id || elem.offsetParent.tagName)) + 
        "<br>" );
      document.write( "hasLayout: " + 
        ((elem.currentStyle && "hasLayout" in elem.currentStyle) ? 
        elem.currentStyle.hasLayout : "N/A") + "<br>" );
      document.write( "<br>" );
    }
    printOffsetData( document.documentElement );
    printOffsetData( document.body );
    printOffsetData( document.getElementById("div1") );
    printOffsetData( document.getElementById("div2") );
    printOffsetData( document.getElementById("div3") );
  </script>
</body>
</html>

Received on Monday, 28 April 2008 21:03:00 UTC