Find Element Position

Discussion in 'Javascript' started by dhtml, Feb 17, 2008.

  1. dhtml

    dhtml Guest

    I've set off to the task of finding an element's position.

    getOffsetCoords( el, container, coords );

    container (optional) is any ancestor of el.
    coords (optional) an object that has x and y properties - { x: 0, y :
    0 }

    I'm including scroll widths and borders of parentNodes.

    This is useful for widgets like tooltip, dragdrop, context menu.

    I'm throwing this up here for people to pick apart.

    Areas that need improving:
    * find cases where it fails
    * find inefficiencies
    * find things that can be improved
    * formatting, or any other annoyances

    Source:
    http://dhtmlkitchen.com/ape/src/dom/position-f.js

    testcase (I have not tested in IE6 (only 7))
    http://dhtmlkitchen.com/ape/test/tests/dom/position-f-test.html
     
    dhtml, Feb 17, 2008
    #1
    1. Advertising

  2. dhtml

    Matt Kruse Guest

    On Feb 17, 4:09 pm, dhtml <> wrote:
    > I've set off to the task of finding an element's position.


    Were none of the 500 existing solutions sufficient? :)

    Matt Kruse
     
    Matt Kruse, Feb 17, 2008
    #2
    1. Advertising

  3. On Feb 17, 2:09 pm, dhtml <> wrote:
    > I've set off to the task of finding an element's position.


    That is a long road if you don't set some very strict limits on what
    kind of elements with what kind of ancestors and what kind of doctypes/
    quirksmode. Just scrolling elements in a table on a Strict page screws
    up half the functions out there. Did you notice that the difference
    between mouse position of an event and element position in IE are 2px
    different. The "bezel" around the viewport window (i.e. the part of
    the browser where the page is displayed) looks like it is about 2px.
    The problem is basically a mess.

    Look for posts in the archives where Matt Kruse and Richard Cornford
    argue about even the idea of having a single general solution to the
    position reporting problem. Matt argues it is a good idea to have a
    single solution but Richard says that even if the solution could be
    written it would be huge and slow. Richard seems to have explored the
    oddities of this problem in great depth and has decided to have a set
    of interchangeable functions/modules with the same api. Each version
    suited to a different set of circumstances. This is a concept that has
    interested me quite a bit recently. This is one of the main c.l.js
    long term battles since I've been reading the group for a couple
    years.

    A good first question is why do you need to know where the element is
    on the page? I have never needed to know this. For drag-drop work a
    position relative to the parent element is plenty.

    Peter
     
    Peter Michaux, Feb 17, 2008
    #3
  4. dhtml

    dhtml Guest

    On Feb 17, 2:43 pm, Matt Kruse <> wrote:
    > On Feb 17, 4:09 pm, dhtml <> wrote:
    >
    > > I've set off to the task of finding an element's position.

    >
    > Were none of the 500 existing solutions sufficient? :)
    >

    No, most of them make assumptions about body being an offsetParent.

    Might want to check yours in Mozilla and Opera. Add some scroll left,
    and scroll on body:
    http://www.javascripttoolbox.com/lib/objectposition/examples.php

    It fails for me in both FF 2 and Opera 9.2

    And that's not even adding any border or setting position: relative on
    body.

    Garrett

    > Matt Kruse
     
    dhtml, Feb 17, 2008
    #4
  5. dhtml

    David Mark Guest

    On Feb 17, 5:09 pm, dhtml <> wrote:
    > I've set off to the task of finding an element's position.
    >
    > getOffsetCoords( el, container, coords );
    >
    > container (optional) is any ancestor of el.
    > coords (optional) an object that has x and y properties - { x: 0, y :
    > 0 }
    >
    > I'm including scroll widths and borders of parentNodes.


    Watch out for Opera with regard to borders.

    >
    > This is useful for widgets like tooltip, dragdrop, context menu.
    >
    > I'm throwing this up here for people to pick apart.


    I don't have time at the moment to pick it apart, but I can tell you
    from experience that just getting the major browsers to work in most
    cases (nobody has time to test every possible case) requires a lot of
    feature testing (e.g. create a div, style it in a way known to cause
    issues in some browsers, append it to the document, check various
    properties against expected results, etc.) There are numerous
    examples of this in my library. I can't remember if you are one of
    the people who received a link to the Alpha. (?) I plan to make a
    similar "pick apart" request here for the Beta version.

    >
    > Areas that need improving:
    >  * find cases where it fails
    >  * find inefficiencies
    >  * find things that can be improved
    >  * formatting, or any other annoyances
    >
    > Source:http://dhtmlkitchen.com/ape/src/dom/position-f.js
    >
    > testcase (I have not tested in IE6 (only 7))http://dhtmlkitchen.com/ape/test/tests/dom/position-f-test.html


    If you use getBoundingClientRect when available, IE6/7 get virtually
    the same results. The code to deal with the "bezel" (border of the
    outermost element) issue that Peter mentioned is in my script. One
    upcoming issue with that is that other browsers (e.g. the new Opera)
    are starting to implement getBoundingClientRect and I wonder if they
    will stay true to IE's version. IIRC, the W3C is working on a
    standard for this method.

    Similarly, getBoxObjectFor is very helpful with Gecko and Mozilla-
    based browsers, though it has some very odd quirks related to
    scrolling containers with borders.

    Everything else must use the typical offsetParent loop and that is
    where most of the feature test results come into play. For instance,
    Opera alone includes border widths in offsetLeft/Top. Feature test
    this and you don't have to worry if they change that in the future (or
    other browsers start to follow their lead.) Personally, I consider
    this a bug, despite the absence of a standard for the offset*
    properties (the client* properties are there to measure borders.)
     
    David Mark, Feb 17, 2008
    #5
  6. dhtml

    David Mark Guest

    On Feb 17, 5:43 pm, Matt Kruse <> wrote:
    > On Feb 17, 4:09 pm, dhtml <> wrote:
    >
    > > I've set off to the task of finding an element's position.

    >
    > Were none of the 500 existing solutions sufficient? :)
    >


    I've never seen one that is close, even for commonly used DOM
    structures. Once you start testing cases like fixed positioned,
    scrolling containers with borders in quirks mode with borders on the
    body element, they completely fall apart as most rely on browser
    sniffing in lieu of proper feature testing.
     
    David Mark, Feb 17, 2008
    #6
  7. dhtml

    David Mark Guest

    On Feb 17, 5:57 pm, dhtml <> wrote:
    > On Feb 17, 2:43 pm, Matt Kruse <> wrote:> On Feb 17, 4:09 pm, dhtml <> wrote:
    >
    > > > I've set off to the task of finding an element's position.

    >
    > > Were none of the 500 existing solutions sufficient? :)

    >
    > No, most of them make assumptions about body being an offsetParent.
    >
    > Might want to check yours in Mozilla and Opera. Add some scroll left,
    > and scroll on body:http://www.javascripttoolbox.com/lib/objectposition/examples.php
    >
    > It fails for me in both FF 2 and Opera 9.2
    >
    > And that's not even adding any border or setting position: relative on
    > body.
    >


    Yes, that solution completely ignores border issues.

    A relatively positioned body element? Now there's an odd test case.
    Never thought to try that one, but I don't think it would be an issue
    with mine (relative positioning works fine for other elements.)

    From looking at one of your recently posted examples, I suspect I need
    to add a couple of additional feature tests related to tables, but
    they should only affect agents that implement neither
    getBoundingClientRect or getBoxObjectFor.
     
    David Mark, Feb 17, 2008
    #7
  8. dhtml

    David Mark Guest

    On Feb 17, 5:57 pm, Peter Michaux <> wrote:
    > On Feb 17, 2:09 pm, dhtml <> wrote:
    >
    > > I've set off to the task of finding an element's position.

    >
    > That is a long road if you don't set some very strict limits on what
    > kind of elements with what kind of ancestors and what kind of doctypes/
    > quirksmode. Just scrolling elements in a table on a Strict page screws
    > up half the functions out there. Did you notice that the difference


    Then those aren't really solutions at all.

    > between mouse position of an event and element position in IE are 2px
    > different. The "bezel" around the viewport window (i.e. the part of
    > the browser where the page is displayed) looks like it is about 2px.


    See my code, specifically the getBoundingClientRect branch.

    > The problem is basically a mess.


    No question.

    >
    > Look for posts in the archives where Matt Kruse and Richard Cornford
    > argue about even the idea of having a single general solution to the
    > position reporting problem. Matt argues it is a good idea to have a
    > single solution but Richard says that even if the solution could be
    > written it would be huge and slow. Richard seems to have explored the
    > oddities of this problem in great depth and has decided to have a set
    > of interchangeable functions/modules with the same api. Each version
    > suited to a different set of circumstances. This is a concept that has
    > interested me quite a bit recently. This is one of the main c.l.js
    > long term battles since I've been reading the group for a couple
    > years.
    >
    > A good first question is why do you need to know where the element is
    > on the page? I have never needed to know this. For drag-drop work a
    > position relative to the parent element is plenty.


    Not if you want to drag or position elements over a statically
    positioned element. This is covered in the unit tests for my drag and
    drop module. The test involving a statically positioned element is
    not included unless the offset module is present. Same for the drop
    target test. You could handle drop targets without calculating
    offsets if the dragged element and target share the same positioned
    parent, but that isn't possible for all applications. Also, some of
    the quirks discussed here (e.g. borders in Opera) come into play for
    statically positioned targets, whether an offset from the document
    origin is needed or not.
     
    David Mark, Feb 17, 2008
    #8
  9. dhtml

    dhtml Guest

    On Feb 17, 3:36 pm, David Mark <> wrote:
    > On Feb 17, 5:09 pm, dhtml <> wrote:
    >


    > Watch out for Opera with regard to borders.
    >

    Covered.

    >
    >
    > > This is useful for widgets like tooltip, dragdrop, context menu.

    >
    > > I'm throwing this up here for people to pick apart.

    >
    > I don't have time at the moment to pick it apart, but I can tell you
    > from experience that just getting the major browsers to work in most
    > cases (nobody has time to test every possible case) requires a lot of
    > feature testing (e.g. create a div, style it in a way known to cause
    > issues in some browsers, append it to the document, check various
    > properties against expected results, etc.) There are numerous
    > examples of this in my library. I can't remember if you are one of
    > the people who received a link to the Alpha. (?) I plan to make a
    > similar "pick apart" request here for the Beta version.
    >

    I'd like to see that.

    >
    > If you use getBoundingClientRect when available, IE6/7 get virtually
    > the same results. The code to deal with the "bezel" (border of the
    > outermost element) issue that Peter mentioned is in my script. One
    > upcoming issue with that is that other browsers (e.g. the new Opera)
    > are starting to implement getBoundingClientRect and I wonder if they
    > will stay true to IE's version. IIRC, the W3C is working on a
    > standard for this method.
    >

    Anne van Kesteren started CSSOM view module two years ago.
    http://dev.w3.org/cvsweb/csswg/cssom-view/Overview.html?rev=1.2#offset-attributes

    The document is mostly inaccurate. I've emailed him about this (and
    his other inaccurate docs), but he is unable or unwilling to change.

    > Similarly, getBoxObjectFor is very helpful with Gecko and Mozilla-
    > based browsers, though it has some very odd quirks related to
    > scrolling containers with borders.
    >

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

    I'm going to look into getBoxObjectFor. If it's not buggy, it could be
    a conditional:

    else if(el.getBoxObjectFor) {

    }

    It might shoujld make it simpler and more efficient. I will
    investigate.

    > Everything else must use the typical offsetParent loop and that is
    > where most of the feature test results come into play. For instance,
    > Opera alone includes border widths in offsetLeft/Top. Feature test
    > this and you don't have to worry if they change that in the future (or
    > other browsers start to follow their lead.)


    Yes, I have a load time constants in a closure. I do the feature
    testing style you described in your prev paragarph.

    Personally, I consider
    > this a bug, despite the absence of a standard for the offset*
    > properties (the client* properties are there to measure borders.)


    It is as per Anne's spec. You should post on the css mailing list:
    "" <>
     
    dhtml, Feb 17, 2008
    #9
  10. dhtml

    dhtml Guest

    On Feb 17, 2:57 pm, Peter Michaux <> wrote:
    > On Feb 17, 2:09 pm, dhtml <> wrote:
    >
    > > I've set off to the task of finding an element's position.

    >
    > That is a long road if you don't set some very strict limits on what
    > kind of elements with what kind of ancestors and what kind of doctypes/
    > quirksmode. Just scrolling elements in a table on a Strict page screws
    > up half the functions out there.

    Can you provide some HTML testcase?

    <table>
    <caption>blah</caption>
    <tbody>
    <tr>
    <td>

    </td>
    </tr>
    </tbody>
    </table>

    Did you notice that the difference
    > between mouse position of an event and element position in IE are 2px
    > different. The "bezel" around the viewport window (i.e. the part of
    > the browser where the page is displayed) looks like it is about 2px.
    > The problem is basically a mess.
    >

    An element position from the viewport is offset by that border in IE.

    This goes back to the difference between IE and Mozilla. IE calls HTML
    the ICB, Moz treats viewport the ICB. IE in backcompat mode treats
    BODY as the ICB. This is the big problem area.

    > Look for posts in the archives where Matt Kruse and Richard Cornford
    > argue about even the idea of having a single general solution to the
    > position reporting problem. Matt argues it is a good idea to have a
    > single solution but Richard says that even if the solution could be
    > written it would be huge and slow. Richard seems to have explored the
    > oddities of this problem in great depth and has decided to have a set
    > of interchangeable functions/modules with the same api. Each version
    > suited to a different set of circumstances. This is a concept that has
    > interested me quite a bit recently. This is one of the main c.l.js
    > long term battles since I've been reading the group for a couple
    > years.
    >

    I will search that.

    > A good first question is why do you need to know where the element is
    > on the page? I have never needed to know this. For drag-drop work a
    > position relative to the parent element is plenty.
    >

    For drag drop, I might have two different containers.

    For a tooltip or context menu or panel, I might have any containers
    surrounding the actuator/target.

    Thank you,

    Garrett

    > Peter
     
    dhtml, Feb 18, 2008
    #10
  11. On Feb 17, 4:06 pm, dhtml <> wrote:
    > On Feb 17, 2:57 pm, Peter Michaux <> wrote:> On Feb 17, 2:09 pm, dhtml <> wrote:
    >
    > > > I've set off to the task of finding an element's position.

    >
    > > That is a long road if you don't set some very strict limits on what
    > > kind of elements with what kind of ancestors and what kind of doctypes/
    > > quirksmode. Just scrolling elements in a table on a Strict page screws
    > > up half the functions out there.

    >
    > Can you provide some HTML testcase?


    I don't know where they are. Just start mixing nested scrolling divs
    in scrolling divs, tables in tables and combinations of the two. It
    won't take more than one try to find problems.

    [snip]

    > > A good first question is why do you need to know where the element is
    > > on the page? I have never needed to know this. For drag-drop work a
    > > position relative to the parent element is plenty.

    >
    > For drag drop, I might have two different containers.


    There are many ways to do it so the positioning problem is quite easy.
    The wrong way to do it is depend on some complex, expensive function
    that calculates the position of the element relative to the page top
    left.

    > For a tooltip or context menu or panel, I might have any containers
    > surrounding the actuator/target.


    You don't need to know the position of what was clicked for a tooltip,
    context menu etc. You just need to know the position of the mouse
    event. That is much easier. Have a look in the FAQ notes about browser
    detection and the scroll reporting function.

    Every case I've encountered there is just an easier way to do it than
    use absolute position reporting. I'm sure there are cases where it is
    necessary but I bet they are rare.

    Peter
     
    Peter Michaux, Feb 18, 2008
    #11
  12. dhtml

    David Mark Guest

    On Feb 17, 6:57 pm, dhtml <> wrote:
    > On Feb 17, 3:36 pm, David Mark <> wrote:
    >
    > > On Feb 17, 5:09 pm, dhtml <> wrote:

    >
    > > Watch out for Opera with regard to borders.

    >
    > Covered.
    >
    >
    >
    >
    >
    >
    >
    > > > This is useful for widgets like tooltip, dragdrop, context menu.

    >
    > > > I'm throwing this up here for people to pick apart.

    >
    > > I don't have time at the moment to pick it apart, but I can tell you
    > > from experience that just getting the major browsers to work in most
    > > cases (nobody has time to test every possible case) requires a lot of
    > > feature testing (e.g. create a div, style it in a way known to cause
    > > issues in some browsers, append it to the document, check various
    > > properties against expected results, etc.)  There are numerous
    > > examples of this in my library.  I can't remember if you are one of
    > > the people who received a link to the Alpha. (?)  I plan to make a
    > > similar "pick apart" request here for the Beta version.

    >
    > I'd like to see that.


    Check your email. I sent you a link to the Alpha.

    >
    >
    >
    > > If you use getBoundingClientRect when available, IE6/7 get virtually
    > > the same results.  The code to deal with the "bezel" (border of the
    > > outermost element) issue that Peter mentioned is in my script.  One
    > > upcoming issue with that is that other browsers (e.g. the new Opera)
    > > are starting to implement getBoundingClientRect and I wonder if they
    > > will stay true to IE's version.  IIRC, the W3C is working on a
    > > standard for this method.

    >
    > Anne van Kesteren started CSSOM view module two years ago.http://dev.w3.org/cvsweb/csswg/cssom-view/Overview.html?rev=1.2#offse...
    >
    > The document is mostly inaccurate. I've emailed him about this (and


    I looked it over recently and did notice some odd choices.

    > his other inaccurate docs), but he is unable or unwilling to change.


    Oh well.

    >
    > > Similarly, getBoxObjectFor is very helpful with Gecko and Mozilla-
    > > based browsers, though it has some very odd quirks related to
    > > scrolling containers with borders.

    >
    > https://bugzilla.mozilla.org/show_bug.cgi?id=340571
    >
    > I'm going to look into getBoxObjectFor. If it's not buggy, it could be
    > a conditional:


    It has a few quirks, but all-in-all, it is far superior to the manual
    offsetParent iteration.

    >
    > else if(el.getBoxObjectFor) {
    >
    > }


    Use isHostMethod! Feature testing by boolean type conversion is out.

    >
    > It might shoujld make it simpler and more efficient. I will
    > investigate.


    It is far simpler and far more efficient. It doesn't account for
    scrolling containers though, so you still have to manually iterate
    through those. IIRC, it also needs a little help to return meaningful
    results for fixed position elements.

    >
    > > Everything else must use the typical offsetParent loop and that is
    > > where most of the feature test results come into play.  For instance,
    > > Opera alone includes border widths in offsetLeft/Top.  Feature test
    > > this and you don't have to worry if they change that in the future (or
    > > other browsers start to follow their lead.)

    >
    > Yes, I have a load time constants in a closure. I do the feature
    > testing style you described in your prev paragarph.
    >
    >   Personally, I consider
    >
    > > this a bug, despite the absence of a standard for the offset*
    > > properties (the client* properties are there to measure borders.)

    >
    > It is as per Anne's spec. You should post on the css mailing list:
    > "" <>


    No time for another forum right now. I will probably just let the w3c
    sort it out.
     
    David Mark, Feb 18, 2008
    #12
  13. dhtml

    Matt Kruse Guest

    On Feb 17, 5:38 pm, David Mark <> wrote:
    > On Feb 17, 5:43 pm, Matt Kruse <> wrote:
    > > Were none of the 500 existing solutions sufficient? :)

    > I've never seen one that is close, even for commonly used DOM
    > structures. Once you start testing cases like fixed positioned,
    > scrolling containers with borders in quirks mode with borders on the
    > body element, they completely fall apart as most rely on browser
    > sniffing in lieu of proper feature testing.


    That's an interesting test case, but probably one that would never
    occur in a real-world situation.
    If a proposed "solution" fails this case, I'm not sure anyone would
    actually care.
    And if someone does have such a situation on their hand, they probably
    already realize that no generalized solution is going to work for
    them.

    As long as the proposed solution makes it clear what cases are and are
    not covered, I think it's okay to not "solve" them all.

    Matt Kruse
     
    Matt Kruse, Feb 18, 2008
    #13
  14. dhtml

    David Mark Guest

    On Feb 17, 8:52 pm, Matt Kruse <> wrote:
    > On Feb 17, 5:38 pm, David Mark <> wrote:
    >
    > > On Feb 17, 5:43 pm, Matt Kruse <> wrote:
    > > > Were none of the 500 existing solutions sufficient? :)

    > > I've never seen one that is close, even for commonly used DOM
    > > structures.  Once you start testing cases like fixed positioned,
    > > scrolling containers with borders in quirks mode with borders on the
    > > body element, they completely fall apart as most rely on browser
    > > sniffing in lieu of proper feature testing.

    >
    > That's an interesting test case, but probably one that would never
    > occur in a real-world situation.


    It does on my test page. Along with several other ridiculous
    scenarios. I didn't add these cases because I thought somebody would
    actually use them, but to expose as many quirks as possible.

    > If a proposed "solution" fails this case, I'm not sure anyone would
    > actually care.


    Perhaps not.

    > And if someone does have such a situation on their hand, they probably
    > already realize that no generalized solution is going to work for
    > them.


    The described test case works for me. I suspect that by making sure
    it worked, I avoided other related quirks.

    >
    > As long as the proposed solution makes it clear what cases are and are
    > not covered, I think it's okay to not "solve" them all.
    >


    It is virtually impossible to list every possible case that is not
    covered. One can only list cases that are known (or thought) to be
    covered and which user agents (and layout modes) were used to test
    them.

    The last time I put my offset location function through a battery of
    tests, I limited the agents involved to:

    IE5/6/7
    Windows Safari (Beta of course, so things could have changed since)
    Opera 9
    Firefox 2
    Netscape 6.2
    And whatever the last version of Netscape was (seemed to mirror
    Firefox in both standards and quirks mode.)

    By the time I was done, I couldn't come up with any test cases that
    failed (meaning one or more pixels off) in standards or quirks mode,
    regardless of margins or borders on the outermost element. Granted, I
    am sure that there were some cases I failed to consider. IIRC,
    scrolling tables weren't considered at all, which means I probably
    need at least one additional feature test. And who can say what Mac
    IE (or even Safari) would do? When I release the public Beta, I hope
    somebody will give me feedback on those.

    The end result wasn't particularly bloated or slow, but all things
    relative, it was more than most apps would need. I really need to
    make some portions of that module optional.
     
    David Mark, Feb 18, 2008
    #14
  15. dhtml

    David Mark Guest

    On Feb 17, 9:39 pm, Jeff <jeff@spam_me_not.com> wrote:
    > Matt Kruse wrote:
    > > On Feb 17, 5:38 pm, David Mark <> wrote:
    > >> On Feb 17, 5:43 pm, Matt Kruse <> wrote:
    > >>> Were none of the 500 existing solutions sufficient? :)
    > >> I've never seen one that is close, even for commonly used DOM
    > >> structures.  Once you start testing cases like fixed positioned,
    > >> scrolling containers with borders in quirks mode with borders on the
    > >> body element, they completely fall apart as most rely on browser
    > >> sniffing in lieu of proper feature testing.

    >
    > > That's an interesting test case, but probably one that would never
    > > occur in a real-world situation.
    > > If a proposed "solution" fails this case, I'm not sure anyone would
    > > actually care.
    > > And if someone does have such a situation on their hand, they probably
    > > already realize that no generalized solution is going to work for
    > > them.

    >
    > > As long as the proposed solution makes it clear what cases are and are
    > > not covered, I think it's okay to not "solve" them all.

    >
    >   Well, where does that leave us? I've been using a while offsetParent
    > loop and that's pretty close for my uses, but if there is something


    Depending on what you use it for, "pretty close" may suffice.
    However, for example, if you wish to transition a random element by
    progressively rendering a clone on top of it, a single pixel off can
    seriously degrade the result.

    > closer that isn't a glut of code, I'd like to see it. Scrollable
    > containers aren't an issue for me.


    Perhaps you should start by looking at Garrett's sample. I imagine it
    has a way to short-circuit the logic dealing with scrolling containers
    (a must as such logic is a major performance hit.)
     
    David Mark, Feb 18, 2008
    #15
  16. dhtml

    dhtml Guest

    On Feb 17, 5:52 pm, Matt Kruse <> wrote:
    > On Feb 17, 5:38 pm, David Mark <> wrote:
    >
    > > On Feb 17, 5:43 pm, Matt Kruse <> wrote:
    > > > Were none of the 500 existing solutions sufficient? :)

    > > I've never seen one that is close, even for commonly used DOM
    > > structures. Once you start testing cases like fixed positioned,
    > > scrolling containers with borders in quirks mode with borders on the
    > > body element, they completely fall apart as most rely on browser
    > > sniffing in lieu of proper feature testing.

    >
    > That's an interesting test case,


    That's not a test case.

    but probably one that would never
    > occur in a real-world situation.
    > If a proposed "solution" fails this case, I'm not sure anyone would
    > actually care.


    Anyone who's used:

    body {
    position: relative;
    }

    to force a containing block,

    or
    #area {
    overflow: scroll;
    }

    would probably care.


    > And if someone does have such a situation on their hand, they probably
    > already realize that no generalized solution is going to work for
    > them.
    >


    Your example addresses the scroll case.

    > As long as the proposed solution makes it clear what cases are and are
    > not covered, I think it's okay to not "solve" them all.
    >

    That's true. Having position: relative and border on body aren't too
    far out though.

    The difficulty with body is that the CSSOM spec says taht the
    offsetParent algorithm stops at body. That contradicts what we know
    about offsetParent or containing blocks. It's a quirks mode behavior
    in a spec.

    > Matt Kruse
     
    dhtml, Feb 18, 2008
    #16
  17. dhtml

    David Mark Guest

    On Feb 17, 5:09 pm, dhtml <> wrote:
    > I've set off to the task of finding an element's position.
    >
    > getOffsetCoords( el, container, coords );
    >
    > container (optional) is any ancestor of el.
    > coords (optional) an object that has x and y properties - { x: 0, y :
    > 0 }
    >
    > I'm including scroll widths and borders of parentNodes.
    >
    > This is useful for widgets like tooltip, dragdrop, context menu.


    You shouldn't need it for a context menu.
    >
    > I'm throwing this up here for people to pick apart.
    >
    > Areas that need improving:
    >  * find cases where it fails
    >  * find inefficiencies
    >  * find things that can be improved
    >  * formatting, or any other annoyances
    >
    > Source:http://dhtmlkitchen.com/ape/src/dom/position-f.js
    >
    > testcase (I have not tested in IE6 (only 7))http://dhtmlkitchen.com/ape/test/tests/dom/position-f-test.html


    I took a little time to survey this.

    // Load-time constants.
    var IS_BACK_COMPAT = document.compatMode === "BackCompat";

    You can't rely on this flag in IE < 6.


    // IE, Safari, and Opera support clientTop. FF 2 doesn't
    var IS_CLIENT_TOP_SUPPORTED = 'clientTop'in document.documentElement;

    I use a wrapper that uses border styles in lieu of clientLeft/Top. I
    think it makes the code easier to follow.

    // XXX Opera <= 9.2 - parent border widths are included in offsetTop.
    var IS_PARENT_BORDER_INCLUDED_IN_OFFSET;

    Can I assume from the comment that this flag will be false in the new
    Opera? It doesn't really matter, but it would be good if they changed
    their scheme to match other browsers.

    // XXX Opera <= 9.2 - body offsetTop is inherited to children's
    offsetTop
    // when body position is not static.
    var IS_BODY_OFFSET_INHERITED;

    A positioned body element is a case I didn't consider. I am not
    surprised that such a style causes issues.

    // XXX Mozilla includes a table border in the TD's offsetLeft.
    // There is 1 exception:
    // When the TR has position: relative and the TD has block level
    content.
    // In that case, the TD does not include the TABLE's border in it's
    offsetLeft.
    // We do not account for this peculiar bug.
    var IS_TABLE_BORDER_INCLUDED_IN_TD_OFFSET;

    So is this variable a placeholder?

    var
    IS_STATIC_BODY_OFFSET_PARENT_BUT_ABSOLUTE_CHILD_SUBTRACTS_BODY_BORDER_WIDTH
    = false;

    Borders/margins on the HTML element (in standards mode) cause
    additional aggravations.

    var getComputedStyle = window.getComputedStyle;
    var bcs;

    var positionedExp = /^(?:r|a|f)/,
    absoluteExp = /^(?:a|f)/;

    There are several issues that are unique to fixed positioning. For
    one, fixed elements in Opera 9 have a null offsetParent. In this
    case, I resorted to getComputedStyle.

    /**
    * @param {HTMLElement} el you want coords of.
    * @param {HTMLElement} container to look up to.
    * @param {x:{Number}, y:{Number}} coords object to pass in.
    * @param {boolean} forceRecalc if true, forces recalculation of body
    scroll offsets.

    This is an interesting idea. Is it to make things less painful for
    applications that don't scroll?

    * @return {x:{Number}, y:{Number}} coords of el from container.
    *
    * Passing in a container will improve performance in other browsers,
    * but will punish IE with a recursive call. Test accordingly.

    Same for Firefox if you use getBoxObjectFor. For some reason, I
    didn't do this in the getBoundingClientRect branch, but resorted to
    the offsetParent loop. IIRC, IE6/7 had the fewest issues with that
    method, though I should change it to work like the gBOF branch for
    performance reasons.

    * <p>
    * Container is sometimes irrelevant. Container is irrelevant when
    comparing two objects'
    * positions against one another, to see if they intersect. In this
    case, pass in document.

    It can be relevant if the two elements share a common positioned
    parent. And why not default to document?

    * </p>
    * Passing in re-used coords will greatly improve performance in all
    browsers.

    Can you elaborate on re-used coords?

    * There is a side effect to passing in coords:
    * For animation or drag drop operations, reuse coords.
    */

    I don't follow that. And I can't conceive of an animation that would/
    should be concerned with offset positions.

    function getOffsetCoords(el, container, coords) {

    var doc = document, body = doc.body, documentElement =
    doc.documentElement;

    Apparently this function is good for one document. I think it is a
    good idea to allow for multiple documents in functions like these
    (e.g. for iframes, objects, etc.)

    if(!container)
    container = doc;

    if(!coords)
    coords = {x:0, y:0};

    if(el === container) {
    coords.x = coords.y = 0;
    return coords;
    }
    if("getBoundingClientRect"in el) {

    I would avoid the in operator for compatibility reasons.


    // In BackCompat mode, body's border goes to the window. BODY is
    ICB.
    var rootBorderEl = (IS_BACK_COMPAT ? body : documentElement);

    But this flag isn't correct in IE < 6 and those versions do not
    display the documentElement. IIRC, this is okay in this case as I
    think IE5.x considers the viewport border to be part of the (otherwise
    invisible) HTML element.

    var box = el.getBoundingClientRect();
    var x, y;
    x = box.left - rootBorderEl.clientLeft
    + Math.max( documentElement.scrollLeft, body.scrollLeft );
    y = box.top - rootBorderEl.clientTop
    + Math.max( documentElement.scrollTop, body.scrollTop );
    if(container !== doc) {
    box = getOffsetCoords(container, null);
    x -= box.x;
    y -= box.y;
    }
    if(IS_BACK_COMPAT) {
    var curSty = body.currentStyle;

    Object inference based on getBoundingClientRect (will break in the new
    Opera.)

    x += parseInt(curSty.marginLeft);
    y += parseInt(curSty.marginTop);

    Use parseFloat. Oddly enough, my gBCR branch does not consider
    margins at all and I am pretty sure I tested quirks mode w/ body
    margins in IE6/7. I'll have to re-test that. Of course, I need to re-
    test everything now that I have transplanted the code into a new
    library (I'm really looking forward to *that*.)

    }
    coords.x = x;
    coords.y = y;

    return coords;
    }

    // Crawling up the tree.
    else {

    var offsetLeft = el.offsetLeft,
    offsetTop = el.offsetTop,
    isBodyStatic = !positionedExp.test(bcs.position);

    What if bcs does not exist. You should allow applications that need
    to run in ancient browsers to compensate by setting inline styles
    (i.e. check inline styles as a fallback.)

    [snip]

    ----
    // Loop up, gathering scroll offsets on parentNodes.
    // when we get to a parent that's an offsetParent, update
    // the current offsetParent marker.

    I prefer to loop through offsetParents and then deal with scrolling
    parents as an optional afterthought. This makes it easy for
    applications that do not involve scrolling containers to prevent the
    extra work. Also, the adjustments for scrolling containers are needed
    for the gBOF branch.

    for( var parent = el.parentNode; parent && parent !== container;
    parent = parent.parentNode) {
    if(parent !== body && parent !== documentElement) {
    lastOffsetParent = parent;
    offsetLeft -= parent.scrollLeft;
    offsetTop -= parent.scrollTop;
    }
    if(parent === offsetParent) {
    // If we get to BODY and have static position, skip it.
    if(parent === body && isBodyStatic);
    else {

    // XXX Mozilla; Exclude static body; if static, it's offsetTop
    will be wrong.

    Negative in some cases, IIRC.
    // Include parent border widths. This matches behavior of
    clientRect approach.
    // XXX Opera <= 9.2 includes parent border widths.
    // See IS_PARENT_BORDER_INCLUDED_IN_OFFSET below.
    if( !IS_PARENT_BORDER_INCLUDED_IN_OFFSET &&
    ! (parent.tagName === "TABLE" &&
    IS_TABLE_BORDER_INCLUDED_IN_TD_OFFSET)) {

    You don't need a strict comparison there.

    if( IS_CLIENT_TOP_SUPPORTED ) {
    offsetLeft += parent.clientLeft;
    offsetTop += parent.clientTop;
    }
    else {
    var pcs = getComputedStyle(parent, "");
    // Mozilla doesn't support clientTop. Add borderWidth to the
    sum.
    offsetLeft += parseInt(pcs.borderLeftWidth)||0;
    offsetTop += parseInt(pcs.borderTopWidth)||0;

    As mentioned, allow for inline styles.
    }
    }
    if(parent !== body) {
    offsetLeft += offsetParent.offsetLeft;
    offsetTop += offsetParent.offsetTop;
    offsetParent = parent.offsetParent; // next marker to check
    for offsetParent.
    }
    }
    }
    }
    [snip]
    var bodyOffsetLeft = parseInt(bcs.marginLeft)||0;
    var bodyOffsetTop = parseInt(bcs.marginTop)||0;
    }

    Use parseFloat or em-based layout (for example) will have rounding
    errors.

    if(isBodyStatic) {

    // XXX: Safari will use HTML for containing block (CSS),
    // but will subtract the body's border from the body's absolutely
    positioned
    // child.offsetTop. Safari reports the child's offsetParent is
    BODY, but
    // doesn't treat it that way (Safari bug).
    if(!isLastElementAbsolute) {
    if(false == IS_PARENT_BORDER_INCLUDED_IN_OFFSET

    Shouldn't this be !IS_PARENT_BORDER_INCLUDED_IN_OFFSET?

    && (container === document || container === documentElement)){
    offsetTop += parseInt(bcs.borderTopWidth);
    offsetLeft += parseInt(bcs.borderLeftWidth);


    Use parseFloat.

    }
    }
    else {
    // XXX Safari subtracts border width of body from element's
    offsetTop (opera does it, too)

    I definitely remember this one.


    if(IS_STATIC_BODY_OFFSET_PARENT_BUT_ABSOLUTE_CHILD_SUBTRACTS_BODY_BORDER_WIDTH)
    {
    offsetTop += parseInt(bcs.borderTopWidth);
    offsetLeft += parseInt(bcs.borderLeftWidth);

    Same here (parseFloat.)

    }
    }
    }
    else if(container === doc || container === documentElement) {
    // If the body is positioned, add its left and top value.

    // Safari will sometimes return "auto" for computedStyle, which
    results NaN.


    So will IE for statically positioned elements. Opera will return
    incorrect results for those with borders.

    bodyOffsetLeft += parseInt(bcs.left)||0;
    bodyOffsetTop += parseInt(bcs.top)||0;


    Use parseFloat.

    // XXX: Opera normally include the parentBorder in offsetTop.
    // We have a preventative measure in the loop above.
    if(isLastElementAbsolute) {
    if(IS_CLIENT_TOP_SUPPORTED &&
    IS_PARENT_BORDER_INCLUDED_IN_OFFSET) {
    offsetTop += body.clientTop;
    offsetLeft += body.clientLeft;
    }
    }
    }
    }

    coords.x = offsetLeft + bodyOffsetLeft;
    coords.y = offsetTop + bodyOffsetTop;

    return coords;
    }
    }

    // A closure for initializing load time constants.
    if(!("getBoundingClientRect"in document.documentElement))

    As mentioned, I would avoid the in operator.

    (function(){
    var waitForBodyTimer = setInterval(function
    domInitLoadTimeConstants() {

    What is this about? A DOM ready simulation?

    if(!document.body) return;

    This excludes XHTML documents in Windows Safari and (reportedly) some
    older Gecko-based browsers.


    clearInterval(waitForBodyTimer);
    var body = document.body;
    var s = body.style, padding = s.padding, border = s.border,
    position = s.position, marginTop = s.marginTop;
    s.padding = 0;
    s.top = 0;
    s.border = '1px solid transparent';

    var x = document.createElement('div');
    x.id='asdf';

    Why do you need to assign an ID?

    var xs = x.style;
    xs.margin = 0;
    xs.position = "static";

    x = body.appendChild(x);

    This will cause a twitch during page load. I would make the height
    and width 0 if you can get away with it in this test.

    IS_PARENT_BORDER_INCLUDED_IN_OFFSET = (x.offsetTop === 1);

    s.border = 0;
    s.padding = 0;

    var table = document.createElement('table');
    try {
    table.innerHTML = "<tbody><tr><td>bla</td></tr></tbody>";

    Why not use DOM methods and lose the try-catch?

    table.style.border = "17px solid red";

    Why set border colors on these dummy elements?


    table.cellSpacing = table.cellPadding = 0;

    body.appendChild(table);
    IS_TABLE_BORDER_INCLUDED_IN_TD_OFFSET =
    table.getElementsByTagName("td")[0].offsetLeft === 17;

    body.removeChild(table);
    } catch(ex){/*IE, we don't care*/}
    if(getComputedStyle) {
    bcs = getComputedStyle(document.body,'');
    }
    // Now add margin to determine if body offsetTop is inherited.
    s.marginTop = "1px";
    s.position = "relative";
    IS_BODY_OFFSET_INHERITED = (x.offsetTop === 1);

    s.marginTop = "0";

    xs.position = "absolute";
    s.position = "static";
    if(x.offsetParent === body) {
    s.border = "1px solid #f3f3f3";
    xs.top = "2px";
    // XXX Safari gets offsetParent wrong (says 'body' when body is
    static,
    // but then positions element from ICB and then subtracts body's
    clientWidth.
    // Safari is half wrong.
    //
    // XXX Mozilla says body is offsetParent but does NOT subtract
    BODY's offsetWidth.

    Subtracts BODY's clientWidth?

    // Mozilla is completely wrong.

    IS_STATIC_BODY_OFFSET_PARENT_BUT_ABSOLUTE_CHILD_SUBTRACTS_BODY_BORDER_WIDTH
    = x.offsetTop === 1;
    }
    s.position = position;

    s.marginTop = marginTop;
    // Put back border and padding the way they were.
    s.border = border;
    s.padding = padding;

    This is going to be twitchy.


    // Release memory (IE).
    body = s = x = xs = table = null;

    }, 60);
    })();

    /**
    * @return {boolean} true if a is vertically within b's content area
    (and does not overlap, top nor bottom).
    */
    function isInsideElement(a, b) {
    var aTop = getOffsetCoords(a).y;
    var bTop = getOffsetCoords(b).y;
    return aTop + a.offsetHeight <= bTop + b.offsetHeight && aTop >=
    bTop;
    }

    /**
    * @return {boolean} true if a overlaps the top of b's content area.
    */
    function isAboveElement(a, b) {
    return (getOffsetCoords(a).y <= getOffsetCoords(b).y);
    }

    /**
    * @return {boolean} true if a overlaps the bottom of b's content
    area.
    */
    function isBelowElement(a, b) {
    return (getOffsetCoords(a).y + a.offsetHeight >=
    getOffsetCoords(b).y + b.offsetHeight);
    }

    [snip]

    The rest appears unrelated.

    Sorry for the inevitable wrapping. I didn't have time to make this
    newsreader-friendly.

    BTW, the email I sent to you bounced (and I was replying to one of
    your messages.) Perhaps GMail is on the fritz tonight?
     
    David Mark, Feb 18, 2008
    #17
  18. dhtml

    dhtml Guest

    On Feb 17, 9:01 pm, David Mark <> wrote:


    On Feb 17, 9:01 pm, David Mark <> wrote:
    > On Feb 17, 5:09 pm, dhtml <> wrote:


    > I took a little time to survey this.
    >

    It's an excellent review. Questions about comments/x.d/borderColor, I
    just corrected the mistakes and
    snipped the question to keep focused on other things.


    > var IS_BACK_COMPAT = document.compatMode === "BackCompat";
    >
    > You can't rely on this flag in IE < 6.
    >


    What do you do? There's likely to eventually be a browser that
    supports getBoundingClientRect, yet has document.compatMode ==
    "undefined"

    <snip>

    > I use a wrapper that uses border styles in lieu of clientLeft/Top. I
    > think it makes the code easier to follow.
    >


    That would be easier to follow; it would reduce conditional checks and
    put the logic on one place. I was doing this at first, but it was
    slow. I replaced it with inline code and the time
    was cut almost in half.

    Removing the extra function call made a big difference.

    > // XXX Opera <= 9.2 - parent border widths are included in offsetTop.
    > var IS_PARENT_BORDER_INCLUDED_IN_OFFSET;
    >
    > Can I assume from the comment that this flag will be false in the new
    > Opera? It doesn't really matter, but it would be good if they changed
    > their scheme to match other browsers.
    >

    The flag gets set based on a test:

    // A closure for initializing load time constants.
    if(!("getBoundingClientRect"in document.documentElement))
    (function(){
    var waitForBodyTimer = setInterval(function
    domInitLoadTimeConstants() {
    if(!document.body) return;

    clearInterval(waitForBodyTimer);

    var x = document.createElement('div');
    var xs = x.style;
    xs.margin = 0;
    xs.position = "static";
    x = body.appendChild(x);
    IS_PARENT_BORDER_INCLUDED_IN_OFFSET = (x.offsetTop === 1);
    ...
    }, 60);
    })();


    <snip>

    > A positioned body element is a case I didn't consider. I am not
    > surprised that such a style causes issues.
    >

    It is a very real case. Setting position: relative makes an element a
    containing block for absolutely positioned elements.

    For example:

    #adiv {
    position: absolute;
    top: 0;
    }
    body {
    margin: 10px;
    }

    adiv's containing block is HTML. It's offsetParent should be HTML, and
    in IE, it is.

    In Opera, Safari, Firefox the offsetParent is BODY. This is per Anne
    van Kesteren's spec.

    In Safari, the offsetTop of adiv will be -10. Given the fact that
    adiv's offsetParent is BODY, this makes sense.


    > var
    > IS_STATIC_BODY_OFFSET_PARENT_BUT_ABSOLUTE_CHILD_SUBTRACTS_BODY_BORDER_WIDTH
    > = false;
    >
    > Borders/margins on the HTML element (in standards mode) cause
    > additional aggravations.
    >
    > var getComputedStyle = window.getComputedStyle;
    > var bcs;
    >
    > var positionedExp = /^(?:r|a|f)/,
    > absoluteExp = /^(?:a|f)/;
    >
    > There are several issues that are unique to fixed positioning. For
    > one, fixed elements in Opera 9 have a null offsetParent. In this
    > case, I resorted to getComputedStyle.
    >

    That would go along with Anne's spec. In the case of fixed
    positioning, I haven't addressed yet.

    myFixedDiv.offsetTop
    myFixedDiv.offsetLeft

    Would seem to provide the desired result.

    > * @param {boolean} forceRecalc if true, forces recalculation of body
    > scroll offsets.
    >
    > This is an interesting idea. Is it to make things less painful for
    > applications that don't scroll?
    >


    It was an idea for addressing the issue where body's border/margin/
    padding/top/left are unchanging.

    It was impossible/extremely difficult to test, and so I removed that.



    > Same for Firefox if you use getBoxObjectFor. For some reason, I
    > didn't do this in the getBoundingClientRect branch, but resorted to
    > the offsetParent loop. IIRC, IE6/7 had the fewest issues with that
    > method, though I should change it to work like the gBOF branch for
    > performance reasons.
    >

    getBoxObjectFor is being discouraged in a bugzilla:
    https://bugzilla.mozilla.org/show_bug.cgi?id=340571

    It's not supported for HTML. It's not guaranteed to provide any
    results in HTML documents.

    <snip>

    >
    > Can you elaborate on re-used coords?
    >

    If you call getOffsetCoords(el, cont),

    it creates an object {x: 0, y: 0};

    if you pass in an object
    getBoxOffsetCoords(el. cont, this.coords);

    It doesn't create a new object. For drag operations, it could mean
    creating hundreds of objects.

    //update coords, no need to return anything.
    getOffsetCoords(dropTarget.el, document, dropTarget.coords);


    > function getOffsetCoords(el, container, coords) {
    >
    > var doc = document, body = doc.body, documentElement =
    > doc.documentElement;
    >
    > Apparently this function is good for one document. I think it is a
    > good idea to allow for multiple documents in functions like these
    > (e.g. for iframes, objects, etc.)
    >


    I am too lazy to write a frame-based test. It might work. I guess it
    wouldn't hurt to put in:

    doc = el.ownerDocument.


    > if("getBoundingClientRect"in el) {
    >
    > I would avoid the in operator for compatibility reasons.
    >


    What copatibility reasons?


    > // In BackCompat mode, body's border goes to the window. BODY is
    > ICB.
    > var rootBorderEl = (IS_BACK_COMPAT ? body : documentElement);
    >
    > But this flag isn't correct in IE < 6 and those versions do not
    > display the documentElement. IIRC, this is okay in this case as I
    > think IE5.x considers the viewport border to be part of the (otherwise
    > invisible) HTML element.
    >


    I don't know how to address this. I don't even have IE6, much less IE
    5.5 to test on.


    > var box = el.getBoundingClientRect();
    > var x, y;
    > x = box.left - rootBorderEl.clientLeft
    > + Math.max( documentElement.scrollLeft, body.scrollLeft );
    > y = box.top - rootBorderEl.clientTop
    > + Math.max( documentElement.scrollTop, body.scrollTop );
    > if(container !== doc) {
    > box = getOffsetCoords(container, null);
    > x -= box.x;
    > y -= box.y;
    > }
    > if(IS_BACK_COMPAT) {
    > var curSty = body.currentStyle;
    >
    > Object inference based on getBoundingClientRect (will break in the new
    > Opera.)


    That's true, it is object inference. The code assumes:
    if documet.getBoundingClientRect, then body.currentStyle is supported.
    Coincidentally Opera supports currentStyle. It is very likely that
    this
    will break in future versions of FF/Safari.

    Changed to:
    if(IS_BACK_COMPAT && IS_CURRENT_STYLE_SUPPORTED) {


    >
    > x += parseInt(curSty.marginLeft);
    > y += parseInt(curSty.marginTop);
    >
    > Use parseFloat.

    Am I missing a fraction of a pixel? I think the coords returned should
    be integers. I can't remember, but I think I remember IE having
    problems with style values with floating point numbers.


    Using parseInt and parseFloat:

    Opera 9.2: (same result for both)
    Expected: 2750 Actual:1794

    Safari - parseFloat
    2750 Actual:2748

    Safari - parseInt
    Expected: 2750 Actual:2748

    Mozilla - parseFloat:
    Expected: 2750 Actual:2750.734
    Mozilla - parseInt:
    Expected: 2750 Actual:2749

    IE - (IE does not get here)
    PASS testGetOffsetLeftFloatingPointEM: passed.

    Opera was way off. Forget about accuracy when dealing with EMs in
    Opera.

    All browsers fail when using EM. Mozilla would be the closest because
    it keeps floating pixels.

    Using Math.round and parseFloat now, I can expect the number to be
    correct in Mozilla. Obviously not good for performance. I will go
    through later and try to profile it. I want to hook a profiler into
    the test runner.

    > I need to re-
    > test everything now that I have transplanted the code into a new
    > library (I'm really looking forward to *that*.)
    >


    I use a patched version of the YUI test runner. I think it's less
    painful than using JSUnit. I can help you get started with this if you
    want.


    >
    > What if bcs does not exist. You should allow applications that need
    > to run in ancient browsers to compensate by setting inline styles
    > (i.e. check inline styles as a fallback.)
    >


    getComputedStyle. (I set |bcs| in the poll-timer to; bcs =
    getComputedStlye(document.body,''))

    That is someting I struggled with.

    The reason I get it in the poll timer is to improve runtime
    performance by reducing a function call to getComputedStyle.



    The problem with relying on style attribute is that it's not a
    computed pixel style.

    <div style="left: 2em">hi</div>

    getComputedStyle is (or should be) a pixel value.


    > [snip]
    >
    > ----
    > // Loop up, gathering scroll offsets on parentNodes.
    > // when we get to a parent that's an offsetParent, update
    > // the current offsetParent marker.
    >
    > I prefer to loop through offsetParents and then deal with scrolling
    > parents as an optional afterthought. This makes it easy for
    > applications that do not involve scrolling containers to prevent the
    > extra work. Also, the adjustments for scrolling containers are needed
    > for the gBOF branch.
    >


    It requires two traversals that way, which would seem to be alot
    slower when you need scroll offsets.

    I need to do some profiling on this...



    > ! (parent.tagName === "TABLE" && IS_TABLE_BORDER_INCLUDED_IN_TD_OFFSET)) {
    >
    > You don't need a strict comparison there.


    That's true algorithm is the same for characters. So it doesn't really
    matter, just one extra char.

    application/xhtml+xml:

    I should call tagName.toLowerCase() == 'table' but that costs more for
    each iteration.


    > if(false == IS_PARENT_BORDER_INCLUDED_IN_OFFSET
    >
    > Shouldn't this be !IS_PARENT_BORDER_INCLUDED_IN_OFFSET?
    >

    I thought it looked clearer to see a blue (my editor uses blue for
    keywords) |false| first. When the code gets there, the
    IS_PARENT_BORDER_INCLUDED_IN_OFFSET is already set to true or false.


    > else if(container === doc || container === documentElement) {
    > // If the body is positioned, add its left and top value.
    >
    > // Safari will sometimes return "auto" for computedStyle, which
    > results NaN.
    >
    > So will IE for statically positioned elements. Opera will return
    > incorrect results for those with borders....
    >


    IE doesn't support computedStyle.
    Opera returns wrong results for |left| when the element has a border?
    I can't reproduce that.

    A browser in the second loop that doesn't support getComputedStyle
    will fail horribly. I need to address. Probably
    if(getBoundingClientRect){...}
    else if(bcs){...}

    > (function(){
    > var waitForBodyTimer = setInterval(function
    > domInitLoadTimeConstants() {
    >
    > What is this about? A DOM ready simulation?
    >

    Sort of. It's polling for existence of document.body every 60ms. Are
    there any issues with that?

    > if(!document.body) return;
    >
    > This excludes XHTML documents in Windows Safari and (reportedly) some
    > older Gecko-based browsers.
    >

    I use document.body all over the place. I'm not attempting to address
    the issue.

    > var xs = x.style;
    > xs.margin = 0;
    > xs.position = "static";
    >
    > x = body.appendChild(x);
    >
    > This will cause a twitch during page load. I would make the height
    > and width 0 if you can get away with it in this test.
    >


    Appending a child causes the page to twitch?

    body.style.height = 0;
    to avoid page flicker?

    Would it be better to call
    body.insertBefore(x, body.firstChild)
    ?

    I haven't made any example/usability tests. I'll need them, and prob
    add some shiny css. The psychological effects of CSS and appearance
    are an interesting, but side topic.

    >
    > Why not use DOM methods and lose the try-catch?

    innerHTML is faster and shorter than DOM. I lost the try-catch. It's
    an IE innerHTML bug, but IE doesn't get there anyway.

    <snip>

    > // XXX Safari gets offsetParent wrong (says 'body' when body is static,
    > // but then positions element from ICB and then subtracts body's
    > clientWidth.
    > // Safari is half wrong.
    > //
    > // XXX Mozilla says body is offsetParent but does NOT subtract
    > BODY's offsetWidth.
    >
    > Subtracts BODY's clientWidth?
    >

    I meant to
    // XXX Mozilla says body is offsetParent but does NOT add el's
    negative offsetLeft/Top.

    Example:

    body {
    border: 10px solid red;
    margin: 0;
    padding: 0;
    }

    #el {
    position: absolute;
    top: 0;
    left : 0;
    }

    #el is at 0, 0 from it's containing block, HTML.
    El is outside of it's static parentNode (body).
    #el is (-10, -10) from the body's inner border edge.

    BODY is considered offsetParent (Safari, Webkit, Opera, IE quirks),
    even when it's not a containing block. This is following the spec Anne
    made up.

    el.offsetTop == -10;// Webkit.

    el.offsetTOp == 0; // Mozilla.

    Mozilla gives the offsetTop/Left values from the offsetParent, like
    the MS docs say.

    So in Mozilla, the offsetParent is BODY, and el's offsetTop is 0.

    This is as per Anne's spec.

    Then to compensate for the problem, Mozilla gives BODY an offsetTop/
    Left of (-10, -10).

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



    > s.position = position;
    >
    > s.marginTop = marginTop;
    > // Put back border and padding the way they were.
    > s.border = border;
    > s.padding = padding;
    >
    > This is going to be twitchy.

    Suggestions welcome. I'll probably have to work that out.

    About the test case: The test case performs ~27 tests, setting
    innerHTML on the main content area in setUP and tearDown. It changes
    cssText on body, html, and #container in each setUp tearDown.

    Then the test case does the same thing all over, calling
    window.scrollTo(10, 100);



    >
    > Sorry for the inevitable wrapping. I didn't have time to make this
    > newsreader-friendly.
    >

    That's about the best review I could ask for. I am considering to
    switch from tabs to spaces. I can post code directly here next time.
     
    dhtml, Feb 19, 2008
    #18
  19. dhtml

    David Mark Guest

    On Feb 18, 8:05 pm, dhtml <> wrote:
    > On Feb 17, 9:01 pm, David Mark <> wrote:
    >
    > On Feb 17, 9:01 pm, David Mark <> wrote:
    >
    > > On Feb 17, 5:09 pm, dhtml <> wrote:
    > > I took a little time to survey this.

    >
    > It's an excellent review. Questions about comments/x.d/borderColor, I


    Thanks.

    > just corrected the mistakes and
    > snipped the question to keep focused on other things.
    >
    > >         var IS_BACK_COMPAT = document.compatMode === "BackCompat";

    >
    > > You can't rely on this flag in IE < 6.

    >
    > What do you do? There's likely to eventually be a browser that
    > supports getBoundingClientRect, yet has document.compatMode ==
    > "undefined"


    As I mentioned, the new Opera (Beta at this time I think) does indeed
    support this method. I don't know whether it supports compatMode or
    not. It has been my experience that compatMode tests are largely
    useless for anything but IE. The multi-object inference I use to get
    around this for IE5 (which has no compatMode property and is always in
    quirks mode) is not ideal, but is the best I could come up with. It
    won't affect new versions of standards-compliant browsers, so the new
    Opera will measure the borders of the HTML element. Who knows if that
    will even be necessary with their (or other) future implementations?
    I'll test them and make any needed adjustments when they come out. No
    matter how robust a solution you have for any given browser scripting
    problem, it is no guarantee that future testing will pass with flying
    colors.

    >
    > <snip>
    >
    > > I use a wrapper that uses border styles in lieu of clientLeft/Top.  I
    > > think it makes the code easier to follow.

    >
    > That would be easier to follow; it would reduce conditional checks and
    > put the logic on one place. I was doing this at first, but it was
    > slow. I replaced it with inline code and the time
    > was cut almost in half.


    Certainly. To this end, I try to call functions like this one as
    infrequently as possible.

    >
    > Removing the extra function call made a big difference.
    >
    > >         // XXX Opera <= 9.2 - parent border widths are included in offsetTop.
    > >         var IS_PARENT_BORDER_INCLUDED_IN_OFFSET;

    >
    > > Can I assume from the comment that this flag will be false in the new
    > > Opera?  It doesn't really matter, but it would be good if they changed
    > > their scheme to match other browsers.

    >
    > The flag gets set based on a test:
    >
    > // A closure for initializing load time constants.
    > if(!("getBoundingClientRect"in document.documentElement))
    >   (function(){
    >     var waitForBodyTimer = setInterval(function
    > domInitLoadTimeConstants() {
    >       if(!document.body) return;
    >
    >       clearInterval(waitForBodyTimer);
    >
    >       var x = document.createElement('div');
    >       var xs = x.style;
    >       xs.margin = 0;
    >       xs.position = "static";
    >       x = body.appendChild(x);
    >       IS_PARENT_BORDER_INCLUDED_IN_OFFSET = (x.offsetTop ===1);
    >   ...
    >     }, 60);
    >
    > })();


    I realize that. I was just commenting on the specificity of the
    comment with regard to the versions that display this quirk.

    >
    > <snip>
    >
    > > A positioned body element is a case I didn't consider.  I am not
    > > surprised that such a style causes issues.

    >
    > It is a very real case. Setting position: relative makes an element a
    > containing block for absolutely positioned elements.


    Yes, but a positioned *body* element? I've never seen such a thing
    and can't imagine a use for it. However, I am all for covering as
    many cases as possible (provided the workarounds can be omitted for
    applications that don't need them covered.)

    >
    > For example:
    >
    > #adiv {
    >   position: absolute;
    >   top: 0;}
    >
    > body {
    >   margin: 10px;
    >
    > }


    But this is not a positioned body element. (?)

    >
    > adiv's containing block is HTML. It's offsetParent should be HTML, and
    > in IE, it is.


    Well, there is no standard for offsetParent yet, so it is hard to say
    who is right.

    >
    > In Opera, Safari, Firefox the offsetParent is BODY. This is per Anne
    > van Kesteren's spec.


    If that spec ever becomes a standard, then we can say that IE is
    wrong.

    >
    > In Safari, the offsetTop of adiv will be -10. Given the fact that
    > adiv's offsetParent is BODY, this makes sense.


    None of the browsers seem to agree on absolute/fixed positioned
    elements and their relative offsets. As I recall, such cases were
    responsible for most of my feature testing and quirk detection.

    >
    > >         var
    > > IS_STATIC_BODY_OFFSET_PARENT_BUT_ABSOLUTE_CHILD_SUBTRACTS_BODY_BORDER_WIDTH
    > > = false;

    >
    > > Borders/margins on the HTML element (in standards mode) cause
    > > additional aggravations.

    >
    > >         var getComputedStyle = window.getComputedStyle;
    > >         var bcs;

    >
    > >         var positionedExp = /^(?:r|a|f)/,
    > >                 absoluteExp = /^(?:a|f)/;

    >
    > > There are several issues that are unique to fixed positioning.  For
    > > one, fixed elements in Opera 9 have a null offsetParent.  In this
    > > case, I resorted to getComputedStyle.

    >
    > That would go along with Anne's spec. In the case of fixed
    > positioning, I haven't addressed yet.
    >
    > myFixedDiv.offsetTop
    > myFixedDiv.offsetLeft
    >
    > Would seem to provide the desired result.


    IIRC, it doesn't. AIUI, the lack of an offsetParent should result in
    a lack of offsetTop/Left properties. But of course, there is no real
    standard at this time.

    >
    > >          * @param {boolean} forceRecalc if true, forces recalculation of body
    > > scroll offsets.

    >
    > > This is an interesting idea.  Is it to make things less painful for
    > > applications that don't scroll?

    >
    > It was an idea for addressing the issue where body's border/margin/
    > padding/top/left are unchanging.


    That is an assumption that I somewhat embraced (at least in regard to
    the adjustment of page origins for fixed positioned elements.) I
    think it is too costly to recalculate all of those each time. An
    exception would be for applications that switch style sheets to
    provide "themes." As I have a module for that, perhaps I should
    revisit this issue and at least provide a mechanism to refresh the
    data.

    >
    > It was impossible/extremely difficult to test, and so I removed that.
    >
    > > Same for Firefox if you use getBoxObjectFor.  For some reason, I
    > > didn't do this in the getBoundingClientRect branch, but resorted to
    > > the offsetParent loop.  IIRC, IE6/7 had the fewest issues with that
    > > method, though I should change it to work like the gBOF branch for
    > > performance reasons.

    >
    > getBoxObjectFor is being discouraged in a bugzilla:https://bugzilla.mozilla.org/show_bug.cgi?id=340571


    Oh well. I tested Firefox and Netscape with that branch disabled, so
    I am confident I can get rid of it if needed. That being said, I
    found gBOF to be very accurate, fast and relatively quirk-free. I'll
    have to check out that article before I decide what do there.

    >
    > It's not supported for HTML. It's not guaranteed to provide any


    What is it supported for then?

    > results in HTML documents.


    I believe I handled the case where it provided an unexpected result,
    but I have never run into that in testing.

    >
    > <snip>
    >
    >
    >
    > > Can you elaborate on re-used coords?

    >
    > If you call getOffsetCoords(el, cont),
    >
    > it creates an object {x: 0, y: 0};
    >
    > if you pass in an object
    > getBoxOffsetCoords(el. cont, this.coords);
    >
    > It doesn't create a new object. For drag operations, it could mean
    > creating hundreds of objects.


    Do you mean for drag operations that have hundreds of drop targets?

    >
    > //update coords, no need to return anything.
    > getOffsetCoords(dropTarget.el, document, dropTarget.coords);
    >
    > >         function getOffsetCoords(el, container, coords) {

    >
    > >                 var doc = document, body = doc.body,documentElement =
    > > doc.documentElement;

    >
    > > Apparently this function is good for one document.  I think it is a
    > > good idea to allow for multiple documents in functions like these
    > > (e.g. for iframes, objects, etc.)

    >
    > I am too lazy to write a frame-based test. It might work. I guess it
    > wouldn't hurt to put in:
    >
    > doc = el.ownerDocument.


    See getElementDocument in the CWR project.

    >
    > >                 if("getBoundingClientRect"in el) {

    >
    > > I would avoid the in operator for compatibility reasons.

    >
    > What copatibility reasons?


    There are older browsers that do not support it. Regardless, the
    isHostMethod function (also in CWR) has been shown to be the best
    solution for this sort of feature detection.

    >
    > >                         // In BackCompat mode, body's border goes to the window. BODY is
    > > ICB.
    > >                         var rootBorderEl = (IS_BACK_COMPAT ? body : documentElement);

    >
    > > But this flag isn't correct in IE < 6 and those versions do not
    > > display the documentElement.  IIRC, this is okay in this case as I
    > > think IE5.x considers the viewport border to be part of the (otherwise
    > > invisible) HTML element.

    >
    > I don't know how to address this. I don't even have IE6, much less IE
    > 5.5 to test on.


    I have IE5.0 and IE5.5 installed side-by-side with IE6 on one of my
    test boxes. When I get around to re-testing my offset position code,
    I will let you know how things turn out. IIRC, I used to resort to
    conditional compilation (ecch!) to work around this as IE5.x does have
    a documentElement property, but recently switched to a multiple object
    inference (still ugly, but at least it minifies properly.) The
    primary issue here is that many older browsers (and some newer ones)
    do not have a compatMode property, but do have a documentElement,
    which may or may not be a part of the layout of the page (in IE5.x it
    is not.)

    >
    > >                         var box = el.getBoundingClientRect();
    > >                         var x, y;
    > >                         x = box.left - rootBorderEl.clientLeft
    > >                                         + Math.max( documentElement.scrollLeft, body.scrollLeft );
    > >                         y = box.top - rootBorderEl.clientTop
    > >                                         + Math.max( documentElement.scrollTop, body.scrollTop );
    > >                         if(container !== doc) {
    > >                                 box = getOffsetCoords(container, null);
    > >                                 x -= box.x;
    > >                                 y -= box.y;
    > >                         }
    > >                         if(IS_BACK_COMPAT) {
    > >                                 var curSty = body.currentStyle;

    >
    > > Object inference based on getBoundingClientRect (will break in the new
    > > Opera.)

    >
    > That's true, it is object inference. The code assumes:
    > if documet.getBoundingClientRect, then body.currentStyle is supported.
    > Coincidentally Opera supports currentStyle. It is very likely that
    > this


    IIRC, the currentStyle property doesn't work very well in Opera.
    Thanks for reminding me as I need to revisit this in my unit test for
    retrieving cascaded styles.

    > will break in future versions of FF/Safari.
    >
    > Changed to:
    > if(IS_BACK_COMPAT && IS_CURRENT_STYLE_SUPPORTED) {
    >
    >
    >
    > >                                 x += parseInt(curSty.marginLeft);
    > >                                 y += parseInt(curSty.marginTop);

    >
    > > Use parseFloat.

    >
    > Am I missing a fraction of a pixel? I think the coords returned should


    They add up.

    > be integers. I can't remember, but I think I remember IE having
    > problems with style values with floating point numbers.


    IE will absolutely return cascaded styles with fractions of a pixel
    when, for instance, em-based layouts are used.

    >
    > Using parseInt and parseFloat:
    >
    > Opera 9.2: (same result for both)
    > Expected: 2750  Actual:1794
    >
    > Safari - parseFloat
    > 2750  Actual:2748
    >
    > Safari - parseInt
    > Expected: 2750  Actual:2748
    >
    > Mozilla - parseFloat:
    > Expected: 2750  Actual:2750.734
    > Mozilla - parseInt:
    > Expected: 2750  Actual:2749
    >
    > IE - (IE does not get here)
    > PASS testGetOffsetLeftFloatingPointEM: passed.
    >
    > Opera was way off. Forget about accuracy when dealing with EMs in
    > Opera.


    I haven't had any issues with it. My entire test page is em-based to
    deliberately provoke such inconsistencies. I do recall that FF has an
    internal round-off error when computing top/left styles of statically
    positioned elements.

    >
    > All browsers fail when using EM. Mozilla would be the closest because
    > it keeps floating pixels.


    That has not been my experience. I even use an em-based border on the
    body of the test page, which is clearly courting disaster (as
    intended.) The worst I have seen is a single pixel round-off error in
    FF, which I have yet to address as I am 99.9% that it is a problem in
    their code (and not an easy one to work around.)

    >
    > Using Math.round and parseFloat now, I can expect the number to be
    > correct in Mozilla. Obviously not good for performance. I will go
    > through later and try to profile it. I want to hook a profiler into
    > the test runner.
    >
    > > I need to re-
    > > test everything now that I have transplanted the code into a new
    > > library (I'm really looking forward to *that*.)

    >
    > I use a patched version of the YUI test runner. I think it's less
    > painful than using JSUnit. I can help you get started with this if you
    > want.


    Thanks, but I have developed a custom testing framework. Granted,
    YUI's is probably better, but I don't have time to mess with it now.

    >
    >
    >
    > > What if bcs does not exist.  You should allow applications that need
    > > to run in ancient browsers to compensate by setting inline styles
    > > (i.e. check inline styles as a fallback.)

    >
    > getComputedStyle. (I set |bcs| in the poll-timer to; bcs =
    > getComputedStlye(document.body,''))


    But gCS may not exist, so you should allow applications to compensate
    for this in older browsers by mirroring computed styles inline. This
    is most important for code that relies on determining a positioned
    parent. Certainly it can make a lesser difference for borders,
    margins, etc.

    >
    > That is someting I struggled with.
    >
    > The reason I get it in the poll timer is to improve runtime
    > performance by reducing a function call to getComputedStyle.
    >
    > The problem with relying on style attribute is that it's not a
    > computed pixel style.
    >
    > <div style="left: 2em">hi</div>


    I am aware of that. It is sometimes possible (e.g. in IE) to compute
    the proper value. Regardless, an application developer who wishes to
    support ancient browsers would be well advised to use pixel units for
    certain inline styles (e.g. border.)

    >
    > getComputedStyle is (or should be) a pixel value.


    It is indeed. IIRC, IE's currentStyle property (which is cascaded
    rather than computed) returns pixels in IE6 (but not IE7) in at least
    some cases.

    >
    > > [snip]

    >
    > > ----
    > >                                 // Loop up, gathering scroll offsets on parentNodes.
    > >                                 // when we get to a parent that's an offsetParent, update
    > >                                 // the current offsetParent marker.

    >
    > > I prefer to loop through offsetParents and then deal with scrolling
    > > parents as an optional afterthought.  This makes it easy for
    > > applications that do not involve scrolling containers to prevent the
    > > extra work.  Also, the adjustments for scrolling containers are needed
    > > for the gBOF branch.

    >
    > It requires two traversals that way, which would seem to be alot
    > slower when you need scroll offsets.


    Yes, but often much faster when you don't. I think that most
    applications will not need to worry about element scroll offsets.

    >
    > I need to do some profiling on this...
    >
    > >  ! (parent.tagName === "TABLE" && IS_TABLE_BORDER_INCLUDED_IN_TD_OFFSET)) {

    >
    > > You don't need a strict comparison there.

    >
    > That's true algorithm is the same for characters. So it doesn't really
    > matter, just one extra char.


    My issue with such practices is that it gives me pause when browsing
    the code (I have to stop and consider why the === operator is in use
    and decide whether it was needed or simply overkill.)

    >
    > application/xhtml+xml:
    >
    > I should call tagName.toLowerCase() == 'table' but that costs more for
    > each iteration.


    Yes, but I think the extra effort is worth it. These sorts of
    functions are always going to be a bottleneck, so I endeavor to design
    systems that call them as infrequently as possible (if at all.)

    >
    > > if(false == IS_PARENT_BORDER_INCLUDED_IN_OFFSET

    >
    > > Shouldn't this be !IS_PARENT_BORDER_INCLUDED_IN_OFFSET?

    >
    > I thought it looked clearer to see a blue (my editor uses blue for
    > keywords) |false| first. When the code gets there, the
    > IS_PARENT_BORDER_INCLUDED_IN_OFFSET is already set to true or false.
    >
    > >                                 else if(container === doc || container === documentElement) {
    > >                                         // If the body is positioned, add its left and top value.

    >
    > >                                         // Safari will sometimes return "auto" for computedStyle, which
    > > results NaN.

    >
    > > So will IE for statically positioned elements.  Opera will return
    > > incorrect results for those with borders....

    >
    > IE doesn't support computedStyle.


    I was referring to its cascaded style property (currentStyle), which
    IIRC, does return computed styles in some situations (e.g. font sizes
    in IE6.) What exactly is a valid computed left or top style for a
    statically positioned element? Nothing would seem to make sense.
    These styles clearly have no effect on such elements.

    > Opera returns wrong results for |left| when the element has a border?
    > I can't reproduce that.


    It does in my copy of Opera 9 (I forget which revision.) I had to put
    a quirk test in for that. Same for height/width. They were always
    off by the width of the border(s).

    >
    > A browser in the second loop that doesn't support getComputedStyle
    > will fail horribly. I need to address. Probably
    > if(getBoundingClientRect){...}
    > else if(bcs){...}
    >
    > >         (function(){
    > >         var waitForBodyTimer = setInterval(function
    > > domInitLoadTimeConstants() {

    >
    > > What is this about?  A DOM ready simulation?

    >
    > Sort of. It's polling for existence of document.body every 60ms. Are
    > there any issues with that?


    It just seems like an odd way to go about it. Why not use
    attachDocumentReadyListener from CWR?

    >
    > >                 if(!document.body) return;

    >
    > > This excludes XHTML documents in Windows Safari and (reportedly) some
    > > older Gecko-based browsers.

    >
    > I use document.body all over the place. I'm not attempting to address
    > the issue.


    Fair enough.

    >
    > >                 var xs = x.style;
    > >                 xs.margin = 0;
    > >                 xs.position = "static";

    >
    > >                 x = body.appendChild(x);

    >
    > > This will cause a twitch during page load.  I would make the height
    > > and width 0 if you can get away with it in this test.

    >
    > Appending a child causes the page to twitch?


    More specifically, appending and then removing a statically positioned
    element will cause the scrollbar to twitch.

    >
    > body.style.height = 0;
    > to avoid page flicker?


    I meant the height and width of the appended static element. And I
    would avoid assumptions about how host objects type convert values
    assigned to their properties.

    >
    > Would it be better to call
    > body.insertBefore(x, body.firstChild)


    Then the whole page will twitch (unless x has no height and width.)

    > ?
    >
    > I haven't made any example/usability tests. I'll need them, and prob
    > add some shiny css. The psychological effects of CSS and appearance
    > are an interesting, but side topic.


    For a test page?

    >
    >
    >
    > > Why not use DOM methods and lose the try-catch?

    >
    > innerHTML is faster and shorter than DOM. I lost the try-catch. It's
    > an IE innerHTML bug, but IE doesn't get there anyway.


    I forget what branch we were talking about. Could future versions of
    IE get there? Assuming they do not fix the innerHTML/table problems,
    you would have an issue.

    >
    > <snip>
    >
    > > // XXX Safari gets offsetParent wrong (says 'body' when body is static,
    > > // but then positions element from ICB and then subtracts body's
    > > clientWidth.
    > > // Safari is half wrong.
    > > //
    > > // XXX Mozilla says body is offsetParent but does NOT subtract
    > > BODY's offsetWidth.

    >
    > > Subtracts BODY's clientWidth?

    >
    > I meant to
    > // XXX Mozilla says body is offsetParent but does NOT add el's
    > negative offsetLeft/Top.
    >
    > Example:
    >
    > body {
    >   border: 10px solid red;
    >   margin: 0;
    >   padding: 0;
    >
    > }
    >
    > #el {
    >   position: absolute;
    >   top: 0;
    >   left : 0;
    >
    > }
    >
    > #el is at 0, 0 from it's containing block, HTML.
    > El is outside of it's static parentNode (body).
    > #el is (-10, -10) from the body's inner border edge.
    >
    > BODY is considered offsetParent (Safari, Webkit, Opera, IE quirks),
    > even when it's not a containing block. This is following the spec Anne
    > made up.
    >
    > el.offsetTop == -10;// Webkit.
    >
    > el.offsetTOp == 0; // Mozilla.


    Yes, I am quite familiar with that one. It was a major source of pain
    until I came up with a viable test for it.

    >
    > Mozilla gives the offsetTop/Left values from the offsetParent, like
    > the MS docs say.
    >
    > So in Mozilla, the offsetParent is BODY, and el's offsetTop is 0.
    >
    > This is as per Anne's spec.
    >
    > Then to compensate for the problem, Mozilla gives BODY an offsetTop/
    > Left of (-10, -10).


    I know. I cursed them repeatedly when I first ran into that.
    Happily, there turned out to be a simple feature test that worked in
    all (tested) Gecko-based browsers. At least they are consistent.

    >
    > https://bugzilla.mozilla.org/show_bug.cgi?id=255754
    >
    > >                 s.position = position;

    >
    > >                 s.marginTop = marginTop;
    > >                 // Put back border and padding the way they were.
    > >                 s.border = border;
    > >                 s.padding = padding;

    >
    > > This is going to be twitchy.

    >
    > Suggestions welcome. I'll probably have to work that out.


    I forget why you were doing that. All I can say at the moment is that
    I didn't include such logic in my take on this problem. Perhaps I
    ignored the specific case you are testing.

    >
    > About the test case: The test case performs ~27 tests, setting
    > innerHTML on the main content area in setUP and tearDown. It changes
    > cssText on body, html, and #container in each setUp tearDown.
    >
    > Then the test case does the same thing all over, calling
    > window.scrollTo(10, 100);


    Sounds good. The last thing you want to miscalculate is the
    document's scroll position.

    >
    >
    >
    > > Sorry for the inevitable wrapping.  I didn't have time to make this
    > > newsreader-friendly.

    >
    > That's about the best review I could ask for. I am considering to
    > switch from tabs to spaces. I can post code directly here next time.


    I made that switch recently and it makes for easier posting. It is a
    little more bloat for unminified builds, but I don't recommend using
    those in production anyway.
     
    David Mark, Feb 19, 2008
    #19
  20. dhtml

    dhtml Guest

    On Feb 18, 6:51 pm, David Mark <> wrote:
    > On Feb 18, 8:05 pm, dhtml <> wrote:
    >
    > > On Feb 17, 9:01 pm, David Mark <> wrote:

    >
    > > On Feb 17, 9:01 pm, David Mark <> wrote:

    >
    > > > On Feb 17, 5:09 pm, dhtml <> wrote:

    >


    <snip>

    >
    >
    >
    > > <snip>

    >
    > > > A positioned body element is a case I didn't consider. I am not
    > > > surprised that such a style causes issues.

    >
    > > It is a very real case. Setting position: relative makes an element a
    > > containing block for absolutely positioned elements.

    >
    > Yes, but a positioned *body* element? I've never seen such a thing
    > and can't imagine a use for it. However, I am all for covering as
    > many cases as possible (provided the workarounds can be omitted for
    > applications that don't need them covered.)


    That way absolutely-positioned children of BODY stay inside body. I do
    this a lot.

    > > For example:

    >
    > > #adiv {
    > > position: absolute;
    > > top: 0;}

    >
    > > body {
    > > margin: 10px;

    >
    > > }

    >
    > But this is not a positioned body element. (?)
    >
    >

    And that's why it's weird. You have a 10px margin around body.
    #adiv's containing block is HTML.
    BODY is not (or shouldn't be) a majical element.

    > > adiv's containing block is HTML. It's offsetParent should be HTML, and
    > > in IE, it is.

    >
    > Well, there is no standard for offsetParent yet, so it is hard to say
    > who is right.
    >

    My Definition:
    offsetParent - a containing block that is offset by margin or left/top
    values.

    Anne van kesteren started this 2 years ago:

    Editor's Draft 9 December 2007
    "1. If any of the following holds true return null and stop this
    algorithm:

    * A is the root element.
    * A is the HTML body element.
    * The computed value of the position property for element A is
    fixed.

    2...

    3. Return the nearest ancestor element of A for which at least one of
    the following is true and stop this algorithm if such an ancestor is
    found:

    * The computed value of the position property is not static.
    * It is the HTML body element.
    * The computed value of the position property of A is static and
    the ancestor is one of the following HTML elements: td, th, or table.
    "

    http://dev.w3.org/cvsweb/csswg/cssom-view/Overview.html?rev=1.2#offset-attributes


    > > In Opera, Safari, Firefox the offsetParent is BODY. This is per Anne
    > > van Kesteren's spec.

    >
    > If that spec ever becomes a standard, then we can say that IE is
    > wrong.
    >

    I would hope that this spec be forgotten a new one started.

    The spec is self contradictory, and thus impossible to implement
    correctly.

    What is A's offsetParent?
    if A is BODY then
    offsetParent is null

    As such, A can then have neither offsetTop, nor offsetLeft. BODY can
    have margin, top, left, border, and position.

    All of these things move the BODY element.

    For some reason, containing block doesn't come up in the document at
    all. Nor does "initial containing block".

    The spec is inaccurate to the point of being almost completely
    fictitious. The damage of having such a spec is considerably large.

    The damage can be seen in Opera, Mozilla, and Safari. These browsers
    have seemed to have attempted to implement the (impossible) spec. None
    of them get it right, of course. It's impossible.

    This is a simple test that barely scratches the surface
    http://dhtmlkitchen.com/ape/test/tests/dom/inline-offsetTop.html


    <snip>

    > > It's not supported for HTML. It's not guaranteed to provide any

    >
    > What is it supported for then?
    >

    getBoxObjectFor is for XUL

    > > > Can you elaborate on re-used coords?

    <snip>
    > Do you mean for drag operations that have hundreds of drop targets?


    When checking drop target coords, with scrolling or moving dropTargets
    (while dragging), coord checks must be done continually, while
    dragging.

    There might be a better strategy for checking coords.

    > > I am too lazy to write a frame-based test. It might work. I guess it
    > > wouldn't hurt to put in:

    >

    Besides, I've got load-time constant for determining certain things
    about the document, those things could easily be different in the
    framed document. It is an edge case that is not worth testing. It
    might be worth testing to throw an error if el.ownerDocument != doc;
    Then the user knows because it fails right away.

    > > doc = el.ownerDocument.

    >
    > See getElementDocument in the CWR project.
    >


    Why just use ownerDocument?

    > > > if("getBoundingClientRect"in el) {

    >
    > > > I would avoid the in operator for compatibility reasons.

    >
    > > What copatibility reasons?

    >
    > There are older browsers that do not support it. Regardless, the
    > isHostMethod function (also in CWR) has been shown to be the best
    > solution for this sort of feature detection.


    I didn't consider that. I reading some old thread where it says that
    IE5.0 doesn't support |in|, so my library will break there. I use |in|
    a lot. It's really the only way to tell if an object has a property.

    > primary issue here is that many older browsers (and some newer ones)
    > do not have a compatMode property, but do have a documentElement,
    > which may or may not be a part of the layout of the page (in IE5.x it
    > is not.)
    >

    var IS_BACK_COMPAT = document.compatMode === "BackCompat" ||
    documentElement && documentElement.clientWidth === 0);

    It's a stronger inference than compatMode (value or absence) alone.
    Not perfect, but better.

    <snip>

    > IIRC, the currentStyle property doesn't work very well in Opera.
    > Thanks for reminding me as I need to revisit this in my unit test for
    > retrieving cascaded styles.


    Does getComputedStyle provides more accurate results?

    Opera has some css rounding issues anyway IIRC.

    getting styles is painful topic worthy of a new group. I can't even
    get currentStyle.clip in IE. I found only clipTop/Left, et c.

    > > Am I missing a fraction of a pixel? I think the coords returned should

    >
    > They add up.
    >
    > > be integers. I can't remember, but I think I remember IE having
    > > problems with style values with floating point numbers.

    >
    > IE will absolutely return cascaded styles with fractions of a pixel
    > when, for instance, em-based layouts are used.
    >


    I was probably confused with setting fontWeight or zIndex.
    style.fontWeight = 101 => Error
    style.zIndex = 1.2 => Error? I can't remember. IE is less forgiving.


    > I haven't had any issues with it. My entire test page is em-based to
    > deliberately provoke such inconsistencies. I do recall that FF has an
    > internal round-off error when computing top/left styles of statically
    > positioned elements.


    There are issues with browsers rounding pixel values:
    http://groups.google.com/group/comp...caf23?lnk=gst&q=IE7 rounding#8ae40d8a59dcaf23
    http://ejohn.org/blog/sub-pixel-problems-in-css/


    > That has not been my experience. I even use an em-based border on the
    > body of the test page, which is clearly courting disaster (as
    > intended.) The worst I have seen is a single pixel round-off error in
    > FF, which I have yet to address as I am 99.9% that it is a problem in
    > their code (and not an easy one to work around.)


    Try using a torture case and you'll see more errors.

    I used fontSize = "777px";
    with 1.18em on two nested elements.

    It's a real torture test. Amazinly, MOzilla got it. webkit's 2px off.

    I don't like seeing failing tests, but I'm leaving it in. It's a way
    to say:
    "if you try to get an exact calcuation off EM units, it might be off a
    few px in some edge cases on some browsers."

    testGetOffsetLeftFloatingPointEM : function() {
    var fontSize = "10px";
    var target = document.getElementById("target");
    var container = document.getElementById("container");
    var c1 = document.getElementById("c1");
    document.documentElement.style.fontSize =
    document.body.style.fontSize =
    c1.style.fontSize =
    container.style.fontSize =
    target.style.fontSize = "777px";

    container.style.borderLeft =
    c1.style.borderLeft = "1.18em solid red";
    target.style.marginLeft = "1.18em";
    target.style.left = 0;
    var expected = Math.round(777 * 1.18 * 3);
    var actual = dom.getOffsetCoords(target);
    Assert.areEqual(expected, actual.x, "getting computed coords from
    EM values failed.");
    },



    > > getComputedStyle. (I set |bcs| in the poll-timer to; bcs =
    > > getComputedStlye(document.body,''))

    >
    > But gCS may not exist, so you should allow applications to compensate
    > for this in older browsers by mirroring computed styles inline. This
    > is most important for code that relies on determining a positioned
    > parent. Certainly it can make a lesser difference for borders,
    > margins, etc.


    I'm not testing in older browsers. I am going to try to get it going
    in IE6, Safari 2, FF 2, Opera 9.

    I am going to exit the function if neither getCOmputedStyle nor
    getBoundingRect are supported.

    > > The problem with relying on style attribute is that it's not a
    > > computed pixel style.

    >
    > > <div style="left: 2em">hi</div>

    >
    > I am aware of that. It is sometimes possible (e.g. in IE) to compute
    > the proper value. Regardless, an application developer who wishes to
    > support ancient browsers would be well advised to use pixel units for
    > certain inline styles (e.g. border.)


    I hope you're not talking about the Dean Edwards hack that's used in
    jQuery's css(). I've tested it thoroughly. The rounding errors are as
    bad as Opera (or worse).

    Using that hack, it's possible to have close results.

    style.fontSize = "10px";
    style.height= "2em";

    You can get an acceptible result.

    When I started using bigger numbers, I got rounding errors.

    > > getComputedStyle is (or should be) a pixel value.

    >
    > It is indeed.

    Webkit returns 'auto' when margin: auto and left is undeclared.
    http://www.dhtmlkitchen.com/test/bug/getComputedStyle.html

    IRC, IE's currentStyle property (which is cascaded
    > rather than computed) returns pixels in IE6 (but not IE7) in at least
    > some cases.


    Hmm.

    There's also style.pixelTop and curentStyle.pixelTop (Opera).

    > Yes, but often much faster when you don't. I think that most
    > applications will not need to worry about element scroll offsets.


    I need to get that profiler going.

    <snip>

    > > application/xhtml+xml:

    >
    > > I should call tagName.toLowerCase() == 'table' but that costs more for
    > > each iteration.

    >
    > Yes, but I think the extra effort is worth it. These sorts of
    > functions are always going to be a bottleneck, so I endeavor to design
    > systems that call them as infrequently as possible (if at all.)


    var TABLE = /[A-Z]/.test(documentElement.tagName) ? "TABLE" : "table";

    Now I can use it as a constant:

    (parent.tagName === TABLE

    > I was referring to its cascaded style property (currentStyle), which
    > IIRC, does return computed styles in some situations (e.g. font sizes
    > in IE6.) What exactly is a valid computed left or top style for a
    > statically positioned element? Nothing would seem to make sense.
    > These styles clearly have no effect on such elements.


    Only 0 would make sense to me.

    When an element has position: static, top and left values do not
    apply.

    > > Opera returns wrong results for |left| when the element has a border?
    > > I can't reproduce that.

    >
    > It does in my copy of Opera 9 (I forget which revision.) I had to put
    > a quirk test in for that. Same for height/width. They were always
    > off by the width of the border(s).


    I've got Opera 9.25 and it doesn't do that.

    > It just seems like an odd way to go about it. Why not use
    > attachDocumentReadyListener from CWR?


    It seemed simple enough. I guess it's making the code messy the way I
    have it?

    <snip>

    > > I haven't made any example/usability tests. I'll need them, and prob
    > > add some shiny css. The psychological effects of CSS and appearance
    > > are an interesting, but side topic.

    >
    > For a test page?
    >

    Sure, why not?

    I've gone and set the height to "0" (string).

    I want to see the twitch you mentioned. I generally dislike making
    changes without a proven reason.

    That's why I need a demo.

    > I forget what branch we were talking about. Could future versions of
    > IE get there? Assuming they do not fix the innerHTML/table problems,
    > you would have an issue.


    Only browsers that do not support getBoundingClientRect get there.

    > > Example:

    >
    > > body {
    > > border: 10px solid red;
    > > margin: 0;
    > > padding: 0;

    >
    > > }

    >
    > > #el {
    > > position: absolute;
    > > top: 0;
    > > left : 0;

    >
    > > }

    >
    > > #el is at 0, 0 from it's containing block, HTML.
    > > El is outside of it's static parentNode (body).
    > > #el is (-10, -10) from the body's inner border edge.

    >
    > > BODY is considered offsetParent (Safari, Webkit, Opera, IE quirks),
    > > even when it's not a containing block. This is following the spec Anne
    > > made up.

    >
    > > el.offsetTop == -10;// Webkit.

    >
    > > el.offsetTOp == 0; // Mozilla.

    >
    > Yes, I am quite familiar with that one. It was a major source of pain
    > until I came up with a viable test for it.


    I don't doubt that it is and will continue to be a source of pain for
    a lot of people, including members of the Webkit team.

    I bet they struggled - and will continue to struggle - with Anne van
    Kesteren's "unofficial" spec.

    I wonder how many man hours (or months) have been put into
    compensating for this careless mistake?

    > > Mozilla gives the offsetTop/Left values from the offsetParent, like
    > > the MS docs say.

    >
    > > So in Mozilla, the offsetParent is BODY, and el's offsetTop is 0.

    >
    > > This is as per Anne's spec.

    >
    > > Then to compensate for the problem, Mozilla gives BODY an offsetTop/
    > > Left of (-10, -10).

    >
    > I know. I cursed them repeatedly when I first ran into that.
    > Happily, there turned out to be a simple feature test that worked in
    > all (tested) Gecko-based browsers. At least they are consistent.
    >

    I'm going to have a look at getElementPosition().

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

    >
    > > > s.position = position;

    >
    > > > s.marginTop = marginTop;
    > > > // Put back border and padding the way they were.
    > > > s.border = border;
    > > > s.padding = padding;

    >
    > > > This is going to be twitchy.

    >
    >
    > I forget why you were doing that. All I can say at the moment is that
    > I didn't include such logic in my take on this problem. Perhaps I
    > ignored the specific case you are testing.
    >

    I can try to take those out, then do some calculations by reading the
    styles and doing math.

    Since I have the tests in place now, I can see where it breaks when I
    change it.

    > > About the test case: The test case performs ~27 tests, setting
    > > innerHTML on the main content area in setUP and tearDown. It changes
    > > cssText on body, html, and #container in each setUp tearDown.

    >
    > > Then the test case does the same thing all over, calling
    > > window.scrollTo(10, 100);

    >
    > Sounds good. The last thing you want to miscalculate is the
    > document's scroll position.


    Garrett
     
    dhtml, Feb 19, 2008
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Eshrath Ali Khan
    Replies:
    1
    Views:
    856
    Joris Gillis
    Nov 9, 2004
  2. Konrad Hammerer
    Replies:
    14
    Views:
    628
    Jonathan N. Little
    Feb 7, 2008
  3. Angus
    Replies:
    10
    Views:
    955
    Jonathan Lee
    Mar 11, 2011
  4. Knut
    Replies:
    1
    Views:
    152
    George Hester
    Oct 15, 2004
  5. Replies:
    4
    Views:
    128
    wisestpotato
    Mar 26, 2007
Loading...

Share This Page