From 425272aea1fb30b734bac25b8f74dbe85a3ce215 Mon Sep 17 00:00:00 2001 From: "Merrifield, Jay" Date: Wed, 17 Oct 2012 13:40:52 -0400 Subject: [PATCH] Fixes #12749, correctly detect position() for position:fixed elements, closes gh-991 --- src/offset.js | 42 ++++++++++++++++------------ test/unit/offset.js | 68 ++++++++++++++++++++++++--------------------- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/src/offset.js b/src/offset.js index 381a42da2..6a2fdc993 100644 --- a/src/offset.js +++ b/src/offset.js @@ -107,33 +107,39 @@ jQuery.offset = { jQuery.fn.extend({ position: function() { - if ( !this[0] ) { + if ( !this[ 0 ] ) { return; } - var elem = this[0], + var offsetParent, offset, + parentOffset = { top: 0, left: 0 }, + elem = this[ 0 ]; - // Get *real* offsetParent - offsetParent = this.offsetParent(), + // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent + if ( jQuery.css( elem, "position" ) === "fixed" ) { + // we assume that getBoundingClientRect is available when computed position is fixed + offset = elem.getBoundingClientRect(); + } else { + // Get *real* offsetParent + offsetParent = this.offsetParent(); + + // Get correct offsets + offset = this.offset(); + if ( !rroot.test( offsetParent[ 0 ].nodeName ) ) { + parentOffset = offsetParent.offset(); + } - // Get correct offsets - offset = this.offset(), - parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css( offsetParent[ 0 ], "borderTopWidth" ) ) || 0; + parentOffset.left += parseFloat( jQuery.css( offsetParent[ 0 ], "borderLeftWidth" ) ) || 0; + } - // Subtract element margins + // Subtract parent offsets and 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.css(elem, "marginTop") ) || 0; - offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; - - // Add offsetParent borders - parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; - parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; - - // Subtract the two offsets return { - top: offset.top - parentOffset.top, - left: offset.left - parentOffset.left + top: offset.top - parentOffset.top - ( parseFloat( jQuery.css( elem, "marginTop" ) ) || 0 ), + left: offset.left - parentOffset.left - ( parseFloat( jQuery.css( elem, "marginLeft" ) ) || 0 ) }; }, diff --git a/test/unit/offset.js b/test/unit/offset.js index 7a1d719f0..b1cb0a3f5 100644 --- a/test/unit/offset.js +++ b/test/unit/offset.js @@ -1,6 +1,27 @@ if ( jQuery.fn.offset ) { -module("offset", { teardown: moduleTeardown }); +module("offset", { setup: function(){ + // force a scroll value on the main window + // this insures that the results will be wrong + // if the offset method is using the scroll offset + // of the parent window + var forceScroll = jQuery("
").css({ "width": 2000, "height": 2000 }); + // this needs to be body, because #qunit-fixture is hidden and elements inside it don't have a scrollTop + forceScroll.appendTo("body"); + var checkDiv = jQuery("
").appendTo("#qunit-fixture")[0]; + + window.scrollTo( 200, 200 ); + window.supportsScroll = ( document.documentElement.scrollTop || document.body.scrollTop ); + window.scrollTo( 1, 1 ); + + checkDiv.style.position = "fixed"; + checkDiv.style.top = "20px"; + // safari subtracts parent border width here which is 5px + window.supportsFixedPosition = ( checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15 ); + checkDiv.style.position = checkDiv.style.top = ""; + jQuery( checkDiv ).remove(); + forceScroll.remove(); +}, teardown: moduleTeardown }); /* Closure-compiler will roll static methods off of the jQuery object and so they will @@ -33,28 +54,11 @@ test("disconnected node", function() { equal( result.left, 0, "Check left" ); }); -var supportsScroll = false; - testIframe("offset/absolute", "absolute", function($, iframe) { expect(4); var doc = iframe.document, - tests, forceScroll; - - // force a scroll value on the main window - // this insures that the results will be wrong - // if the offset method is using the scroll offset - // of the parent window - forceScroll = jQuery("
").css({ "width": 2000, "height": 2000 }); - forceScroll.appendTo("body"); - - window.scrollTo(200, 200); - - if ( document.documentElement.scrollTop || document.body.scrollTop ) { - supportsScroll = true; - } - - window.scrollTo(1, 1); + tests; // get offset tests = [ @@ -74,8 +78,6 @@ testIframe("offset/absolute", "absolute", function($, iframe) { equal( jQuery( this["id"], doc ).position().top, this["top"], "jQuery('" + this["id"] + "').position().top" ); equal( jQuery( this["id"], doc ).position().left, this["left"], "jQuery('" + this["id"] + "').position().left" ); }); - - forceScroll.remove(); }); testIframe("offset/absolute", "absolute", function( $ ) { @@ -293,21 +295,25 @@ testIframe("offset/static", "static", function( $ ) { }); testIframe("offset/fixed", "fixed", function( $ ) { - expect(30); + expect(34); var tests = [ - { "id": "#fixed-1", "top": 1001, "left": 1001 }, - { "id": "#fixed-2", "top": 1021, "left": 1021 } + { "id": "#fixed-1", "offsetTop": 1001, "offsetLeft": 1001, "positionTop": 0, "positionLeft": 0 }, + { "id": "#fixed-2", "offsetTop": 1021, "offsetLeft": 1021, "positionTop": 20, "positionLeft": 20 } ]; jQuery.each( tests, function() { - if ( !supportsScroll ) { + if ( !window.supportsScroll ) { + ok( true, "Browser doesn't support scroll position." ); + ok( true, "Browser doesn't support scroll position." ); ok( true, "Browser doesn't support scroll position." ); ok( true, "Browser doesn't support scroll position." ); - } else if ( jQuery.offset.supportsFixedPosition ) { - equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset().top" ); - equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset().left" ); + } else if ( window.supportsFixedPosition ) { + equal( $( this["id"] ).offset().top, this["offsetTop"], "jQuery('" + this["id"] + "').offset().top" ); + equal( $( this["id"] ).position().top, this["positionTop"], "jQuery('" + this["id"] + "').position().top" ); + equal( $( this["id"] ).offset().left, this["offsetLeft"], "jQuery('" + this["id"] + "').offset().left" ); + equal( $( this["id"] ).position().left, this["positionLeft"], "jQuery('" + this["id"] + "').position().left" ); } else { // need to have same number of assertions ok( true, "Fixed position is not supported" ); @@ -325,7 +331,7 @@ testIframe("offset/fixed", "fixed", function( $ ) { ]; jQuery.each( tests, function() { - if ( jQuery.offset.supportsFixedPosition ) { + if ( window.supportsFixedPosition ) { $( this["id"] ).offset({ "top": this["top"], "left": this["left"] }); equal( $( this["id"] ).offset().top, this["top"], "jQuery('" + this["id"] + "').offset({ top: " + this["top"] + " })" ); equal( $( this["id"] ).offset().left, this["left"], "jQuery('" + this["id"] + "').offset({ left: " + this["left"] + " })" ); @@ -349,7 +355,7 @@ testIframe("offset/fixed", "fixed", function( $ ) { // Bug 8316 var $noTopLeft = $("#fixed-no-top-left"); - if ( jQuery.offset.supportsFixedPosition ) { + if ( window.supportsFixedPosition ) { equal( $noTopLeft.offset().top, 1007, "Check offset top for fixed element with no top set" ); equal( $noTopLeft.offset().left, 1007, "Check offset left for fixed element with no left set" ); } else { @@ -397,7 +403,7 @@ testIframe("offset/scroll", "scroll", function( $, win ) { win.name = "test"; - if ( !supportsScroll ) { + if ( !window.supportsScroll ) { ok( true, "Browser doesn't support scroll position." ); ok( true, "Browser doesn't support scroll position." ); -- 2.39.5