Re: [cssom-view] small update

On Fri, Mar 14, 2008 at 3:04 PM, Mike Wilson <mikewse@hotmail.com> wrote:
>
>  Hi Anne,
>

>  I have studied some of the differences between the various
>  approaches and made some comparison tables below. There are
>  lots of data here so please excuse (and inform me) if something
>  is not correct. The HTML file used for testing is at the end of
>  this mail.
>

Hi Chris,

I've made an example script to run the test, included below.

Aside from the bugs that occur in all FF/Op/Saf, there is a 1 clear,
fundamental difference in the handling of offsetTop/Left when the
offsetParent in question is BODY with position: relative.


>  offsetParent
>  ------------
>  This is the offsetParent returned for the respective elements
>  in the example file per browser/spec:
>
>  offsetParent for/in: IE7     FF2     CSSOM   [CB]    simple?
>  HTML                 null    null    null    ICB     null
>   BODY                null    null    null    HTML    HTML
>   div1(pos=rel)      HTML    BODY    BODY    BODY    BODY
>    div2a             div1    div1    div1    div1    div1
>    div2b             div1    div1    div1    div1    div1
>     div3a(pos=abs)   div1    div1    div1    div1    div1
>     div3b            div2b   div1    div1    div2b   div2b
>
>  Observations:
>
>  1) CSSOM differs from IE by letting div1 point to BODY instead
>    of HTML.
>
>    Q: What was the rationale for this change? (I agree that it
>       is "cleaner" not to special-case children of BODY to
>       instead point to HTML, but IE is the mother of these
>       properties so...)

It is no more of a special case for pointing to HTML than it is for
pointing to BODY. If offsetParent would be null, then the fallback
could be to use the root node (HTML). This is what IE seems to be
doing.

[snip]

>
>  *   The Firefox algorithm seems to be completely different and
>     I have no guess on how to interpret this.

It is a bug. Firefox subtracts the border width.

https://bugzilla.mozilla.org/show_bug.cgi?id=255754


>  3) As can be seen in both tables IE returns useful values for
>    BODY's offsetLeft property while CSSOM hardwires it to 0.
>
>    Q: What was the rationale for this deviation from IE's
>       behaviour?


Good question.


>
>  4) As can be seen in the upper table, CSSOM returns offsetLeft
>    = 67 for div1, which is the distance to the viewport edge or
>    margin edge of the |HTML| element, even though div1 reports
>    using BODY as offsetParent, thus:
>      div1.offsetLeft = <distance to viewport/HTML>
>      div1.offsetParent = BODY
>    It would be more consistent to use
>      div1.offsetLeft = <distance to HTML>
>      div1.offsetParent = HTML
>    or
>      div1.offsetLeft = <distance to viewport>
>      div1.offsetParent = null
>    or
>      div1.offsetLeft = <distance to BODY>
>      div1.offsetParent = BODY
>
>    This change from IE's behaviour leads to returning an
>    offsetParent that isn't the element used when calculating
>    offsets, which is inconsistent.

Good observation.

However when BODY has position: relative, things are different.

Firefox 2/Safari:

testBodyStatic
#div1
tagName       offsetLeft    offsetTop     offsetParent
DIV#div1......47............47............BODY#body

testBodyRelative
#div1
tagName       offsetLeft    offsetTop     offsetParent
DIV#div1......12............12............BODY#body

IE:
testBodyStatic
#div1
tagName       offsetLeft    offsetTop     offsetParent
DIV#div1......67............63............HTML#html

testBodyRelative
#div1
tagName       offsetLeft    offsetTop     offsetParent
DIV#div1......12............8.............BODY#body


Opera:
testBodyStatic
#div1
tagName       offsetLeft    offsetTop     offsetParent
DIV#div1......67............67............BODY#body

testBodyRelative
#div1
tagName       offsetLeft    offsetTop     offsetParent
DIV#div1......67............67............BODY#body


Observations:
testBodyRelative:
 1)  Firefox and Safari use BODY as offsetParent and derive their
offsetLeft/Top values from BODY.
 2) Opera: BODY is not special-cased

Insight:
 For getting the position of an element: using offsetTop:
 position: relative can be added to the container, BODY. This makes
body work like a "normal" offsetParent in all browsers except Opera.


Garrett Smith
Example HTML:
(example js, writeUpTree.js below)

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html id='html'>
       <head>
               <meta http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
                <script src="writeUpTree.js"></script>
               <title></title>
               <style type="text/css">
                       * {
                               border-color: gray;
                               border-style: solid;
                       }
                       html {
                               margin: 4px;
                               position: relative;
                               border-width: 2px;
                               padding: 1px;
                       }
                       body {
                               font-family: verdana, arial, helvetica,
sans-serif;
                               position: relative;
                               font-size: 77%;
                               margin: 32px;
                               border-width: 16px;
                               padding: 8px;
                       }
                       div {
                               height: 30px;
                               overflow: hidden;
                               text-align: right;
                       }
                       #div1 {
                               position: relative;
                               height: auto;
                               margin: 4px;
                               border-width: 2px;
                               padding: 1px;
                       }
                       #div2a {
                               margin: 32px;
                               border-width: 16px;
                               padding: 8px;
                       }
                       #div2b {
                               height: 60px;
                               margin: 32px;
                               border-width: 16px;
                               padding: 8px;
                       }
                       #div3a {
                               position: absolute;
                               left: 64px;
                               top: 64px;
                               margin: 4px;
                               border-width: 2px;
                               padding: 1px;
                       }
                       #div3b {
                               margin: 4px;
                               border-width: 2px;
                               padding: 1px;
                       }
               </style>
       </head>
       <body id='body'>
               <div id="div1">
                       <div id="div2a">div2a</div>
                       <div id="div2b">
                               <div id="div3a">div3a</div>
                               <div id="div3b">div3b</div>
                               div2b
                       </div>
                       div1
               </div>

               <pre id="results"></pre>

                <script>setTimeout(function(){

                var body = document.body,
                    bs = body.style,
                    bCssText = bs.cssText;

                function setUp() {
                    bs.cssText = bCssText;
                }

                function testBodyStatic() {

info("--------------------------------------------------------------------");
                    info("testBodyStatic");
                    bs.position = "static";
                    writeUpTree('div1');
                }

                function testBodyRelative() {
                    info("testBodyRelative");
                    bs.position = "relative";
                    writeUpTree('div1');

info("--------------------------------------------------------------------");
                }

                setUp();
                testBodyStatic();
                setUp();
                testBodyRelative();

                flushResults();
                }, 100);</script>
       </body>
</html>


var result = [];
var resultsEl;

function info() {
    resultsEl = document.getElementById('results');
    result[result.length] = Array.prototype.join.call(arguments,'');
}

function writeUpTree(id) {
    resultsEl = document.getElementById('results');
    info('#'+id);
    var headers = ["tagName", "offsetLeft", "offsetTop", "offsetParent"];

    var hs = [];
    for(var i =0; i< headers.length; i++)
        hs.push(headers[i], makeDots(headers[i], ' '));
    info(hs.join(''));

    var tn = document.getElementById(id);
    for(var p = tn, op = p.offsetParent;  op; p= p.parentNode,
op=p.offsetParent) {
    try{
        var s = p.tagName + (p.id ? "#" + p.id : "");
        info(
                s, makeDots(s),
                p.offsetLeft, makeDots( String(p.offsetLeft)),
                p.offsetTop, makeDots(String(p.offsetTop)),
                op.tagName + (op.id ? "#" + op.id : "")
                );}catch(e){alert(e.message);}
    }
    info(''); // XXX Opera: Must pass empty string to writeln;
}
function makeDots( coll, ch ) {
    return Array.prototype.join.call({length: 14 - coll.length+1}, ch||'.');
}

function flushResults() {
    resultsEl = document.getElementById('results');
    var sResult = result.join('\r\n');

    // XXX IE doesn't support whitespace with innerHTML.
    resultsEl.innerHTML = '\n';

    // Use the Peter Michaux outerHTML hack.
    if(resultsEl.innerHTML !== '\n'&&'outerHTML'in resultsEl) {
        resultsEl.outerHTML = resultsEl.outerHTML.replace(/>[.*^<]?/,
'>'+ sResult +'<');
        resultsEl.innerHTML = sResult;
    } else {
        resultsEl.innerHTML = sResult;
    }
}
>  Best regards
>  Mike Wilson
>

[snip example]

Received on Saturday, 15 March 2008 00:45:00 UTC