absolutely positioned child has negative offsetTop/Left when parent has border, overflow:hidden, position:relative




12 years ago
a year ago


(Reporter: jbrunette, Unassigned)


(Blocks: 1 bug, {regression, testcase})

1.8 Branch
Windows XP
regression, testcase
Bug Flags:
blocking1.9 -
wanted1.9 +
blocking1.8.1 -
blocking1.8.0.1 -

Firefox Tracking Flags

(Not tracked)


(Whiteboard: spec needed)


(2 attachments, 2 obsolete attachments)



12 years ago
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4) Gecko/20050907 Firefox/1.4
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8b4) Gecko/20050907 Firefox/1.4

Firefox 1.5 beta1 release candidate build.

If a parent element has a border, overflow:hidden and position:relative, a child
element's offsetLeft and offsetTop could be negative.

Reproducible: Always

Steps to Reproduce:
1. View attached testcase

Actual Results:  
test's offsetTop = -100
test's offsetLeft = -100

Expected Results:  
test's offsetTop = 0
test's offsetLeft = 0

Comment 1

12 years ago
Created attachment 195268 [details]
testcase illustrating the problem

Comment 2

12 years ago
Duplicate of bug 255754?

Comment 3

12 years ago
I tested the testcase with and without "<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML
4.01 Transitional//EN">) to be on the safe side.

Out of curiousity, I removed EITHER the "overflow: hidden;" OR "position:
relative" to see how this testcase react.  What I found instead is that
offsetTop and offsetLeft did return to a value of zero.  It's an unexpected
result.  Maybe, it is not be a dup of bug 255754.
Component: General → DOM
Keywords: testcase
Product: Firefox → Core
Version: unspecified → 1.8 Branch

Comment 4

12 years ago
Opera 8.02 also renders offsetLeft, offsetTop differently depending on position
and overflow values. 
The problem to begin with, the origin of the problem is that MSDN does not
define strictly, explicitly how offsetLeft/offsetTop should be computed in
normal situations and in various other situations. Even offsetParent is not
defined clearly: there is no precise spec. - formal rigorous spec - identifying
how the offsetParent can be, should be determined. offsetLeft, offsetTop
definitions are circularly dependent, self-inter-dependent to offsetParent
Add to this quirks mode vs standards rendering mode, and other properties
(padding, border, margin, box-sizing) which  interfere or affect too
calculations of offsetLeft, offsetTop or which may influence too the way
offsetParent is to be defined and then you have a real mess, headache,
impossible task.

This bug is entirely up to module owner.

Comment 5

12 years ago
Also, since these properties (offsetLeft/Top/Parent) come from Microsoft DHTML
object model, when trying to implement such properties as correctly as one can,
we also become chained, dependent on how Microsoft defines and implements those
properties. Who knows if in 3 months these properties will be re-defined
differently and implemented differently in IE 7... 

-> Component: DOM: Mozilla Extensions
Component: DOM → DOM: Mozilla Extensions
Assignee: nobody → general
QA Contact: general → ian
So what's IE's behavior here?
Keywords: qawanted
IE6 results:
test's offsetTop is 100
test's offsetLeft is 100

Mozilla1.7 results:
test's offsetTop is 0
test's offsetLeft is 0

Current build results:
test's offsetTop is -100
test's offsetLeft is -100

It might be useful to know when this behavior changed in Mozilla (Ria?).
Between 1.8b2_2005040306 and 1.8b2_2005040406 it changed from 0 to -100.


Looks like the scrollframe changes to not be a box frame changed something that the DOM was relying on...  This breaks the use of offsetTop/Left for this relatively common case, so we should really try to get this fixed on the branches too.  :(
Ever confirmed: true
Flags: blocking1.8.1?
Flags: blocking1.8.0.1?
Keywords: regression
Created attachment 207087 [details]
MSIE on testcase
Note that for MSIE what I'm more interested in is not just behavior on this testcase but behavior in general.  What does offset* actually do?
What a mess. Maybe Ian can write a spec for this? (or maybe he already has? hope hope hope)
I'd really like to have a spec before messing with that code.
Agreed; hence the questions about what IE does.
If we're still trying to write a spec it doesn't sound like this could possibly make  If you get a safe, tested patch checked into the trunk you can request approval on it.
Flags: blocking1.8.0.1? → blocking1.8.0.1-
Whiteboard: spec needed
Flags: blocking1.9a1?

Comment 16

11 years ago
Shoot: http://dump.testsuite.org/2006/dom/style/offset/spec

According to the specification we should not do the border quirks from Internet Explorer and just calculate from border to border in case there is an offsetParent and otherwise from border to the initial containing block. This is what Opera has implemented at the moment.
Does that give sufficient compat with IE that the properties are usable in these situations?

Comment 18

11 years ago
Yeah. I'd say more usable as you never have to add the border yourself.

The only real quirk you have to take over from Internet Explorer (as said somewhere in another bug) is having document.body (wrongly indicated as HTMLBodyElement in the specification) as final offsetParent which means that document.body will always have an offsetLeft and offsetTop of 0 and when document.body is the offsetParent you calculate things against the initial containing block instead of against document.body.

(The difference between document.body and HTMLBodyElement is strictly that multiple nodes can implement the HTMLBodyElement interface, but there's only one document.body ever.)
document.body can be an HTMLFramesetElement, for what it's worth.  What happens then?

Comment 20

11 years ago
Yeah, I haven't worked out frames yet (see open issues). Hope to get to that before July, but there are other things I have to do as well...

Comment 21

11 years ago
Not going to block 1.8.1 for this bug, but we'll happily consider a baked-on-trunk patch.
Flags: blocking1.8.1? → blocking1.8.1-

Comment 22

11 years ago
So in Internet Explorer every <frameset> element is part of the offsetParent chain. The offsetParent of the <frameset> element that matches document.body is <html> and not null, surprisingly enough. I guess it makes sense to follow that, except perhaps for the fact that here document.body.offsetParent is <html> instead of null... I'd rather be consistent.
Flags: blocking1.9a1? → blocking1.9-
Whiteboard: spec needed → [wanted-1.9]spec needed

Comment 23

11 years ago
I think this is more general problem. If a parent element's css overflow property value is not visible(i.e. auto,hidden or scroll), then the offsetLeft and offsetTop values of child element will be wrong. The result offsetTop value happens to be the correct value substracts the border-top-width of parent element. The result offsetLeft value happens to be the correct value substracts the border-left-width of parent element. I think this is due to BoxObject bug. please check bug#353363 and bug#356066. 

Blocks: 353363
offset* does not use box objects.  So bug 356066 is not relevant.

Again, what this bug needs is a spec.

Comment 25

10 years ago
The negative offset bug is gone on trunk.

I think the issue here is that what IE does here is a bit insane.  Apparently, we're supposed to subtract out the border for absolutely positioned elements, but not for relatively positioned ones.
Flags: wanted1.9+
Whiteboard: [wanted-1.9]spec needed → spec needed
Assignee: general → nobody
QA Contact: ian → general

Comment 26

8 years ago
Created attachment 403764 [details]
Div sizing/positioning test page set to show negative offetLeft problem

This page shows a negative offsetTop/Left value (on the bottom line of text), for the innermost nested div (id='ccDiv' border color blue).

If you change ccDiv to normal position-ing by pressing the '()' button on the blue line, or change the offsetParent (aaDiv, the upper red line) so that it has style.overflow visible (press the 'visible') button, the offset value (shown on the bottom line, will not be negative.

Comment 27

8 years ago
I tracked the cause of this bug to nsGenericHTMLElement.cpp GetOffsetRect().  In the version of source I was using (3.5), on line 572 there is the following code:

  // Subtract the parent border unless it uses border-box sizing.
  if (parent &&
      parent->GetStylePosition()->mBoxSizing != NS_STYLE_BOX_SIZING_BORDER) {
    const nsStyleBorder* border = parent->GetStyleBorder();
    origin.x -= border->GetActualBorderWidth(NS_SIDE_LEFT);
    origin.y -= border->GetActualBorderWidth(NS_SIDE_TOP);

In a test using the attachment above (403764) pressing the 'Test cc offsetLeft' button invokes GetOffsetLeft which calls GetOffsetRect.  In that code, parent->GetStylePosition()->mBoxSizing evaluates to 0 which causes the body of the 'if' above to execute.  This is what causes the offset to be (incorrectly) negative.

I don't understand what the purpose of this 'if' statement is.
The purpose is to make sure coordinates end up in the right coordinate system (specifically, the same one that "width" is measured in).  But note comment 13 and comment 16.  Is there now a proposed spec that's web-compatible?

In any case, what's happening here is that in GetOffsetRect our primary frame is abs pos.  Its parent is the scrolled frame, whose parent is the scrollframe.  The nonzero border is on the scrollframe.  The scrollframe is relatively positioned.

isPositioned and isAbsolutelyPositioned are both true.  Therefore isOffsetParent ends up false, but this block:

499           if (!isAbsolutelyPositioned && !isOffsetParent) {
500             origin += parent->GetPositionIgnoringScrolling();
501           }

is not executed when |parent| is the scrolled frame.  So we end up not adding its origin (which is precisely offset by the border width).  We continue in the loop with the scrollframe, which is treated as the offsetparent, and we break out of the loop and subtract the border widths, which we never added.

It looks like this code was added in bug 81290.  jst, I bet you don't recall why you added it, right?

It seems to me that no matter what else we do here that code is wrong: the parent of an abs pos node is its containing block in all cases, so if that containing block is offset from the offsetParent primary frame (the positioned one) for some reason we should be including that offset...  As in, we should just remove the above code block.


4 years ago
Component: DOM: Mozilla Extensions → DOM
Product: Core → Core

Comment 29

4 years ago
Removing keyword since the request was answered in comment 7.
Keywords: qawanted
Version 	47.0.1
Build ID 	20160623154057
User Agent 	Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0
User Agent 	Mozilla/5.0 (Windows NT 5.1; rv:50.0) Gecko/20100101 Firefox/50.0
Last Resolved: a year ago
Resolution: --- → WORKSFORME
Michelle, what works for you?  The bug is clearly reproducible using attachment 403764 [details].

Attachment 195268 [details] doesn't show a problem, but it hasn't since comment 25 at least.
Flags: needinfo?(mfunches)
Resolution: WORKSFORME → ---
Summary: child has negative offsetTop/Left when parent has border, overflow:hidden, position:relative → absolutely positioned child has negative offsetTop/Left when parent has border, overflow:hidden, position:relative
Attachment #195268 - Attachment is obsolete: true
Created attachment 8772040 [details]

Attached file for what I tested (offset.html)
Flags: needinfo?(mfunches) → needinfo?(bzbarsky)
Yes, that's the attachment that hasn't shown this problem for 9 years now and that I marked obsolete 3 days ago...  The _other_ testcase on this bug shows the problem just fine.
Flags: needinfo?(bzbarsky)
Attachment #8772040 - Attachment is obsolete: true
You need to log in before you can comment on or make changes to this bug.