Manipulation: Support $el.html(selfRemovingScript) (#5378)
[jquery.git] / src / offset.js
blobca27ca5c7d74adc5767d4373edffddcb022b51cc
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.body || offsetParent === doc.documentElement ) &&
125                                 jQuery.css( offsetParent, "position" ) === "static" ) {
127                                 offsetParent = offsetParent.parentNode;
128                         }
129                         if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
131                                 // Incorporate borders into its offset, since they are outside its content origin
132                                 parentOffset = jQuery( offsetParent ).offset();
133                                 parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
134                                 parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
135                         }
136                 }
138                 // Subtract parent offsets and element margins
139                 return {
140                         top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
141                         left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
142                 };
143         },
145         // This method will return documentElement in the following cases:
146         // 1) For the element inside the iframe without offsetParent, this method will return
147         //    documentElement of the parent window
148         // 2) For the hidden or detached element
149         // 3) For body or html element, i.e. in case of the html node - it will return itself
150         //
151         // but those exceptions were never presented as a real life use-cases
152         // and might be considered as more preferable results.
153         //
154         // This logic, however, is not guaranteed and can change at any point in the future
155         offsetParent: function() {
156                 return this.map( function() {
157                         var offsetParent = this.offsetParent;
159                         while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
160                                 offsetParent = offsetParent.offsetParent;
161                         }
163                         return offsetParent || documentElement;
164                 } );
165         }
166 } );
168 // Create scrollLeft and scrollTop methods
169 jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
170         var top = "pageYOffset" === prop;
172         jQuery.fn[ method ] = function( val ) {
173                 return access( this, function( elem, method, val ) {
175                         // Coalesce documents and windows
176                         var win;
177                         if ( isWindow( elem ) ) {
178                                 win = elem;
179                         } else if ( elem.nodeType === 9 ) {
180                                 win = elem.defaultView;
181                         }
183                         if ( val === undefined ) {
184                                 return win ? win[ prop ] : elem[ method ];
185                         }
187                         if ( win ) {
188                                 win.scrollTo(
189                                         !top ? val : win.pageXOffset,
190                                         top ? val : win.pageYOffset
191                                 );
193                         } else {
194                                 elem[ method ] = val;
195                         }
196                 }, method, val, arguments.length );
197         };
198 } );
200 export { jQuery, jQuery as $ };