Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Javascript > Find Element Position

Reply
Thread Tools

Find Element Position

 
 
Peter Michaux
Guest
Posts: n/a
 
      02-18-2008
On Feb 17, 4:06 pm, dhtml <(E-Mail Removed)> wrote:
> On Feb 17, 2:57 pm, Peter Michaux <(E-Mail Removed)> wrote:> On Feb 17, 2:09 pm, dhtml <(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
 
David Mark
Guest
Posts: n/a
 
      02-18-2008
On Feb 17, 6:57*pm, dhtml <(E-Mail Removed)> wrote:
> On Feb 17, 3:36 pm, David Mark <(E-Mail Removed)> wrote:
>
> > On Feb 17, 5:09 pm, dhtml <(E-Mail Removed)> 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...?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:
> "(E-Mail Removed)" <(E-Mail Removed)>


No time for another forum right now. I will probably just let the w3c
sort it out.

 
Reply With Quote
 
 
 
 
Matt Kruse
Guest
Posts: n/a
 
      02-18-2008
On Feb 17, 5:38 pm, David Mark <(E-Mail Removed)> wrote:
> On Feb 17, 5:43 pm, Matt Kruse <(E-Mail Removed)> 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
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      02-18-2008
On Feb 17, 8:52*pm, Matt Kruse <(E-Mail Removed)> wrote:
> On Feb 17, 5:38 pm, David Mark <(E-Mail Removed)> wrote:
>
> > On Feb 17, 5:43 pm, Matt Kruse <(E-Mail Removed)> 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.
 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      02-18-2008
On Feb 17, 9:39*pm, Jeff <jeff@spam_me_not.com> wrote:
> Matt Kruse wrote:
> > On Feb 17, 5:38 pm, David Mark <(E-Mail Removed)> wrote:
> >> On Feb 17, 5:43 pm, Matt Kruse <(E-Mail Removed)> 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.)
 
Reply With Quote
 
dhtml
Guest
Posts: n/a
 
      02-18-2008
On Feb 17, 5:52 pm, Matt Kruse <(E-Mail Removed)> wrote:
> On Feb 17, 5:38 pm, David Mark <(E-Mail Removed)> wrote:
>
> > On Feb 17, 5:43 pm, Matt Kruse <(E-Mail Removed)> 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


 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      02-18-2008
On Feb 17, 5:09*pm, dhtml <(E-Mail Removed)> 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/tes...on-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_SU BTRACTS_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_SU BTRACTS_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?
 
Reply With Quote
 
dhtml
Guest
Posts: n/a
 
      02-19-2008
On Feb 17, 9:01 pm, David Mark <(E-Mail Removed)> wrote:


On Feb 17, 9:01 pm, David Mark <(E-Mail Removed)> wrote:
> On Feb 17, 5:09 pm, dhtml <(E-Mail Removed)> 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_SU BTRACTS_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.

 
Reply With Quote
 
David Mark
Guest
Posts: n/a
 
      02-19-2008
On Feb 18, 8:05*pm, dhtml <(E-Mail Removed)> wrote:
> On Feb 17, 9:01 pm, David Mark <(E-Mail Removed)> wrote:
>
> On Feb 17, 9:01 pm, David Mark <(E-Mail Removed)> wrote:
>
> > On Feb 17, 5:09 pm, dhtml <(E-Mail Removed)> 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_SU BTRACTS_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.
 
Reply With Quote
 
dhtml
Guest
Posts: n/a
 
      02-19-2008
On Feb 18, 6:51 pm, David Mark <(E-Mail Removed)> wrote:
> On Feb 18, 8:05 pm, dhtml <(E-Mail Removed)> wrote:
>
> > On Feb 17, 9:01 pm, David Mark <(E-Mail Removed)> wrote:

>
> > On Feb 17, 9:01 pm, David Mark <(E-Mail Removed)> wrote:

>
> > > On Feb 17, 5:09 pm, dhtml <(E-Mail Removed)> 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...set-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/tes...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....e40d8a59dcaf23
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...utedStyle.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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Find position of element using binary_search Angus C++ 10 03-11-2011 12:38 AM
Position an element dynamically on top of an existing element Konrad Hammerer HTML 14 02-07-2008 11:34 PM
find position of a element in a table louvino@gmail.com Javascript 4 03-26-2007 08:57 AM
How to find out the position of a element in the XML Document Eshrath Ali Khan XML 1 11-09-2004 07:30 PM
CSS and javascript, find absolute position of element Knut Javascript 1 10-15-2004 02:27 AM



Advertisments