Deprecated: Define `.hover()` using non-deprecated methods
[jquery.git] / src / offset.js
blob7d98083b9e6b5a6fb9bee5ab1557bb9a1aac40da
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";
8 import "./selector.js"; // contains
10 jQuery.offset = {
11         setOffset: function( elem, options, i ) {
12                 var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
13                         position = jQuery.css( elem, "position" ),
14                         curElem = jQuery( elem ),
15                         props = {};
17                 // Set position first, in-case top/left are set even on static elem
18                 if ( position === "static" ) {
19                         elem.style.position = "relative";
20                 }
22                 curOffset = curElem.offset();
23                 curCSSTop = jQuery.css( elem, "top" );
24                 curCSSLeft = jQuery.css( elem, "left" );
25                 calculatePosition = ( position === "absolute" || position === "fixed" ) &&
26                         ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
28                 // Need to be able to calculate position if either
29                 // top or left is auto and position is either absolute or fixed
30                 if ( calculatePosition ) {
31                         curPosition = curElem.position();
32                         curTop = curPosition.top;
33                         curLeft = curPosition.left;
35                 } else {
36                         curTop = parseFloat( curCSSTop ) || 0;
37                         curLeft = parseFloat( curCSSLeft ) || 0;
38                 }
40                 if ( typeof options === "function" ) {
42                         // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
43                         options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
44                 }
46                 if ( options.top != null ) {
47                         props.top = ( options.top - curOffset.top ) + curTop;
48                 }
49                 if ( options.left != null ) {
50                         props.left = ( options.left - curOffset.left ) + curLeft;
51                 }
53                 if ( "using" in options ) {
54                         options.using.call( elem, props );
56                 } else {
57                         curElem.css( props );
58                 }
59         }
62 jQuery.fn.extend( {
64         // offset() relates an element's border box to the document origin
65         offset: function( options ) {
67                 // Preserve chaining for setter
68                 if ( arguments.length ) {
69                         return options === undefined ?
70                                 this :
71                                 this.each( function( i ) {
72                                         jQuery.offset.setOffset( this, options, i );
73                                 } );
74                 }
76                 var rect, win,
77                         elem = this[ 0 ];
79                 if ( !elem ) {
80                         return;
81                 }
83                 // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
84                 // Support: IE <=11+
85                 // Running getBoundingClientRect on a
86                 // disconnected node in IE throws an error
87                 if ( !elem.getClientRects().length ) {
88                         return { top: 0, left: 0 };
89                 }
91                 // Get document-relative position by adding viewport scroll to viewport-relative gBCR
92                 rect = elem.getBoundingClientRect();
93                 win = elem.ownerDocument.defaultView;
94                 return {
95                         top: rect.top + win.pageYOffset,
96                         left: rect.left + win.pageXOffset
97                 };
98         },
100         // position() relates an element's margin box to its offset parent's padding box
101         // This corresponds to the behavior of CSS absolute positioning
102         position: function() {
103                 if ( !this[ 0 ] ) {
104                         return;
105                 }
107                 var offsetParent, offset, doc,
108                         elem = this[ 0 ],
109                         parentOffset = { top: 0, left: 0 };
111                 // position:fixed elements are offset from the viewport, which itself always has zero offset
112                 if ( jQuery.css( elem, "position" ) === "fixed" ) {
114                         // Assume position:fixed implies availability of getBoundingClientRect
115                         offset = elem.getBoundingClientRect();
117                 } else {
118                         offset = this.offset();
120                         // Account for the *real* offset parent, which can be the document or its root element
121                         // when a statically positioned element is identified
122                         doc = elem.ownerDocument;
123                         offsetParent = elem.offsetParent || doc.documentElement;
124                         while ( offsetParent &&
125                                 ( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
126                                 jQuery.css( offsetParent, "position" ) === "static" ) {
128                                 offsetParent = offsetParent.parentNode;
129                         }
130                         if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
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 default jQuery;