Permalink
Browse files

Offset: Use correct offset parents; include all border/scroll values

Thanks @anseki

Fixes gh-3080
Fixes gh-3107
Closes gh-3096
Closes gh-3487
  • Loading branch information...
gibson042 committed Apr 24, 2017
1 parent e1b1b2d commit 1d2df772b4d6e5dbf91df6e75f4a1809f7879ab0
Showing with 340 additions and 27 deletions.
  1. +31 −26 src/offset.js
  2. +99 −0 test/data/offset/boxes.html
  3. +210 −1 test/unit/offset.js
@@ -7,13 +7,12 @@ define( [
"./css/curCSS",
"./css/addGetHookIf",
"./css/support",
"./core/nodeName",
"./core/init",
"./css",
"./selector" // contains
], function( jQuery, access, document, documentElement, rnumnonpx,
curCSS, addGetHookIf, support, nodeName ) {
curCSS, addGetHookIf, support ) {
"use strict";
@@ -70,6 +69,8 @@ jQuery.offset = {
};
jQuery.fn.extend( {
// offset() relates an element's border box to the document origin
offset: function( options ) {
// Preserve chaining for setter
@@ -81,7 +82,7 @@ jQuery.fn.extend( {
} );
}
var doc, docElem, rect, win,
var rect, win,
elem = this[ 0 ];
if ( !elem ) {
@@ -96,50 +97,54 @@ jQuery.fn.extend( {
return { top: 0, left: 0 };
}
// Get document-relative position by adding viewport scroll to viewport-relative gBCR
rect = elem.getBoundingClientRect();
doc = elem.ownerDocument;
docElem = doc.documentElement;
win = doc.defaultView;
win = elem.ownerDocument.defaultView;
return {
top: rect.top + win.pageYOffset - docElem.clientTop,
left: rect.left + win.pageXOffset - docElem.clientLeft
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset
};
},
// position() relates an element's margin box to its offset parent's padding box
// This corresponds to the behavior of CSS absolute positioning
position: function() {
if ( !this[ 0 ] ) {
return;
}
var offsetParent, offset,
var offsetParent, offset, doc,
elem = this[ 0 ],
parentOffset = { top: 0, left: 0 };
// Fixed elements are offset from window (parentOffset = {top:0, left: 0},
// because it is its only offset parent
// position:fixed elements are offset from the viewport, which itself always has zero offset
if ( jQuery.css( elem, "position" ) === "fixed" ) {
// Assume getBoundingClientRect is there when computed position is fixed
// Assume position:fixed implies availability of getBoundingClientRect
offset = elem.getBoundingClientRect();
} else {
offset = this.offset();
// Get *real* offsetParent
offsetParent = this.offsetParent();
// Account for the *real* offset parent, which can be the document or its root element
// when a statically positioned element is identified
doc = elem.ownerDocument;
offsetParent = elem.offsetParent || doc.documentElement;
while ( offsetParent &&
( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
jQuery.css( offsetParent, "position" ) === "static" ) {
// Get correct offsets
offset = this.offset();
if ( !nodeName( offsetParent[ 0 ], "html" ) ) {
parentOffset = offsetParent.offset();
offsetParent = offsetParent.parentNode;
}
if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
// Incorporate borders into its offset, since they are outside its content origin
parentOffset = jQuery( offsetParent ).offset();
parentOffset = {
top: parentOffset.top + jQuery.css( offsetParent, "borderTopWidth", true ),
left: parentOffset.left + jQuery.css( offsetParent, "borderLeftWidth", true )
};
}
// Add offsetParent borders
parentOffset = {
top: parentOffset.top + jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ),
left: parentOffset.left + jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true )
};
}
// Subtract parent offsets and element margins
@@ -0,0 +1,99 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html id="documentElement" class="box">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="description" content="horizontal values 2^N; vertical doubled">
<title>Nonempty margin/border/padding/position</title>
<style type="text/css" media="screen">
/* work with convenient classes, units, and dimensions */
.static { position: static; }
.relative { position: relative; }
.absolute { position: absolute; }
.fixed { position: fixed; }
.box {
font-size: 4px;
border-style: solid;
min-width: 300px;
}
/* start the exponential scales, reserving the first bit for scroll position */
.box {
border-width: 1em 0.5em;
top: 2em; left: 1em;
margin: 4em 2em;
padding: 8em 4em;
}
#documentElement {
margin: 16em 8em;
border-width: 32em 16em;
padding: 64em 32em;
}
#body {
margin: 128em 64em;
border-width: 256em 128em;
padding: 512em 256em;
}
#documentElement {
top: 1024em; left: 512em;
}
#body {
top: 2048em; left: 1024em;
}
/* style for humans */
:not(.box) {
font-size: 20px;
}
html {
border-color: hsl(20, 100%, 70%);
background-color: hsl(110, 100%, 70%);
}
body {
border-color: hsl(200, 100%, 70%);
background-color: hsl(290, 100%, 70%);
}
html::after,
body::after {
font: italic 16px sans-serif;
content: attr(id);
}
div.box {
background-color: hsla(0, 0%, 70%, 0.5);
opacity: 0.7;
}
div.box div.box {
background-color: hsla(60, 100%, 70%, 0.5);
}
</style>
<script src="../../jquery.js"></script>
<script src="../iframeTest.js"></script>
<script type="text/javascript" charset="utf-8">
jQuery( function() {
window.scrollTo( 1, 2 );
startIframeTest();
} );
</script>
</head>
<body id="body" class="box">
<div id="relative" class="relative box">
<div id="relative-relative" class="relative box"><code
>relative &gt; relative</code></div>
<div id="relative-absolute" class="absolute box"><code
>relative &gt; absolute</code></div>
</div>
<div id="absolute" class="absolute box">
<div id="absolute-relative" class="relative box"><code
>absolute &gt; relative</code></div>
<div id="absolute-absolute" class="absolute box"><code
>absolute &gt; absolute</code></div>
</div>
<div id="fixed" class="fixed box">
<div id="fixed-relative" class="relative box"><code
>fixed &gt; relative</code></div>
<div id="fixed-absolute" class="absolute box"><code
>fixed &gt; absolute</code></div>
</div>
<p id="positionTest" class="absolute">position:absolute with no top/left values</p>
</body>
</html>
Oops, something went wrong.

0 comments on commit 1d2df77

Please sign in to comment.