blob: 83b1c3a533ef1d4582bf8e6d931e0fd87b6017e2 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001define( [
2 "./core",
3 "./core/access",
4 "./var/document",
5 "./var/documentElement",
6 "./var/isFunction",
7 "./css/var/rnumnonpx",
8 "./css/curCSS",
9 "./css/addGetHookIf",
10 "./css/support",
11 "./var/isWindow",
12 "./core/init",
13 "./css",
14 "./selector" // contains
15], function( jQuery, access, document, documentElement, isFunction, rnumnonpx,
16 curCSS, addGetHookIf, support, isWindow ) {
17
18"use strict";
19
20jQuery.offset = {
21 setOffset: function( elem, options, i ) {
22 var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
23 position = jQuery.css( elem, "position" ),
24 curElem = jQuery( elem ),
25 props = {};
26
27 // Set position first, in-case top/left are set even on static elem
28 if ( position === "static" ) {
29 elem.style.position = "relative";
30 }
31
32 curOffset = curElem.offset();
33 curCSSTop = jQuery.css( elem, "top" );
34 curCSSLeft = jQuery.css( elem, "left" );
35 calculatePosition = ( position === "absolute" || position === "fixed" ) &&
36 ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
37
38 // Need to be able to calculate position if either
39 // top or left is auto and position is either absolute or fixed
40 if ( calculatePosition ) {
41 curPosition = curElem.position();
42 curTop = curPosition.top;
43 curLeft = curPosition.left;
44
45 } else {
46 curTop = parseFloat( curCSSTop ) || 0;
47 curLeft = parseFloat( curCSSLeft ) || 0;
48 }
49
50 if ( isFunction( options ) ) {
51
52 // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
53 options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
54 }
55
56 if ( options.top != null ) {
57 props.top = ( options.top - curOffset.top ) + curTop;
58 }
59 if ( options.left != null ) {
60 props.left = ( options.left - curOffset.left ) + curLeft;
61 }
62
63 if ( "using" in options ) {
64 options.using.call( elem, props );
65
66 } else {
67 curElem.css( props );
68 }
69 }
70};
71
72jQuery.fn.extend( {
73
74 // offset() relates an element's border box to the document origin
75 offset: function( options ) {
76
77 // Preserve chaining for setter
78 if ( arguments.length ) {
79 return options === undefined ?
80 this :
81 this.each( function( i ) {
82 jQuery.offset.setOffset( this, options, i );
83 } );
84 }
85
86 var rect, win,
87 elem = this[ 0 ];
88
89 if ( !elem ) {
90 return;
91 }
92
93 // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
94 // Support: IE <=11 only
95 // Running getBoundingClientRect on a
96 // disconnected node in IE throws an error
97 if ( !elem.getClientRects().length ) {
98 return { top: 0, left: 0 };
99 }
100
101 // Get document-relative position by adding viewport scroll to viewport-relative gBCR
102 rect = elem.getBoundingClientRect();
103 win = elem.ownerDocument.defaultView;
104 return {
105 top: rect.top + win.pageYOffset,
106 left: rect.left + win.pageXOffset
107 };
108 },
109
110 // position() relates an element's margin box to its offset parent's padding box
111 // This corresponds to the behavior of CSS absolute positioning
112 position: function() {
113 if ( !this[ 0 ] ) {
114 return;
115 }
116
117 var offsetParent, offset, doc,
118 elem = this[ 0 ],
119 parentOffset = { top: 0, left: 0 };
120
121 // position:fixed elements are offset from the viewport, which itself always has zero offset
122 if ( jQuery.css( elem, "position" ) === "fixed" ) {
123
124 // Assume position:fixed implies availability of getBoundingClientRect
125 offset = elem.getBoundingClientRect();
126
127 } else {
128 offset = this.offset();
129
130 // Account for the *real* offset parent, which can be the document or its root element
131 // when a statically positioned element is identified
132 doc = elem.ownerDocument;
133 offsetParent = elem.offsetParent || doc.documentElement;
134 while ( offsetParent &&
135 ( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
136 jQuery.css( offsetParent, "position" ) === "static" ) {
137
138 offsetParent = offsetParent.parentNode;
139 }
140 if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
141
142 // Incorporate borders into its offset, since they are outside its content origin
143 parentOffset = jQuery( offsetParent ).offset();
144 parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
145 parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
146 }
147 }
148
149 // Subtract parent offsets and element margins
150 return {
151 top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
152 left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
153 };
154 },
155
156 // This method will return documentElement in the following cases:
157 // 1) For the element inside the iframe without offsetParent, this method will return
158 // documentElement of the parent window
159 // 2) For the hidden or detached element
160 // 3) For body or html element, i.e. in case of the html node - it will return itself
161 //
162 // but those exceptions were never presented as a real life use-cases
163 // and might be considered as more preferable results.
164 //
165 // This logic, however, is not guaranteed and can change at any point in the future
166 offsetParent: function() {
167 return this.map( function() {
168 var offsetParent = this.offsetParent;
169
170 while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
171 offsetParent = offsetParent.offsetParent;
172 }
173
174 return offsetParent || documentElement;
175 } );
176 }
177} );
178
179// Create scrollLeft and scrollTop methods
180jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
181 var top = "pageYOffset" === prop;
182
183 jQuery.fn[ method ] = function( val ) {
184 return access( this, function( elem, method, val ) {
185
186 // Coalesce documents and windows
187 var win;
188 if ( isWindow( elem ) ) {
189 win = elem;
190 } else if ( elem.nodeType === 9 ) {
191 win = elem.defaultView;
192 }
193
194 if ( val === undefined ) {
195 return win ? win[ prop ] : elem[ method ];
196 }
197
198 if ( win ) {
199 win.scrollTo(
200 !top ? val : win.pageXOffset,
201 top ? val : win.pageYOffset
202 );
203
204 } else {
205 elem[ method ] = val;
206 }
207 }, method, val, arguments.length );
208 };
209} );
210
211// Support: Safari <=7 - 9.1, Chrome <=37 - 49
212// Add the top/left cssHooks using jQuery.fn.position
213// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
214// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
215// getComputedStyle returns percent when specified for top/left/bottom/right;
216// rather than make the css module depend on the offset module, just check for it here
217jQuery.each( [ "top", "left" ], function( i, prop ) {
218 jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
219 function( elem, computed ) {
220 if ( computed ) {
221 computed = curCSS( elem, prop );
222
223 // If curCSS returns percentage, fallback to offset
224 return rnumnonpx.test( computed ) ?
225 jQuery( elem ).position()[ prop ] + "px" :
226 computed;
227 }
228 }
229 );
230} );
231
232return jQuery;
233} );