Tests: revert concurrency group change
[jquery.git] / src / offset.js
blob65dab2bd205202c4533018638d3b789a8ecb6cc8
1 import { jQuery } from "./core.js";
2 import { access } from "./core/access.js";
3 import { documentElement } from "./var/documentElement.js";
4 import { isWindow } from "./var/isWindow.js";
6 import "./core/init.js";
7 import "./css.js";
9 jQuery.offset = {
10         setOffset: function( elem, options, i ) {
11                 var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
12                         position = jQuery.css( elem, "position" ),
13                         curElem = jQuery( elem ),
14                         props = {};
16                 // Set position first, in-case top/left are set even on static elem
17                 if ( position === "static" ) {
18                         elem.style.position = "relative";
19                 }
21                 curOffset = curElem.offset();
22                 curCSSTop = jQuery.css( elem, "top" );
23                 curCSSLeft = jQuery.css( elem, "left" );
24                 calculatePosition = ( position === "absolute" || position === "fixed" ) &&
25                         ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
27                 // Need to be able to calculate position if either
28                 // top or left is auto and position is either absolute or fixed
29                 if ( calculatePosition ) {
30                         curPosition = curElem.position();
31                         curTop = curPosition.top;
32                         curLeft = curPosition.left;
34                 } else {
35                         curTop = parseFloat( curCSSTop ) || 0;
36                         curLeft = parseFloat( curCSSLeft ) || 0;
37                 }
39                 if ( typeof options === "function" ) {
41                         // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
42                         options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
43                 }
45                 if ( options.top != null ) {
46                         props.top = ( options.top - curOffset.top ) + curTop;
47                 }
48                 if ( options.left != null ) {
49                         props.left = ( options.left - curOffset.left ) + curLeft;
50                 }
52                 if ( "using" in options ) {
53                         options.using.call( elem, props );
55                 } else {
56                         curElem.css( props );
57                 }
58         }
61 jQuery.fn.extend( {
63         // offset() relates an element's border box to the document origin
64         offset: function( options ) {
66                 // Preserve chaining for setter
67                 if ( arguments.length ) {
68                         return options === undefined ?
69                                 this :
70                                 this.each( function( i ) {
71                                         jQuery.offset.setOffset( this, options, i );
72                                 } );
73                 }
75                 var rect, win,
76                         elem = this[ 0 ];
78                 if ( !elem ) {
79                         return;
80                 }
82                 // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
83                 // Support: IE <=11+
84                 // Running getBoundingClientRect on a
85                 // disconnected node in IE throws an error
86                 if ( !elem.getClientRects().length ) {
87                         return { top: 0, left: 0 };
88                 }
90                 // Get document-relative position by adding viewport scroll to viewport-relative gBCR
91                 rect = elem.getBoundingClientRect();
92                 win = elem.ownerDocument.defaultView;
93                 return {
94                         top: rect.top + win.pageYOffset,
95                         left: rect.left + win.pageXOffset
96                 };
97         },
99         // position() relates an element's margin box to its offset parent's padding box
100         // This corresponds to the behavior of CSS absolute positioning
101         position: function() {
102                 if ( !this[ 0 ] ) {
103                         return;
104                 }
106                 var offsetParent, offset, doc,
107                         elem = this[ 0 ],
108                         parentOffset = { top: 0, left: 0 };
110                 // position:fixed elements are offset from the viewport, which itself always has zero offset
111                 if ( jQuery.css( elem, "position" ) === "fixed" ) {
113                         // Assume position:fixed implies availability of getBoundingClientRect
114                         offset = elem.getBoundingClientRect();
116                 } else {
117                         offset = this.offset();
119                         // Account for the *real* offset parent, which can be the document or its root element
120                         // when a statically positioned element is identified
121                         doc = elem.ownerDocument;
122                         offsetParent = elem.offsetParent || doc.documentElement;
123                         while ( offsetParent &&
124                                 offsetParent !== doc.documentElement &&
125                                 jQuery.css( offsetParent, "position" ) === "static" ) {
127                                 offsetParent = offsetParent.offsetParent || doc.documentElement;
128                         }
129                         if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 &&
130                                 jQuery.css( offsetParent, "position" ) !== "static" ) {
132                                 // Incorporate borders into its offset, since they are outside its content origin
133                                 parentOffset = jQuery( offsetParent ).offset();
134                                 parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
135                                 parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
136                         }
137                 }
139                 // Subtract parent offsets and element margins
140                 return {
141                         top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
142                         left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
143                 };
144         },
146         // This method will return documentElement in the following cases:
147         // 1) For the element inside the iframe without offsetParent, this method will return
148         //    documentElement of the parent window
149         // 2) For the hidden or detached element
150         // 3) For body or html element, i.e. in case of the html node - it will return itself
151         //
152         // but those exceptions were never presented as a real life use-cases
153         // and might be considered as more preferable results.
154         //
155         // This logic, however, is not guaranteed and can change at any point in the future
156         offsetParent: function() {
157                 return this.map( function() {
158                         var offsetParent = this.offsetParent;
160                         while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
161                                 offsetParent = offsetParent.offsetParent;
162                         }
164                         return offsetParent || documentElement;
165                 } );
166         }
167 } );
169 // Create scrollLeft and scrollTop methods
170 jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
171         var top = "pageYOffset" === prop;
173         jQuery.fn[ method ] = function( val ) {
174                 return access( this, function( elem, method, val ) {
176                         // Coalesce documents and windows
177                         var win;
178                         if ( isWindow( elem ) ) {
179                                 win = elem;
180                         } else if ( elem.nodeType === 9 ) {
181                                 win = elem.defaultView;
182                         }
184                         if ( val === undefined ) {
185                                 return win ? win[ prop ] : elem[ method ];
186                         }
188                         if ( win ) {
189                                 win.scrollTo(
190                                         !top ? val : win.pageXOffset,
191                                         top ? val : win.pageYOffset
192                                 );
194                         } else {
195                                 elem[ method ] = val;
196                         }
197                 }, method, val, arguments.length );
198         };
199 } );
201 export { jQuery, jQuery as $ };