Skip to content

Instantly share code, notes, and snippets.

@mc0

mc0/offset.js Secret

Created August 5, 2009 22:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mc0/03a0ac3456e2399ea3dd to your computer and use it in GitHub Desktop.
Save mc0/03a0ac3456e2399ea3dd to your computer and use it in GitHub Desktop.
/* WIP */
var Position = {
tests: {},
basicOffsetTests = function() {
var result = Position.tests;
result.CLIENT_TOP_SUPPORTED = 'clientTop' in Fuse._docEl;
result.TABLE_TAG = return /^h/.test(Fuse._docEl.tagName) ? "table" : "TABLE";
Position.basicOffsetTests = emptyFunction;
},
offsetTests = function offsetTests(req) {
var body = Fuse._body,
result = Position.tests;
result.ran = true;
if (!body) return;
var marginTop = "marginTop", position = "position", padding = "padding",
stat = "static",
border = "border", s = body.style,
bv = '1px solid transparent',
z = "0",
one = "1px",
offsetTop = "offsetTop",
ds = Fuse._docEl.style,
x = Fuse._doc.createElement('div'),
xs = x.style,
table = Fuse._doc.createElement(result.TABLE_TAG),
bodyBackup = {padding: s.padding, marginTop: s.marginTop,
'top': s.top, border: s.border, position: s.position},
docBackup = {padding: ds.padding, marginTop: ds.marginTop,
position: ds.position};
table.cellSpacing = table.cellPadding = xs.margin =
s[padding] = s[marginTop] = s.top = z;
s[border] = bv;
ds.position = stat;
// Table test (relies on td not defaulting to 7px)
table.innerHTML = "<tbody><tr><td>x</td></tr></tbody>";
table.style[border] = "7px solid";
body.insertBefore(table, body.firstChild);
result.IS_TABLE_BORDER_INCLUDED_IN_TD_OFFSET =
table.getElementsByTagName("td")[0].offsetLeft === 7;
body.removeChild(table);
// Relies on marginTop of 0
s.top = one;
result.IS_BODY_TOP_INHERITED = x[offsetTop] === 1;
// Now add margin to determine if body offsetTop is inherited.
s.top = z;
s[marginTop] = one;
s[position] = relative;
result.IS_BODY_MARGIN_INHERITED = (x[offsetTop] === 1);
result.IS_BODY_OFFSET_EXCLUDING_MARGIN = body[offsetTop] === 0;
result.IS_BODY_OFFSET_IGNORED_WHEN_BODY_RELATIVE_AND_LAST_CHILD_POSITIONED = x[offsetTop] === 0;
//IS_BODY_OFFSET_TOP_NO_OFFSETPARENT = body.offsetTop && !body.offsetParent;
xs[position] = "absolute";
s[position] = stat;
if(x.offsetParent === body) {
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 EL's offsetLeft/Top.
// Mozilla is completely wrong.
result.IS_STATIC_BODY_OFFSET_PARENT_BUT_ABSOLUTE_CHILD_SUBTRACTS_BODY_BORDER_WIDTH = x[offsetTop] === 1;
s[border] = z;
xs[position] = relative;
ds[padding] = one;
s[marginTop] = z;
result.IS_CONTAINER_BODY_STATIC_INCLUDING_HTML_PADDING = x[offsetTop] === 3;
// Opera does not respect position: relative on BODY.
s[position] = relative;
result.IS_CONTAINER_BODY_RELATIVE_INCLUDING_HTML_PADDING_REL_CHILD = x[offsetTop] === 3;
xs[position] = "absolute";
result.IS_CONTAINER_BODY_RELATIVE_INCLUDING_HTML_PADDING_ABS_CHILD = x[offsetTop] === 3;
// Opera inherits HTML margin when body is relative and child is relative or absolute.
ds[padding] = z;
ds[marginTop] = one;
result.IS_CONTAINER_BODY_INCLUDING_HTML_MARGIN = x[offsetTop] === 3;
}
// xs.position = "fixed";
// FIXED_HAS_OFFSETPARENT = x.offsetParent != null;
body.removeChild(x);
Fuse.Element.Methods(body).setStyle(bodyBackup);
Fuse.Element.Methods(ds).setStyle(docBackup);
}
};
Position.toFloat = function toFloat(val) { return parseFloat(val) || 0 };
if (Feature('ELEMENT_BOUNDING_CLIENT_RECT')) {
Position.getOffsets = function getOffsets(el, container) {
if (!el || !el.ownerDocument) { return null }
Position.basicOffsetTests();
var toFloat = Position.toFloat
doc = el.ownerDocument,
docEl = doc.documentElement,
body = doc.body,
tests = Position.tests,
box = el.getBoundingClientRect(),
rootEl = Bug('BODY_ACTING_AS_ROOT') ? body : docEl,
clientTop = rootEl.clientTop || 0,
clientLeft = rootEl.clientLeft || 0,
x = box.left + max(docEl.scrollLeft, body.scrollLeft),
y = box.top + max(docEl.scrollTop, body.scrollTop),
bodyCurStyle;
container = container || doc;
if (tests.CLIENT_TOP_SUPPORTED) {
x -= clientTop;
y -= clientLeft;
}
if (container !== doc) {
box = Position.getOffsets(container, null);
x -= box.x;
y -= box.y;
if (Bug('BODY_ACTING_AS_ROOT') &&
container === body &&
tests.CLIENT_TOP_SUPPORTED) {
x -= borderLeft;
y -= borderTop;
}
else if (Bug('BODY_ACTING_AS_ROOT') &&
Feature('ELEMENT_CURRENT_STYLE') &&
container !== body) {
bodyCurStyle = body.currentStyle;
x += toFloat(bodyCurStyle.marginLeft) + toFloat(bodyCurStyle.left);
y += toFloat(bodyCurStyle.marginTop) + toFloat(bodyCurStyle.top);
}
}
return {left: x, top: y};
};
}
else {
Position.getOffsets = function getOffsets(el, container, coords) {
if (!el || !el.ownerDocument) { return null }
Position.offsetTests();
var toFloat = Position.toFloat
doc = el.ownerDocument,
docEl = doc.documentElement,
body = doc.body,
tests = Position.tests;
container = container || doc;
if (!coords) coords = {};
else coords = {left: toFloat(coords.left), top: toFloat(coords.left)};
if (el === container)
return (coords.x = coords.y = 0) && coords;
// Crawling up the tree.
if (Feature('ELEMENT_COMPUTED_STYLE')) {
var offsetLeft = el.offsetLeft,
offsetTop = el.offsetTop,
defaultView = doc.defaultView,
cs = defaultView.getComputedStyle(el, '');
if (cs.position == "fixed") {
coords.x = offsetLeft + documentElement.scrollLeft;
coords.y = offsetTop + documentElement.scrollTop;
return coords;
}
var bcs = defaultView.getComputedStyle(body,''),
isBodyStatic = !positionedExp.test(bcs.position),
lastOffsetParent = el,
parent = el.parentNode,
offsetParent = el.offsetParent;
// Main loop -----------------------------------------------------------------------
// Loop up, gathering scroll offsets on parentNodes.
// when we get to a parent that's an offsetParent, update
// the current offsetParent marker.
for( ; parent && parent !== container; parent = parent.parentNode) {
if (parent !== body && parent !== documentElement) {
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.
// Include parent border widths. This matches behavior of clientRect approach.
// XXX Opera <= 9.2 includes parent border widths.
// See Bug('BODY_OFFSETS_INHERIT_ITS_MARGINS') below.
if ( !Bug('BODY_OFFSETS_INHERIT_ITS_MARGINS') &&
! (parent.tagName === tests.TABLE_TAG && tests.IS_TABLE_BORDER_INCLUDED_IN_TD_OFFSET)) {
var pcs = defaultView.getComputedStyle(parent, "");
// Mozilla doesn't support clientTop. Add borderWidth to the sum.
offsetLeft += parseFloat(pcs[borderLeftWidth]) || 0;
offsetTop += parseFloat(pcs[borderTopWidth]) || 0;
}
if (parent !== body) {
offsetLeft += offsetParent.offsetLeft;
offsetTop += offsetParent.offsetTop;
lastOffsetParent = offsetParent;
offsetParent = parent.offsetParent; // next marker to check for offsetParent.
}
}
}
}
//--------Post - loop, body adjustments----------------------------------------------
// Complications due to CSSOM Views - the browsers try to implement a contradictory
// spec: http://www.w3.org/TR/cssom-view/#offset-attributes
// XXX Mozilla, Safari 3, Opera: body margin is never
// included in body offsetLeft/offsetTop.
// This is wrong. Body's offsetTop should work like any other element.
// In Safari 2.0.4, BODY can have offsetParent, and even
// if it doesn't, it can still have offsetTop.
// But Safari 2.0.4 doubles offsetTop for relatively positioned elements
// and this script does not account for that.
// XXX Mozilla: When body has a border, body's offsetTop === negative borderWidth;
// Don't use body.offsetTop.
var bodyOffsetLeft = 0,
bodyOffsetTop = 0,
isLastElementAbsolute,
isLastOffsetElementPositioned,
isContainerDocOrDocEl = container === doc || container === documentElement,
dcs,
lastOffsetPosition;
// If the lastOffsetParent is document,
// it is not positioned (and hence, not absolute).
if (lastOffsetParent != doc) {
lastOffsetPosition = defaultView.getComputedStyle(lastOffsetParent,'').position;
isLastElementAbsolute = absoluteExp.test(lastOffsetPosition);
isLastOffsetElementPositioned = isLastElementAbsolute ||
positionedExp.test(lastOffsetPosition);
}
// do we need to add margin?
if (
(lastOffsetParent === el && el.offsetParent === body && !tests.IS_BODY_MARGIN_INHERITED
&& container !== body && !(isBodyStatic && tests.IS_BODY_OFFSET_EXCLUDING_MARGIN))
|| (tests.IS_BODY_MARGIN_INHERITED && lastOffsetParent === el && !isLastOffsetElementPositioned)
|| !isBodyStatic
&& isLastOffsetElementPositioned
&& tests.IS_BODY_OFFSET_IGNORED_WHEN_BODY_RELATIVE_AND_LAST_CHILD_POSITIONED
&& isContainerDocOrDocEl) {
bodyOffsetTop += parseFloat(bcs.marginTop) || 0;
bodyOffsetLeft += parseFloat(bcs.marginLeft) || 0;
}
// Case for padding on documentElement.
if (container === body) {
dcs = defaultView.getComputedStyle(documentElement,'');
if (
(!isBodyStatic &&
((tests.IS_CONTAINER_BODY_RELATIVE_INCLUDING_HTML_PADDING_REL_CHILD && !isLastElementAbsolute)
||
(tests.IS_CONTAINER_BODY_RELATIVE_INCLUDING_HTML_PADDING_ABS_CHILD && isLastElementAbsolute))
)
|| isBodyStatic && tests.IS_CONTAINER_BODY_STATIC_INCLUDING_HTML_PADDING
) {
bodyOffsetTop -= parseFloat(dcs.paddingTop) || 0;
bodyOffsetLeft -= parseFloat(dcs.paddingLeft) || 0;
}
if (tests.IS_CONTAINER_BODY_INCLUDING_HTML_MARGIN){
if (!isLastOffsetElementPositioned
|| isLastOffsetElementPositioned && !isBodyStatic)
bodyOffsetTop -= parseFloat(dcs.marginTop) || 0;
bodyOffsetLeft -= parseFloat(dcs.marginLeft) || 0;
}
}
if (isBodyStatic) {
// XXX Safari subtracts border width of body from element's offsetTop (opera does it, too)
if (tests.IS_STATIC_BODY_OFFSET_PARENT_BUT_ABSOLUTE_CHILD_SUBTRACTS_BODY_BORDER_WIDTH
// 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).
|| (!isLastElementAbsolute && !Bug('BODY_OFFSETS_INHERIT_ITS_MARGINS')
&& isContainerDocOrDocEl)) {
bodyOffsetTop += parseFloat(bcs[borderTopWidth]);
bodyOffsetLeft += parseFloat(bcs[borderLeftWidth]);
}
}
// body is positioned, and if it excludes margin,
// it's probably partly using the AVK-CSSOM disaster.
else if (tests.IS_BODY_OFFSET_EXCLUDING_MARGIN) {
if (isContainerDocOrDocEl) {
if (!tests.IS_BODY_TOP_INHERITED) {
// If the body is positioned, add its left and top value.
bodyOffsetTop += parseFloat(bcs.top) || 0;
bodyOffsetLeft += parseFloat(bcs.left) || 0;
// XXX: Opera normally include the parentBorder in offsetTop.
// We have a preventative measure in the loop above.
if (isLastElementAbsolute && Bug('BODY_OFFSETS_INHERIT_ITS_MARGINS')) {
bodyOffsetTop += parseFloat(bcs[borderTopWidth]);
bodyOffsetLeft += parseFloat(bcs[borderLeftWidth]);
}
}
// Padding on documentElement is not included,
// but in this case, we're searching to documentElement, so we
// have to add it back in.
if (container === doc && !isBodyStatic
&& !tests.IS_CONTAINER_BODY_RELATIVE_INCLUDING_HTML_PADDING_REL_CHILD) {
if (!dcs) dcs = defaultView.getComputedStyle(documentElement,'');
bodyOffsetTop += parseFloat(dcs.paddingTop) || 0;
bodyOffsetLeft += parseFloat(dcs.paddingLeft) || 0;
}
}
else if (tests.IS_BODY_TOP_INHERITED) {
bodyOffsetTop -= parseFloat(bcs.top);
bodyOffsetLeft -= parseFloat(bcs.left);
}
if (tests.IS_BODY_MARGIN_INHERITED && (!isLastOffsetElementPositioned || container === body)) {
bodyOffsetTop -= parseFloat(bcs.marginTop) || 0;
bodyOffsetLeft -= parseFloat(bcs.marginLeft) || 0;
}
}
coords.x = round(offsetLeft + bodyOffsetLeft);
coords.y = round(offsetTop + bodyOffsetTop);
return coords;
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment