Setting body overflow to "hidden" breaks document.body.clientHeight and clientWidth

RESOLVED FIXED in mozilla1.3beta

Status

()

Core
DOM
P1
major
RESOLVED FIXED
16 years ago
5 years ago

People

(Reporter: J Gibbs, Assigned: jst)

Tracking

({topembed+})

Trunk
mozilla1.3beta
topembed+
Points:
---
Bug Flags:
blocking1.3b -

Firefox Tracking Flags

(Not tracked)

Details

(Whiteboard: [HAVE FIX?], URL)

Attachments

(1 attachment, 1 obsolete attachment)

4.50 KB, patch
Christopher Aillon (sabbatical, not receiving bugmail)
: review+
peterv
: superreview+
Details | Diff | Splinter Review
(Reporter)

Description

16 years ago
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.1) Gecko/20020826
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.1) Gecko/20020826

If in a stylesheet I set the document overflow to "hidden", then in javascript document.body.clientHeight and clientWidth are "0".  Moreover, if the document body is empty, or contains only absolutely positioned blocks, then offsetHeight and offsetWidth are are also "0".  If I remove the overflow line from the stylesheet, then all these properties are their correct values.

This behavior is different from Internet Explorer -- in IE, setting body overflow to hidden just means that clientWidth and clientHeight get slightly larger, as there are no scrollbars displayed.  Additionally, if the document body is empty (ie, there is nothing between the BODY tags), or if overflow is set to hidden and all page content is absolutely positioned, then in IE offsetHeight and offsetWidth are equal to the displayed "whitespace".

Reproducible: Always

Steps to Reproduce:
1.
2.
3.

Comment 1

16 years ago
can you attach a testcase ?
Does it also happen with latest 1.2 ?
http://ftp.mozilla.org/pub/mozilla/nightly/latest-1.2/
(Reporter)

Comment 2

16 years ago
Here's a demonstration page: http://thisoldhaus.com/tests/moz_test/

I'll download 1.2b and try it out...
(Reporter)

Comment 3

16 years ago
Okay, it seems to be even more broken in 1.2b

If I set body overflow=hidden, body padding=0px, and only have absolutely positioned content, then in 1.1 the content is visible, but offsetHeight/Width and clientHeight/Width are all zero.  

In 1.2b all values are still zero, *and* the content is not displayed.

Comment 4

16 years ago
-> Layout (Forms ?)
Assignee: asa → float
Component: Browser-General → Layout: Floats
QA Contact: asa → ian
The content not being displayed is bug 170011.  Please don't discuss that issue
further here.

The rest seems to be the implementation of clientWidth / clientHeight, although
there could be a layout problem.  ->Dom Style.
Component: Layout: Floats → DOM Style
.
Assignee: float → jst
So... I have a few questions.

1)  What should clientHeight/clientWidth in general be for non-positioned elements
    with no overflow set?  (the msdn doc says nothing on the topic)
2)  Perhaps we should be applying our "if it's the body, munge the numbers" hack
    even in cases when the body's primary frame implementes nsIScrollFrame?
Status: UNCONFIRMED → NEW
Component: DOM Style → DOM Mozilla Extensions
Ever confirmed: true
OS: Windows 2000 → All
Hardware: PC → All
(Reporter)

Comment 8

16 years ago
> What should clientHeight/clientWidth in general be for non-positioned 
> elements with no overflow set?  (the msdn doc says nothing on the topic)

Here's the MSDN page illustrating Element Dimension and Location:
http://msdn.microsoft.com/workshop/author/om/measuring.asp

In Internet Explorer, clientHeight/clientWidth and offsetHeight/offsetWidth for 
the document body are actually reflective of the browser window's "page display 
area" (not sure the actual name), rather than the contents of the page.  
offsetHeight/offsetWidth reflect the overall dimensions of the page display 
area, and clientHeight/clientWidth are the dimensions of the display area minus 
the scrollbars and document body border (if present).  (This also means that 
offsetHeight and offsetWidth can never be smaller than clientHeight and 
clientWidth).

An example: in Internet Explorer, if I have a page with body overflow set to 
hidden, then offsetHeight and clientHeight are equal (say, 550px).  offsetWidth 
and clientWidth are also equal (say, 750px).  If I take this same page and 
change body overflow to scroll, then offsetHeight and offsetWidth stay the same 
(550px and 750px), but clientHeight and clientWidth shrink by the height and 
with of the scrollbars (becoming something like 525px and 725px).  In IE this 
behavior (and the numbers) are consistent, no matter if the content is 
absolutely positioned, relatively positioned, or even if there is no page 
content at all.

If you try my test page ( http://thisoldhaus.com/tests/moz_test/ ), and compare 
Mozilla's behavior to that of IE, you'll see there's a big difference between 
the two.
OK.  So IE treats <body> as special in all cases.... (makes sense in IE's
rendering model, and is pretty odd in Mozilla, since you can have stuff
rendering that's not part of the <body>).

Notice that if you set overflow:scroll then the numbers are different, but so is
the _rendering_.  The problem is that we shouldn't be returning those zeroes in
the hidden case, but we _do_ want to return the correct values for <body> (which
will differ from IE's) in the scroll case.  Right?
(Reporter)

Comment 10

16 years ago
> Notice that if you set overflow:scroll then the numbers are 
> different, but so is the _rendering_.  The problem is that 
> we shouldn't be returning those zeroes in the hidden case, 
> but we _do_ want to return the correct values for <body> 
> (which will differ from IE's) in the scroll case.  Right?

I suppose so.  However, it makes no sense to me how, if body overflow is not 
set, that clientHeight is the total available height of the display area, but 
if overflow is set to scroll, then suddenly clientHeight is only the height of 
the <body> minus the scrollbars (which, if the page only consists of a 
paragraph or two, is much less than the total available height).  It doesn't 
seem consistent.

But in any case, as a site developer, I need to be able to find out the 
browser's available display area (minus scrollbars and the like, if displayed), 
and I need these numbers to be consistent regardless of what overflow is set 
to, and regardless of of the page contents.  And even when this bug is fixed, 
it appears I won't always be able to use document.body.clientHeight/clientWidth 
to find these dimensions - correct?  If this is the case, what do I use?
Well... this is why I asked what IE's definition of clientHeight is for a div
that does not have overflow.  That's the situation we have with <body>.  The
heart of the problem is that in IE the <body> is the entire rendering area.  In
Mozilla it is not.  So if you set "overflow:scroll; height: 100px" on the body,
there will be no content outside that 100px tall strip, whereas IE will ignore
the height:100px.

It's a matter of whether clientHeight/clientWidth on the body should be
consistent with all other scrolled containers or consistent with IE...  We can't
have it both ways.
(Reporter)

Comment 12

16 years ago
> Well... this is why I asked what IE's definition of 
> clientHeight is for a div that does not have overflow.  

AFAIK, in IE, if you have a 100px tall <div>, with a border of 5px, and
scrolling content (overflow could be "auto", "scrolling", or not even
specified), then:
    offsetHeight = 100px
    clientHeight = (offsetHeight - border - scrollbar) = ~65px

If you take the same <div> and set overflow:hidden (or if overflow is not set,
and there isn't enough content to scroll), then:
    offsetHeight = 100px
    clientHeight = (offsetHeight - border) = 90px;


> The heart of the problem is that in IE the <body> is 
> the entire rendering area.  In Mozilla it is not.  So 
> if you set "overflow:scroll; height: 100px" on the body, 
> there will be no content outside that 100px tall strip, 
> whereas IE will ignore the height:100px.

> It's a matter of whether clientHeight/clientWidth on the 
> body should be consistent with all other scrolled containers 
> or consistent with IE...  We can't have it both ways.

But even in Mozilla, <body> doesn't consistently behave like other objects.  If
in a stylesheet you have...

    BODY {
        height: 200px;
        width: 200px;
        border: solid 1px black;
        background-color: blue;
    }

...you'll get a 200x200px <body> object with border, but the background color is
set for the *entire* rendering area.  If <body> is treated the same as <div>,
shouldn't the background only apply within the boundaries of the <body> object,
just like border?

In my opinion, IE makes more sense in this regard -- <body> should be an "alias"
for the entire rendering area.  If I set a <body> background, it applies to the
whole area.  If I set BODY { overflow:scroll; }, then scrollbars should appear
for the entire rendering area.  The same for border.  And
document.body.offsetHeight/offsetWidth, document.body.clientHeight/clientWidth,
and document.body.scrollHeight/scrollWidth should tell me information about the
entire rendering area and its contents, so that I can use javascript to position
content within the entire available area.  

What's the rationale for treating <body> as independent of the rendering area? 
And is there any way to retrieve the dimensions of the rendering area, or to
force it to display or hide the scrollbars, independent of the <body> object and
its contents?
That <body> background is painted on the canvas if the <html> background is not
set as a backwards compatibility hack and only in legacy HTML documents (not in
XHTML or any other XML).  See http://www.w3.org/TR/CSS21/colors.html#q2,
paragraph 3 and 4.

> What's the rationale for treating <body> as independent of the rendering area? 

Because the rendering area is the canvas.  Inside that is the root element
(<html>), inside which is the <body>.  As a simple test:

<html>
<head>
<title>The title</title>
<style>
  head, style, title { display: block }
  style { white-space: pre; font-family: monospace }
  head { border: 2px solid green }
  body { border: 2px solid blue }
  html { border: 2px solid red }
</style>
</head>
<body>
There is content outside the body
</body>
</html>

As far as CSS is concerned, content inside <head> is in no way special; it just
happens to be set display:none in the UA stylesheet.

> And is there any way to retrieve the dimensions of the rendering area

For now, getting the computed height and width of document.documentElement will
work (using getComputedStyle).  I would have thought that
clientWidth/clientHeight would work on it as well, but they do not.  That's
buggy, just like us returning 0 for overflow:hidden <body> is buggy....

You can force hiding of the scrollbars by setting overflow:hidden on the <body>
or <html> (sorta hacked in for limited IE compat).  There's no way to force them
to show, to my knowledge.
(Reporter)

Comment 14

16 years ago
> As far as CSS is concerned, content inside <head> 
> is in no way special; it just happens to be set 
> display:none in the UA stylesheet.

:o  Looks like I need to do some reading; I didn't know about that.
 
 
> I would have thought that clientWidth/clientHeight 
> would work on it as well, but they do not.  That's
> buggy, just like us returning 0 for overflow:hidden 
> <body> is buggy....

Yes -- if I now understand correctly for Mozilla, if Body Overflow is set to 
Hidden, clientWidth/clientHeight should only be zero if:
  a) the <body> has no border and contains no content, or 
  b) the <body> has no border, and all it's content is absolutely 
     positioned, and thus "magically" becomes independent of the body object.

Also, clientWidth/clientHeight are currently incorrect if Body Overflow is not 
specified -- they should be equal or less than offsetWidth/offsetHeight, but 
instead are equal to the canvas width and height.  This is part of what 
confused me -- I had assumed that clientWidth/clientHeight for the <body> 
represented the same things in Mozilla as in IE -- in this one case they do, 
but it's a bug, and should be fixed so as to be consistent with Mozilla's 
rendering model.

Updated

16 years ago
Keywords: mozilla1.3

Comment 15

16 years ago
*** Bug 184976 has been marked as a duplicate of this bug. ***

Comment 16

16 years ago
Adding URL from dupe, this bug brakes the page.
Severity: normal → major

Comment 17

16 years ago
FYI because this is such a huge bug in breaking SBC signup (and SBC broadband
users may be using Netscape 7), we are going try to get them to implement a
workaround.

I have added topembed, nsbeta1 anyway and ? for blocking 1.3b.
Flags: blocking1.3b?
Keywords: nsbeta1, topembed

Comment 18

16 years ago
Re: comment #8 and #9:
Do not trust too much the image describing the MSIE DHMTL object model for
several reasons.
1- They changed the image while working on MSIE 6 beta 1 but they never actually
use MSIE 6 in standards compliant rendering mode to do the new, latest image. I
can prove that.

2- There is a contradiction between the applied box model (conforming to the
CSS1 box model) and the image gif 
http://msdn.microsoft.com/workshop/graphics/dhtmlpos.gif
coming from
http://msdn.microsoft.com/workshop/author/om/measuring.asp
and this image
http://msdn.microsoft.com/workshop/graphics/boxdim.gif
found at
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnie60/html/cssenhancements.asp

as the div's top value is within the margin when the image show it should be
outside. Blatant contradiction here that anyone, everyone can see.

3- When I started my javascript section, I saved this image:
http://www.geocities.com/Area51/Realm/8655/GRAPHICS/GIF/MSIEdhtmlpos.gif
just in case the MSDN page would change or the image disappear. Several months
later, they changed the image to this
http://www.geocities.com/Area51/Realm/8655/GRAPHICS/GIF/MSIEdhtmlposNew.gif

4- I even tried to construct in standards compliant rendering mode the
MSIEdhtmlposNew.gif while applying the unique idiosyncratic logic in their image
and I never could. They just updated the image with a change on the clientLeft,
clientTop values and that's it. They never updated their DHTML object model
schema according to their claimed full compliance with CSS1 box model and
according to their "When you use the !DOCTYPE declaration to specify
standards-compliant mode, this element represents the canvas葉he entire surface
onto which a document's contents can be rendered."

For many reasons, the DHMTL object model gif image at MSDN should not be trusted
blindly.
Despite all this, I really think people should vote for bug 156388 
Thanks :)

Comment 19

16 years ago
Re: comment #12:
"documentElement of type Element, readonly
    This is a convenience attribute that allows direct access to the child node
that is the root element of the document. For HTML documents, this is the
element with the tagName 'HTML'."
read at
http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-87CD092

MSIE 6 for Windows is compliant with DOM level 1 Core and DOM level 2 Core.
Mozilla is still not compliant.


Re: comment #13:
>> And is there any way to retrieve the dimensions of the rendering area
>For now, getting the computed height and width of document.documentElement will
work (using getComputedStyle).  I would have thought that
clientWidth/clientHeight would work on it as well, but they do not.

That's because bug 156388 has not been fixed yet. The dimensions of the
rendering area are easy to get as soon as you fix bug 156388. After 3 months of
waiting, I personally insisted that Mr. Zbarsky confirm bug 156388. Also after
reading that
"the _correct_ width of the rendering area in Mozilla can be gotten by looking
at document.body.clientWidth just like in IE."
http://bugzilla.mozilla.org/show_bug.cgi?id=48634#c52

MSIE 6 has 2 rendering modes, therefore 2 answers to how to get the dimensions
of the browser viewport. 
Everywhere in this bugfile and in other bugs, if you do not use a doctype
declaration with a full url in the system id part, then you trigger the browser
into backward compatible rendering mode (document.compatMode == "BackCompat")
where body is the element representing the canvas of the document.
Canvas according to W3C: "the space where the formatting structure is rendered;
the canvas is infinite for each dimension of the space." The canvas is the
entire "surface" on which the document is laid out.

Most of comment 19 (especially the first part, about documentElement) is 
factually incorrect.  Please stop spamming bugs with such comments.

Comment 21

16 years ago
http://thisoldhaus.com/tests/moz_test/ 

case:
Content Position: absolute
Body Overflow: hidden

Expected Results: 
offsetHeight : 0
clientHeight : 0

Actual Results:
offsetHeight : 0
clientHeight : 0


If I set BODY { overflow:scroll; }, then scrollbars should appear
for the entire rendering area.

No, the body is a block level element in normal flow. HTML{overflow:auto;} is
the currently broken feature your seeking. 

comment 10 
>It doesn't seem consistent.

It *is* inconsistent. IE is trying to keep this property for historical reasons.
Mozilla does not need to do this. We have window.innerHeight/Width. I'd like
window.hScrollbarSize and window.vScrollbarSize. This would make more sense than
incosistantly using body.offsetHeight/Width.


comment 13 
>> And is there any way to retrieve the dimensions of the rendering area

>For now, getting the computed height and width of document.documentElement will
>work (using getComputedStyle)

That should give the height of the documentElement, not the viewport. For the
viewport, you need window.innerHeight - (isVScrollbarPresent ? 15 : 0);

Detecting isVScrollbarPresent is not a simple task. Ideally, Mozilla will expose
this property to scripting environments (JavaScript).

javascript:alert(document.defaultView.getComputedStyle(document.documentElement,"").getPropertyValue("height"))
javascript:alert(window.innerHeight)

>It's a matter of whether clientHeight/clientWidth on the body should be
>consistent with all other scrolled containers or consistent with IE...  We
>can't have it both ways.


Be consistent with the property, not Internet Explorer's incorrect
implementation of the property. This is a fundamental problem with Internet
Explorer. It should not be copied.

Comment 22

16 years ago
>Detecting isVScrollbarPresent is not a simple task. Ideally, Mozilla will expose
this property to scripting environments (JavaScript).

If the html element does not have a margin (and that is easy to verify and to
measure), then a simple comparison will indicate, reveal if the html element
uses scrollbars. The code and everything is already in attachment 91255 [details] (bug
140308). Of course, this is assuming that other css property values are set to
default values like overflow set to visible or auto, height set to auto. If it's
set to scroll, then that's easy to know.

There is also another indirect way to verify this. If the scrollHeight value of
an element is greater than its offsetHeight + 15px, then you know that there is
an vertical scrollbar: that's absolutely necessary by definition of the DHTML
object model, and this, in both rendering modes. 
If the scrollWidth value of an element is greater than its offsetWidth + 15px,
then you know that there is an horizontal scrollbar.
Of course, all this is assuming Mozilla implements the DHTML object model and
its properties for the document root element.

----

Mozilla does not implement clientWidth, clientHeight, scrollLeft, scrollTop,
clientLeft and clientTop for the document root element, for documentElement.
MSIE 6 for Windows does accordingly in standards compliant rendering mode. This
is what my previous comment should have said. I'm sure Boris understands :);
anyway, a lot of people already have in bug 62536, bug 111207, in this bug
notwithstanding bug 156388.

Updated

16 years ago
Keywords: topembed → topembed+
Target Milestone: --- → mozilla1.3beta

Updated

16 years ago
Flags: blocking1.3b? → blocking1.3b-

Comment 23

16 years ago
Bug 169677 [ document.body.clientHeight = 0 if no scrollbars=1 passed to
window.open() ], related or even a dupe? 
(Assignee)

Comment 24

16 years ago
*** Bug 169677 has been marked as a duplicate of this bug. ***
(Assignee)

Comment 25

16 years ago
Created attachment 111662 [details] [diff] [review]
Proposed fix.

This makes body.client* be the size of the body frame even if there's no
scrollbars (or possibility for scrollbars) in the body frame. Thoughts?
(Assignee)

Comment 26

16 years ago
Ignore the bogus param descriptions in the comment above the declaration of
GetClientAreaSize() in nsGenericHTMLElement.h.
Status: NEW → ASSIGNED
Priority: -- → P1
Whiteboard: [HAVE FIX?]
(Assignee)

Updated

16 years ago
Attachment #111662 - Flags: superreview?(peterv)
Attachment #111662 - Flags: review?(bzbarsky)
Comment on attachment 111662 [details] [diff] [review]
Proposed fix.

>Index: content/html/content/src/nsGenericHTMLElement.cpp
>===================================================================

>+  if (border) {
>+    nsStyleCoord coord;
>+
>+    if (eStyleUnit_Coord == border->mBorder.GetTopUnit()) {
>+      size.height -= border->mBorder.GetTop(coord).GetCoordValue();
>+    }
>+    if (eStyleUnit_Coord == border->mBorder.GetTopUnit()) {
>+      size.height -= border->mBorder.GetTop(coord).GetCoordValue();
>+    }

Shouldn't this be GetBottomUnit and GetBottom? Do we want to comment the code
about special-casing body?
Attachment #111662 - Flags: superreview?(peterv) → superreview+
Comment on attachment 111662 [details] [diff] [review]
Proposed fix.

>+  if (border) {
>+    nsStyleCoord coord;
>+
>+    if (eStyleUnit_Coord == border->mBorder.GetTopUnit()) {
>+      size.height -= border->mBorder.GetTop(coord).GetCoordValue();
>+    }
>+    if (eStyleUnit_Coord == border->mBorder.GetTopUnit()) {
>+      size.height -= border->mBorder.GetTop(coord).GetCoordValue();
>+    }
>+
>+    if (eStyleUnit_Coord == border->mBorder.GetLeftUnit()) {
>+      size.width -= border->mBorder.GetLeft(coord).GetCoordValue();
>+    }
>+    if (eStyleUnit_Coord == border->mBorder.GetRightUnit()) {
>+      size.width -= border->mBorder.GetRight(coord).GetCoordValue();
>+    }
>+  }


Since you don't care about the origin (x and y values) you can just do the
following (you might want to call |nsStyleBorder::CalcBorderFor(const
nsIFrame*, nsMargin&)| first, if for some reason the border is not cached):

  if (border) {
    nsMargin theBorder;
    if (border->GetBorder(theBorder)) {
      size.Deflate(theBorder);
    }
  }


Or, keep your existing code and use nsSize as the return value for
GetClientAreaSize().  But if you do, fix what peterv said.
(Assignee)

Comment 29

16 years ago
Created attachment 112208 [details] [diff] [review]
Better fix.
(Assignee)

Updated

16 years ago
Attachment #111662 - Attachment is obsolete: true
(Assignee)

Updated

16 years ago
Attachment #112208 - Flags: superreview?(peterv)
Attachment #112208 - Flags: review?(caillon)
Comment on attachment 112208 [details] [diff] [review]
Better fix.

Nice.  less code is always good.  :-)
Attachment #112208 - Flags: review?(caillon) → review+
Attachment #112208 - Flags: superreview?(peterv) → superreview+
(Assignee)

Comment 31

16 years ago
Fix checked in last night, marking FIXED. Please re-open if you disagree with
how this works in Mozilla now.
Status: ASSIGNED → RESOLVED
Last Resolved: 16 years ago
Resolution: --- → FIXED
Component: DOM: Mozilla Extensions → DOM
Product: Core → Core
You need to log in before you can comment on or make changes to this bug.