X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Foffset.js;h=0a8b9e64faa100b9441619f3353e2cf04aa47a7e;hb=991d039b62f5dfcb9e3d99fe28212a6874e8f5c7;hp=e74556cd28b74b30711ad11059807f3e3d9496c5;hpb=e317bb7105c1288a6fbce03725013daebf095d25;p=jquery.git diff --git a/src/offset.js b/src/offset.js index e74556c..0a8b9e6 100644 --- a/src/offset.js +++ b/src/offset.js @@ -1,102 +1,166 @@ -// The Offset Method -// Originally By Brandon Aaron, part of the Dimension Plugin -// http://jquery.com/plugins/project/dimensions -jQuery.fn.offset = function() { - var left = 0, top = 0, elem = this[0], results; - - if ( elem ) with ( jQuery.browser ) { - var parent = elem.parentNode, - offsetChild = elem, - offsetParent = elem.offsetParent, - doc = elem.ownerDocument, - safari2 = safari && parseInt(version) < 522, - fixed = jQuery.css(elem, "position") == "fixed"; - - // Use getBoundingClientRect if available - if ( elem.getBoundingClientRect ) { - var box = elem.getBoundingClientRect(); - - // Add the document scroll offsets - add( - box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft), - box.top + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop) - ); - - // IE adds the HTML element's border, by default it is medium which is 2px - // IE 6 and 7 quirks mode the border width is overwritable by the following css html { border: 0; } - // IE 7 standards mode, the border is always 2px - // This border/offset is typically represented by the clientLeft and clientTop properties - // However, in IE6 and 7 quirks mode the clientLeft and clientTop properties are not updated when overwriting it via CSS - // Therefore this method will be off by 2px in IE while in quirksmode - add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop ); - - // Otherwise loop through the offsetParents and parentNodes - } else { - - // Initial element offsets - add( elem.offsetLeft, elem.offsetTop ); - - // Get parent offsets - while ( offsetParent ) { - // Add offsetParent offsets - add( offsetParent.offsetLeft, offsetParent.offsetTop ); - - // Mozilla and Safari > 2 does not include the border on offset parents - // However Mozilla adds the border for table or table cells - if ( mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2 ) - border( offsetParent ); - - // Add the document scroll offsets if position is fixed on any offsetParent - if ( !fixed && jQuery.css(offsetParent, "position") == "fixed" ) - fixed = true; - - // Set offsetChild to previous offsetParent unless it is the body element - offsetChild = /^body$/i.test(offsetParent.tagName) ? offsetChild : offsetParent; - // Get next offsetParent - offsetParent = offsetParent.offsetParent; - } - - // Get parent scroll offsets - while ( parent.tagName && !/^body|html$/i.test(parent.tagName) ) { - // Remove parent scroll UNLESS that parent is inline or a table-row to work around Opera inline/table scrollLeft/Top bug - if ( !/^inline|table-row.*$/i.test(jQuery.css(parent, "display")) ) - // Subtract parent scroll offsets - add( -parent.scrollLeft, -parent.scrollTop ); - - // Mozilla does not add the border for a parent that has overflow != visible - if ( mozilla && jQuery.css(parent, "overflow") != "visible" ) - border( parent ); - - // Get next parent - parent = parent.parentNode; +if ( "getBoundingClientRect" in document.documentElement ) + jQuery.fn.offset = function() { + var elem = this[0]; + if ( !elem || !elem.ownerDocument ) return null; + if ( elem === elem.ownerDocument.body ) return jQuery.offset.bodyOffset( elem ); + var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement, + clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, + top = box.top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop, + left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft; + return { top: top, left: left }; + }; +else + jQuery.fn.offset = function() { + var elem = this[0]; + if ( !elem || !elem.ownerDocument ) return null; + if ( elem === elem.ownerDocument.body ) return jQuery.offset.bodyOffset( elem ); + jQuery.offset.initialize(); + + var offsetParent = elem.offsetParent, prevOffsetParent = elem, + doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement, + body = doc.body, defaultView = doc.defaultView, + prevComputedStyle = defaultView.getComputedStyle(elem, null), + top = elem.offsetTop, left = elem.offsetLeft; + + while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) { + if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) break; + computedStyle = defaultView.getComputedStyle(elem, null); + top -= elem.scrollTop, left -= elem.scrollLeft; + if ( elem === offsetParent ) { + top += elem.offsetTop, left += elem.offsetLeft; + if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) ) + top += parseFloat( computedStyle.borderTopWidth ) || 0, + left += parseFloat( computedStyle.borderLeftWidth ) || 0; + prevOffsetParent = offsetParent, offsetParent = elem.offsetParent; } - - // Safari <= 2 doubles body offsets with a fixed position element/offsetParent or absolutely positioned offsetChild - // Mozilla doubles body offsets with a non-absolutely positioned offsetChild - if ( (safari2 && (fixed || jQuery.css(offsetChild, "position") == "absolute")) || - (mozilla && jQuery.css(offsetChild, "position") != "absoltue") ) - add( -doc.body.offsetLeft, -doc.body.offsetTop ); - - // Add the document scroll offsets if position is fixed - if ( fixed ) - add( - Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft), - Math.max(doc.documentElement.scrollTop, doc.body.scrollTop) - ); + if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) + top += parseFloat( computedStyle.borderTopWidth ) || 0, + left += parseFloat( computedStyle.borderLeftWidth ) || 0; + prevComputedStyle = computedStyle; } - // Return an object with top and left properties - results = { top: top, left: left }; - } + if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) + top += body.offsetTop, + left += body.offsetLeft; - return results; + if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) + top += Math.max( docElem.scrollTop, body.scrollTop ), + left += Math.max( docElem.scrollLeft, body.scrollLeft ); - function border(elem) { - add( jQuery.css(elem, "borderLeftWidth"), jQuery.css(elem, "borderTopWidth") ); - } + return { top: top, left: left }; + }; + +jQuery.offset = { + initialize: function() { + var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, 'marginTop', true) ) || 0, + html = '
'; + + jQuery.extend( container.style, { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' } ); + + container.innerHTML = html; + body.insertBefore( container, body.firstChild ); + innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild; + + this.doesNotAddBorder = (checkDiv.offsetTop !== 5); + this.doesAddBorderForTableAndCells = (td.offsetTop === 5); - function add(l, t) { - left += parseInt(l) || 0; - top += parseInt(t) || 0; + checkDiv.style.position = 'fixed', checkDiv.style.top = '20px'; + this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15); // safari subtracts parent border width here which is 5px + checkDiv.style.position = '', checkDiv.style.top = ''; + + innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative'; + this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5); + + this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop); + + body.removeChild( container ); + jQuery.offset.initialize = function(){}; + + body = container = innerDiv = checkDiv = table = td = null; + }, + + bodyOffset: function(body) { + jQuery.offset.initialize(); + var top = body.offsetTop, left = body.offsetLeft; + if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) + top += parseFloat( jQuery.curCSS(body, 'marginTop', true) ) || 0, + left += parseFloat( jQuery.curCSS(body, 'marginLeft', true) ) || 0; + return { top: top, left: left }; } }; + + +jQuery.fn.extend({ + position: function() { + if ( !this[0] ) return null; + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.curCSS(elem, 'marginTop', true) ) || 0; + offset.left -= parseFloat( jQuery.curCSS(elem, 'marginLeft', true) ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.curCSS(offsetParent[0], 'borderTopWidth', true) ) || 0; + parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], 'borderLeftWidth', true) ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }, + + offsetParent: function() { + var offsetParent = this[0].offsetParent || document.body; + while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') === 'static') ) + offsetParent = offsetParent.offsetParent; + return jQuery( offsetParent ); + } +}); + + +// Create scrollLeft and scrollTop methods +jQuery.each( ['Left', 'Top'], function(i, name) { + var method = 'scroll' + name; + + jQuery.fn[ method ] = function(val) { + if ( !this[0] ) return null; + + var elem = this[0], win = ("scrollTo" in elem && elem.document) ? elem : + (elem.nodeName === "#document") ? elem.defaultView || elem.parentWindow : + false; + + return val !== undefined ? + + // Set the scroll offset + this.each(function() { + win = ("scrollTo" in this && this.document) ? this : + (this.nodeName === "#document") ? this.defaultView || this.parentWindow : + false; + + win ? + win.scrollTo( + !i ? val : jQuery(win).scrollLeft(), + i ? val : jQuery(win).scrollTop() + ) : + this[ method ] = val; + }) : + + // Return the scroll offset + win ? + win[ i ? 'pageYOffset' : 'pageXOffset' ] || + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + }; +});