3 if ( !includesModule( "offset" ) ) {
8 forceScroll
= supportjQuery( "<div></div>" ).css( { width
: 2000, height
: 2000 } ),
9 checkSupport = function( assert
) {
14 // Append forceScroll to the body instead of #qunit-fixture because the latter is hidden
15 forceScroll
.appendTo( "body" );
16 window
.scrollTo( 200, 200 );
19 // Support: iOS <=7 - 12+
20 // Hijack the iframe test infrastructure to detect viewport scrollability
21 // for pages with position:fixed document element
22 var done
= assert
.async();
26 function( assert
, $, win
, doc
) {
27 var scrollTop
= win
.pageYOffset
,
28 scrollLeft
= win
.pageXOffset
;
29 doc
.documentElement
.style
.position
= "fixed";
30 win
.scrollTo( scrollLeft
, scrollTop
);
31 alwaysScrollable
= win
.pageXOffset
!== 0;
34 function mockQUnit_test( _
, testCallback
) {
35 setTimeout( function() {
36 testCallback( assert
);
42 QUnit
.module( "offset", { beforeEach: function( assert
) {
43 if ( typeof checkSupport
=== "function" ) {
44 checkSupport( assert
);
47 // Force a scroll value on the main window to ensure incorrect results
48 // if offset is using the scroll offset of the parent window
49 forceScroll
.appendTo( "body" );
50 window
.scrollTo( 1, 1 );
52 }, afterEach
: moduleTeardown
} );
54 QUnit
.test( "empty set", function( assert
) {
56 assert
.strictEqual( jQuery().offset(), undefined, "offset() returns undefined for empty set (trac-11962)" );
57 assert
.strictEqual( jQuery().position(), undefined, "position() returns undefined for empty set (trac-11962)" );
60 QUnit
.test( "disconnected element", function( assert
) {
63 var result
= jQuery( document
.createElement( "div" ) ).offset();
65 // These tests are solely for main/compat consistency
66 // Retrieving offset on disconnected/hidden elements is not officially
67 // valid input, but will return zeros for back-compat
68 assert
.equal( result
.top
, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" );
69 assert
.equal( result
.left
, 0, "Retrieving offset on disconnected elements returns zeros (gh-2310)" );
70 assert
.equal( Object
.keys( result
).length
, 2, "Retrieving offset on disconnected elements returns offset object (gh-3167)" );
71 assert
.equal( jQuery
.isPlainObject( result
), true, "Retrieving offset on disconnected elements returns plain object (gh-3612)" );
74 QUnit
.test( "hidden (display: none) element", function( assert
) {
77 var node
= jQuery( "<div style='display: none'></div>" ).appendTo( "#qunit-fixture" ),
78 result
= node
.offset();
82 // These tests are solely for main/compat consistency
83 // Retrieving offset on disconnected/hidden elements is not officially
84 // valid input, but will return zeros for back-compat
85 assert
.equal( result
.top
, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" );
86 assert
.equal( result
.left
, 0, "Retrieving offset on hidden elements returns zeros (gh-2310)" );
87 assert
.equal( Object
.keys( result
).length
, 2, "Retrieving offset on hidden elements returns offset object (gh-3167)" );
88 assert
.equal( jQuery
.isPlainObject( result
), true, "Retrieving offset on hidden elements returns plain object (gh-3612)" );
91 QUnit
.test( "0 sized element", function( assert
) {
94 var node
= jQuery( "<div style='margin: 5px; width: 0; height: 0'></div>" ).appendTo( "#qunit-fixture" ),
95 result
= node
.offset();
99 assert
.notEqual( result
.top
, 0, "Retrieving offset on 0 sized elements (gh-3167)" );
100 assert
.notEqual( result
.left
, 0, "Retrieving offset on 0 sized elements (gh-3167)" );
101 assert
.equal( Object
.keys( result
).length
, 2, "Retrieving offset on 0 sized elements returns offset object (gh-3167)" );
102 assert
.equal( jQuery
.isPlainObject( result
), true, "Retrieving offset on 0 sized elements returns plain object (gh-3612)" );
105 QUnit
.test( "hidden (visibility: hidden) element", function( assert
) {
108 var node
= jQuery( "<div style='margin: 5px; visibility: hidden'></div>" ).appendTo( "#qunit-fixture" ),
109 result
= node
.offset();
113 assert
.notEqual( result
.top
, 0, "Retrieving offset on visibility:hidden elements (gh-3167)" );
114 assert
.notEqual( result
.left
, 0, "Retrieving offset on visibility:hidden elements (gh-3167)" );
115 assert
.equal( Object
.keys( result
).length
, 2, "Retrieving offset on visibility:hidden elements returns offset object (gh-3167)" );
116 assert
.equal( jQuery
.isPlainObject( result
), true, "Retrieving offset on visibility:hidden elements returns plain object (gh-3612)" );
119 QUnit
.test( "normal element", function( assert
) {
122 var node
= jQuery( "<div>" ).appendTo( "#qunit-fixture" ),
123 offset
= node
.offset(),
124 position
= node
.position();
128 assert
.equal( Object
.keys( offset
).length
, 2, "Retrieving offset on normal elements returns offset object (gh-3612)" );
129 assert
.equal( jQuery
.isPlainObject( offset
), true, "Retrieving offset on normal elements returns plain object (gh-3612)" );
131 assert
.equal( Object
.keys( position
).length
, 2, "Retrieving position on normal elements returns offset object (gh-3612)" );
132 assert
.equal( jQuery
.isPlainObject( position
), true, "Retrieving position on normal elements returns plain object (gh-3612)" );
135 testIframe( "absolute", "offset/absolute.html", function( assert
, $, iframe
) {
138 var doc
= iframe
.document
,
143 { "id": "#absolute-1", "top": 1, "left": 1 }
145 jQuery
.each( tests
, function() {
146 assert
.equal( jQuery( this.id
, doc
).offset().top
, this.top
, "jQuery('" + this.id
+ "').offset().top" );
147 assert
.equal( jQuery( this.id
, doc
).offset().left
, this.left
, "jQuery('" + this.id
+ "').offset().left" );
152 { "id": "#absolute-1", "top": 0, "left": 0 }
154 jQuery
.each( tests
, function() {
155 assert
.equal( jQuery( this.id
, doc
).position().top
, this.top
, "jQuery('" + this.id
+ "').position().top" );
156 assert
.equal( jQuery( this.id
, doc
).position().left
, this.left
, "jQuery('" + this.id
+ "').position().left" );
160 testIframe( "absolute", "offset/absolute.html", function( assert
, $ ) {
161 assert
.expect( 178 );
167 { "id": "#absolute-1", "top": 1, "left": 1 },
168 { "id": "#absolute-1-1", "top": 5, "left": 5 },
169 { "id": "#absolute-1-1-1", "top": 9, "left": 9 },
170 { "id": "#absolute-2", "top": 20, "left": 20 }
172 jQuery
.each( tests
, function() {
173 assert
.equal( $( this.id
).offset().top
, this.top
, "jQuery('" + this.id
+ "').offset().top" );
174 assert
.equal( $( this.id
).offset().left
, this.left
, "jQuery('" + this.id
+ "').offset().left" );
179 { "id": "#absolute-1", "top": 0, "left": 0 },
180 { "id": "#absolute-1-1", "top": 1, "left": 1 },
181 { "id": "#absolute-1-1-1", "top": 1, "left": 1 },
182 { "id": "#absolute-2", "top": 19, "left": 19 }
184 jQuery
.each( tests
, function() {
185 assert
.equal( $( this.id
).position().top
, this.top
, "jQuery('" + this.id
+ "').position().top" );
186 assert
.equal( $( this.id
).position().left
, this.left
, "jQuery('" + this.id
+ "').position().left" );
190 offset
= $( "#positionTest" ).offset( { "top": 10, "left": 10 } ).offset();
191 assert
.equal( offset
.top
, 10, "Setting offset on element with position absolute but 'auto' values." );
192 assert
.equal( offset
.left
, 10, "Setting offset on element with position absolute but 'auto' values." );
196 { "id": "#absolute-2", "top": 30, "left": 30 },
197 { "id": "#absolute-2", "top": 10, "left": 10 },
198 { "id": "#absolute-2", "top": -1, "left": -1 },
199 { "id": "#absolute-2", "top": 19, "left": 19 },
200 { "id": "#absolute-1-1-1", "top": 15, "left": 15 },
201 { "id": "#absolute-1-1-1", "top": 5, "left": 5 },
202 { "id": "#absolute-1-1-1", "top": -1, "left": -1 },
203 { "id": "#absolute-1-1-1", "top": 9, "left": 9 },
204 { "id": "#absolute-1-1", "top": 10, "left": 10 },
205 { "id": "#absolute-1-1", "top": 0, "left": 0 },
206 { "id": "#absolute-1-1", "top": -1, "left": -1 },
207 { "id": "#absolute-1-1", "top": 5, "left": 5 },
208 { "id": "#absolute-1", "top": 2, "left": 2 },
209 { "id": "#absolute-1", "top": 0, "left": 0 },
210 { "id": "#absolute-1", "top": -1, "left": -1 },
211 { "id": "#absolute-1", "top": 1, "left": 1 }
213 jQuery
.each( tests
, function() {
214 $( this.id
).offset( { "top": this.top
, "left": this.left
} );
215 assert
.equal( $( this.id
).offset().top
, this.top
, "jQuery('" + this.id
+ "').offset({ top: " + this.top
+ " })" );
216 assert
.equal( $( this.id
).offset().left
, this.left
, "jQuery('" + this.id
+ "').offset({ left: " + this.left
+ " })" );
218 var top
= this.top
, left
= this.left
;
220 $( this.id
).offset( function( i
, val
) {
221 assert
.equal( val
.top
, top
, "Verify incoming top position." );
222 assert
.equal( val
.left
, left
, "Verify incoming top position." );
223 return { "top": top
+ 1, "left": left
+ 1 };
225 assert
.equal( $( this.id
).offset().top
, this.top
+ 1, "jQuery('" + this.id
+ "').offset({ top: " + ( this.top
+ 1 ) + " })" );
226 assert
.equal( $( this.id
).offset().left
, this.left
+ 1, "jQuery('" + this.id
+ "').offset({ left: " + ( this.left
+ 1 ) + " })" );
229 .offset( { "left": this.left
+ 2 } )
230 .offset( { "top": this.top
+ 2 } );
231 assert
.equal( $( this.id
).offset().top
, this.top
+ 2, "Setting one property at a time." );
232 assert
.equal( $( this.id
).offset().left
, this.left
+ 2, "Setting one property at a time." );
234 $( this.id
).offset( { "top": this.top
, "left": this.left
, "using": function( props
) {
236 "top": props
.top
+ 1,
237 "left": props
.left
+ 1
240 assert
.equal( $( this.id
).offset().top
, this.top
+ 1, "jQuery('" + this.id
+ "').offset({ top: " + ( this.top
+ 1 ) + ", using: fn })" );
241 assert
.equal( $( this.id
).offset().left
, this.left
+ 1, "jQuery('" + this.id
+ "').offset({ left: " + ( this.left
+ 1 ) + ", using: fn })" );
245 testIframe( "relative", "offset/relative.html", function( assert
, $ ) {
250 { "id": "#relative-1", "top": 7, "left": 7 },
251 { "id": "#relative-1-1", "top": 15, "left": 15 },
252 { "id": "#relative-2", "top": 142, "left": 27 },
253 { "id": "#relative-2-1", "top": 149, "left": 52 }
255 jQuery
.each( tests
, function() {
256 assert
.equal( $( this.id
).offset().top
, this.top
, "jQuery('" + this.id
+ "').offset().top" );
257 assert
.equal( $( this.id
).offset().left
, this.left
, "jQuery('" + this.id
+ "').offset().left" );
262 { "id": "#relative-1", "top": 6, "left": 6 },
263 { "id": "#relative-1-1", "top": 5, "left": 5 },
264 { "id": "#relative-2", "top": 141, "left": 26 },
265 { "id": "#relative-2-1", "top": 5, "left": 5 }
267 jQuery
.each( tests
, function() {
268 assert
.equal( $( this.id
).position().top
, this.top
, "jQuery('" + this.id
+ "').position().top" );
269 assert
.equal( $( this.id
).position().left
, this.left
, "jQuery('" + this.id
+ "').position().left" );
274 { "id": "#relative-2", "top": 200, "left": 50 },
275 { "id": "#relative-2", "top": 100, "left": 10 },
276 { "id": "#relative-2", "top": -5, "left": -5 },
277 { "id": "#relative-2", "top": 142, "left": 27 },
278 { "id": "#relative-1-1", "top": 100, "left": 100 },
279 { "id": "#relative-1-1", "top": 5, "left": 5 },
280 { "id": "#relative-1-1", "top": -1, "left": -1 },
281 { "id": "#relative-1-1", "top": 15, "left": 15 },
282 { "id": "#relative-1", "top": 100, "left": 100 },
283 { "id": "#relative-1", "top": 0, "left": 0 },
284 { "id": "#relative-1", "top": -1, "left": -1 },
285 { "id": "#relative-1", "top": 7, "left": 7 }
287 jQuery
.each( tests
, function() {
288 $( this.id
).offset( { "top": this.top
, "left": this.left
} );
289 assert
.equal( $( this.id
).offset().top
, this.top
, "jQuery('" + this.id
+ "').offset({ top: " + this.top
+ " })" );
290 assert
.equal( $( this.id
).offset().left
, this.left
, "jQuery('" + this.id
+ "').offset({ left: " + this.left
+ " })" );
292 $( this.id
).offset( { "top": this.top
, "left": this.left
, "using": function( props
) {
294 "top": props
.top
+ 1,
295 "left": props
.left
+ 1
298 assert
.equal( $( this.id
).offset().top
, this.top
+ 1, "jQuery('" + this.id
+ "').offset({ top: " + ( this.top
+ 1 ) + ", using: fn })" );
299 assert
.equal( $( this.id
).offset().left
, this.left
+ 1, "jQuery('" + this.id
+ "').offset({ left: " + ( this.left
+ 1 ) + ", using: fn })" );
303 testIframe( "static", "offset/static.html", function( assert
, $ ) {
308 { "id": "#static-1", "top": 7, "left": 7 },
309 { "id": "#static-1-1", "top": 15, "left": 15 },
310 { "id": "#static-1-1-1", "top": 23, "left": 23 },
311 { "id": "#static-2", "top": 122, left
: 7 }
313 jQuery
.each( tests
, function() {
314 assert
.equal( $( this.id
).offset().top
, this.top
, "jQuery('" + this.id
+ "').offset().top" );
315 assert
.equal( $( this.id
).offset().left
, this.left
, "jQuery('" + this.id
+ "').offset().left" );
320 { "id": "#static-1", "top": 6, "left": 6 },
321 { "id": "#static-1-1", "top": 14, "left": 14 },
322 { "id": "#static-1-1-1", "top": 22, "left": 22 },
323 { "id": "#static-2", "top": 121, "left": 6 }
325 jQuery
.each( tests
, function() {
326 assert
.equal( $( this.id
).position().top
, this.top
, "jQuery('" + this.top
+ "').position().top" );
327 assert
.equal( $( this.id
).position().left
, this.left
, "jQuery('" + this.left
+ "').position().left" );
332 { "id": "#static-2", "top": 200, "left": 200 },
333 { "id": "#static-2", "top": 100, "left": 100 },
334 { "id": "#static-2", "top": -2, "left": -2 },
335 { "id": "#static-2", "top": 121, "left": 6 },
336 { "id": "#static-1-1-1", "top": 50, "left": 50 },
337 { "id": "#static-1-1-1", "top": 10, "left": 10 },
338 { "id": "#static-1-1-1", "top": -1, "left": -1 },
339 { "id": "#static-1-1-1", "top": 22, "left": 22 },
340 { "id": "#static-1-1", "top": 25, "left": 25 },
341 { "id": "#static-1-1", "top": 10, "left": 10 },
342 { "id": "#static-1-1", "top": -3, "left": -3 },
343 { "id": "#static-1-1", "top": 14, "left": 14 },
344 { "id": "#static-1", "top": 30, "left": 30 },
345 { "id": "#static-1", "top": 2, "left": 2 },
346 { "id": "#static-1", "top": -2, "left": -2 },
347 { "id": "#static-1", "top": 7, "left": 7 }
349 jQuery
.each( tests
, function() {
350 $( this.id
).offset( { "top": this.top
, "left": this.left
} );
351 assert
.equal( $( this.id
).offset().top
, this.top
, "jQuery('" + this.id
+ "').offset({ top: " + this.top
+ " })" );
352 assert
.equal( $( this.id
).offset().left
, this.left
, "jQuery('" + this.id
+ "').offset({ left: " + this.left
+ " })" );
354 $( this.id
).offset( { "top": this.top
, "left": this.left
, "using": function( props
) {
356 "top": props
.top
+ 1,
357 "left": props
.left
+ 1
360 assert
.equal( $( this.id
).offset().top
, this.top
+ 1, "jQuery('" + this.id
+ "').offset({ top: " + ( this.top
+ 1 ) + ", using: fn })" );
361 assert
.equal( $( this.id
).offset().left
, this.left
+ 1, "jQuery('" + this.id
+ "').offset({ left: " + ( this.left
+ 1 ) + ", using: fn })" );
365 testIframe( "fixed", "offset/fixed.html", function( assert
, $ ) {
368 var tests
, $noTopLeft
;
387 jQuery
.each( tests
, function() {
388 assert
.equal( jQuery
.isPlainObject( $( this.id
).offset() ), true, "jQuery('" + this.id
+ "').offset() is plain object" );
389 assert
.equal( jQuery
.isPlainObject( $( this.id
).position() ), true, "jQuery('" + this.id
+ "').position() is plain object" );
390 assert
.equal( $( this.id
).offset().top
, this.offsetTop
, "jQuery('" + this.id
+ "').offset().top" );
391 assert
.equal( $( this.id
).position().top
, this.positionTop
, "jQuery('" + this.id
+ "').position().top" );
392 assert
.equal( $( this.id
).offset().left
, this.offsetLeft
, "jQuery('" + this.id
+ "').offset().left" );
393 assert
.equal( $( this.id
).position().left
, this.positionLeft
, "jQuery('" + this.id
+ "').position().left" );
397 { "id": "#fixed-1", "top": 100, "left": 100 },
398 { "id": "#fixed-1", "top": 0, "left": 0 },
399 { "id": "#fixed-1", "top": -4, "left": -4 },
400 { "id": "#fixed-2", "top": 200, "left": 200 },
401 { "id": "#fixed-2", "top": 0, "left": 0 },
402 { "id": "#fixed-2", "top": -5, "left": -5 }
405 jQuery
.each( tests
, function() {
406 $( this.id
).offset( { "top": this.top
, "left": this.left
} );
407 assert
.equal( $( this.id
).offset().top
, this.top
, "jQuery('" + this.id
+ "').offset({ top: " + this.top
+ " })" );
408 assert
.equal( $( this.id
).offset().left
, this.left
, "jQuery('" + this.id
+ "').offset({ left: " + this.left
+ " })" );
410 $( this.id
).offset( { "top": this.top
, "left": this.left
, "using": function( props
) {
412 "top": props
.top
+ 1,
413 "left": props
.left
+ 1
416 assert
.equal( $( this.id
).offset().top
, this.top
+ 1, "jQuery('" + this.id
+ "').offset({ top: " + ( this.top
+ 1 ) + ", using: fn })" );
417 assert
.equal( $( this.id
).offset().left
, this.left
+ 1, "jQuery('" + this.id
+ "').offset({ left: " + ( this.left
+ 1 ) + ", using: fn })" );
421 $noTopLeft
= $( "#fixed-no-top-left" );
423 assert
.equal( $noTopLeft
.offset().top
, 1007, "Check offset top for fixed element with no top set" );
424 assert
.equal( $noTopLeft
.offset().left
, 1007, "Check offset left for fixed element with no left set" );
427 testIframe( "table", "offset/table.html", function( assert
, $ ) {
430 assert
.equal( $( "#table-1" ).offset().top
, 6, "jQuery('#table-1').offset().top" );
431 assert
.equal( $( "#table-1" ).offset().left
, 6, "jQuery('#table-1').offset().left" );
433 assert
.equal( $( "#th-1" ).offset().top
, 10, "jQuery('#th-1').offset().top" );
434 assert
.equal( $( "#th-1" ).offset().left
, 10, "jQuery('#th-1').offset().left" );
436 assert
.equal( $( "#th-1" ).position().top
, 10, "jQuery('#th-1').position().top" );
437 assert
.equal( $( "#th-1" ).position().left
, 10, "jQuery('#th-1').position().left" );
440 testIframe( "scroll", "offset/scroll.html", function( assert
, $, win
) {
443 assert
.equal( $( "#scroll-1" ).offset().top
, 7, "jQuery('#scroll-1').offset().top" );
444 assert
.equal( $( "#scroll-1" ).offset().left
, 7, "jQuery('#scroll-1').offset().left" );
446 assert
.equal( $( "#scroll-1-1" ).offset().top
, 11, "jQuery('#scroll-1-1').offset().top" );
447 assert
.equal( $( "#scroll-1-1" ).offset().left
, 11, "jQuery('#scroll-1-1').offset().left" );
449 // These tests are solely for main/compat consistency
450 // Retrieving offset on disconnected/hidden elements is not officially
451 // valid input, but will return zeros for back-compat
452 assert
.equal( $( "#hidden" ).offset().top
, 0, "Hidden elements do not subtract scroll" );
453 assert
.equal( $( "#hidden" ).offset().left
, 0, "Hidden elements do not subtract scroll" );
455 // scroll offset tests .scrollTop/Left
456 assert
.equal( $( "#scroll-1" ).scrollTop(), 5, "jQuery('#scroll-1').scrollTop()" );
457 assert
.equal( $( "#scroll-1" ).scrollLeft(), 5, "jQuery('#scroll-1').scrollLeft()" );
459 assert
.equal( $( "#scroll-1-1" ).scrollTop(), 0, "jQuery('#scroll-1-1').scrollTop()" );
460 assert
.equal( $( "#scroll-1-1" ).scrollLeft(), 0, "jQuery('#scroll-1-1').scrollLeft()" );
462 // scroll method chaining
463 assert
.equal( $( "#scroll-1" ).scrollTop( undefined ).scrollTop(), 5, ".scrollTop(undefined) is chainable (trac-5571)" );
464 assert
.equal( $( "#scroll-1" ).scrollLeft( undefined ).scrollLeft(), 5, ".scrollLeft(undefined) is chainable (trac-5571)" );
468 assert
.equal( $( win
).scrollTop(), 1000, "jQuery(window).scrollTop()" );
469 assert
.equal( $( win
).scrollLeft(), 1000, "jQuery(window).scrollLeft()" );
471 assert
.equal( $( win
.document
).scrollTop(), 1000, "jQuery(document).scrollTop()" );
472 assert
.equal( $( win
.document
).scrollLeft(), 1000, "jQuery(document).scrollLeft()" );
474 // test jQuery using parent window/document
475 // jQuery reference here is in the iframe
476 window
.scrollTo( 0, 0 );
477 assert
.equal( $( window
).scrollTop(), 0, "jQuery(window).scrollTop() other window" );
478 assert
.equal( $( window
).scrollLeft(), 0, "jQuery(window).scrollLeft() other window" );
479 assert
.equal( $( document
).scrollTop(), 0, "jQuery(window).scrollTop() other document" );
480 assert
.equal( $( document
).scrollLeft(), 0, "jQuery(window).scrollLeft() other document" );
482 // Tests scrollTop/Left with empty jquery objects
483 assert
.notEqual( $().scrollTop( 100 ), null, "jQuery().scrollTop(100) testing setter on empty jquery object" );
484 assert
.notEqual( $().scrollLeft( 100 ), null, "jQuery().scrollLeft(100) testing setter on empty jquery object" );
485 assert
.notEqual( $().scrollTop( null ), null, "jQuery().scrollTop(null) testing setter on empty jquery object" );
486 assert
.notEqual( $().scrollLeft( null ), null, "jQuery().scrollLeft(null) testing setter on empty jquery object" );
487 assert
.strictEqual( $().scrollTop(), undefined, "jQuery().scrollTop() testing getter on empty jquery object" );
488 assert
.strictEqual( $().scrollLeft(), undefined, "jQuery().scrollLeft() testing getter on empty jquery object" );
491 testIframe( "body", "offset/body.html", function( assert
, $ ) {
494 assert
.equal( $( "body" ).offset().top
, 1, "jQuery('#body').offset().top" );
495 assert
.equal( $( "body" ).offset().left
, 1, "jQuery('#body').offset().left" );
496 assert
.equal( $( "#firstElement" ).position().left
, 5, "$('#firstElement').position().left" );
497 assert
.equal( $( "#firstElement" ).position().top
, 5, "$('#firstElement').position().top" );
500 QUnit
.test( "chaining", function( assert
) {
503 var coords
= { "top": 1, "left": 1 };
504 assert
.equal( jQuery( "#absolute-1" ).offset( coords
).jquery
, jQuery
.fn
.jquery
, "offset(coords) returns jQuery object" );
505 assert
.equal( jQuery( "#non-existent" ).offset( coords
).jquery
, jQuery
.fn
.jquery
, "offset(coords) with empty jQuery set returns jQuery object" );
506 assert
.equal( jQuery( "#absolute-1" ).offset( undefined ).jquery
, jQuery
.fn
.jquery
, "offset(undefined) returns jQuery object (trac-5571)" );
509 // Test complex content under a variety of <html>/<body> positioning styles
511 var POSITION_VALUES
= [ "static", "relative", "absolute", "fixed" ],
513 // Use shorthands for describing an element's relevant properties
515 ( "top left marginTop marginLeft borderTop borderLeft paddingTop paddingLeft" +
516 " style parent" ).split( /\s+/g ),
519 supportjQuery
.each( arguments
, function( i
, value
) {
520 propObj
[ BOX_PROPS
[ i
] ] = value
;
525 // Values must stay synchronized with test/data/offset/boxes.html
526 divProps = function( position
, parentId
) {
527 return props( 8, 4, 16, 8, 4, 2, 32, 16, position
, parentId
);
529 htmlProps = function( position
) {
530 return props( position
=== "static" ? 0 : 4096, position
=== "static" ? 0 : 2048,
531 64, 32, 128, 64, 256, 128, position
);
533 bodyProps = function( position
) {
534 return props( position
=== "static" ? 0 : 8192, position
=== "static" ? 0 : 4096,
535 512, 256, 1024, 512, 2048, 1024, position
,
536 position
!== "fixed" && "documentElement" );
539 function getExpectations( htmlPos
, bodyPos
, scrollTop
, scrollLeft
) {
541 // Initialize data about page elements
543 "documentElement": htmlProps( htmlPos
),
544 "body": bodyProps( bodyPos
),
545 "relative": divProps( "relative", "body" ),
546 "relative-relative": divProps( "relative", "relative" ),
547 "relative-absolute": divProps( "absolute", "relative" ),
548 "absolute": divProps( "absolute", "body" ),
549 "absolute-relative": divProps( "relative", "absolute" ),
550 "absolute-absolute": divProps( "absolute", "absolute" ),
551 "fixed": divProps( "fixed" ),
552 "fixed-relative": divProps( "relative", "fixed" ),
553 "fixed-absolute": divProps( "absolute", "fixed" )
556 // Define position and offset expectations for page elements
557 supportjQuery
.each( expectations
, function( id
, props
) {
558 var parent
= expectations
[ props
.parent
],
560 // position() relates an element's margin box to its offset parent's padding box
566 // offset() relates an element's border box to the document origin
567 offset
= props
.offset
= {
568 top
: pos
.top
+ props
.marginTop
,
569 left
: pos
.left
+ props
.marginLeft
572 // Account for ancestors differently by element position
573 // fixed: ignore them
574 // absolute: offset includes offsetParent offset+border
575 // relative: position includes parent padding (and also position+margin+border when
576 // parent is not offsetParent); offset includes parent offset+border+padding
577 // static: same as relative
578 for ( ; parent
; parent
= expectations
[ parent
.parent
] ) {
581 if ( props
.style
=== "fixed" ) {
585 // position:absolute bypass
586 if ( props
.style
=== "absolute" && parent
.style
=== "static" ) {
591 offset
.top
+= parent
.offset
.top
+ parent
.borderTop
;
592 offset
.left
+= parent
.offset
.left
+ parent
.borderLeft
;
593 if ( props
.style
!== "absolute" ) {
594 offset
.top
+= parent
.paddingTop
;
595 offset
.left
+= parent
.paddingLeft
;
597 // position:relative or position:static position update
598 pos
.top
+= parent
.paddingTop
;
599 pos
.left
+= parent
.paddingLeft
;
600 if ( parent
.style
=== "static" ) {
601 pos
.top
+= parent
.pos
.top
+ parent
.marginTop
+ parent
.borderTop
;
602 pos
.left
+= parent
.pos
.left
+ parent
.marginLeft
+ parent
.borderLeft
;
608 // Viewport scroll affects position:fixed elements, except when the page is
610 if ( props
.style
=== "fixed" &&
611 ( alwaysScrollable
|| expectations
.documentElement
.style
!== "fixed" ) ) {
613 offset
.top
+= scrollTop
;
614 offset
.left
+= scrollLeft
;
621 // Cover each combination of <html> position and <body> position
622 supportjQuery
.each( POSITION_VALUES
, function( _
, htmlPos
) {
623 supportjQuery
.each( POSITION_VALUES
, function( _
, bodyPos
) {
624 var label
= "nonzero box properties - html." + htmlPos
+ " body." + bodyPos
;
625 testIframe( label
, "offset/boxes.html", function( assert
, $, win
, doc
) {
627 // Define expectations at runtime to properly account for scrolling
628 var scrollTop
= win
.pageYOffset
,
629 scrollLeft
= win
.pageXOffset
,
630 expectations
= getExpectations( htmlPos
, bodyPos
, scrollTop
, scrollLeft
);
632 assert
.expect( 3 * Object
.keys( expectations
).length
);
634 // Setup documentElement and body styles, preserving scroll position
635 doc
.documentElement
.style
.position
= htmlPos
;
636 doc
.body
.style
.position
= bodyPos
;
637 win
.scrollTo( scrollLeft
, scrollTop
);
639 // Verify expected document offset
640 supportjQuery
.each( expectations
, function( id
, descriptor
) {
642 supportjQuery
.extend( {}, $( "#" + id
).offset() ),
644 "jQuery('#" + id
+ "').offset(): top " + descriptor
.offset
.top
+
645 ", left " + descriptor
.offset
.left
);
648 // Verify expected relative position
649 supportjQuery
.each( expectations
, function( id
, descriptor
) {
651 supportjQuery
.extend( {}, $( "#" + id
).position() ),
653 "jQuery('#" + id
+ "').position(): top " + descriptor
.pos
.top
+
654 ", left " + descriptor
.pos
.left
);
657 // Verify that values round-trip
658 supportjQuery
.each( Object
.keys( expectations
).reverse(), function( _
, id
) {
659 var $el
= $( "#" + id
),
660 pos
= supportjQuery
.extend( {}, $el
.position() );
662 $el
.css( { top
: pos
.top
, left
: pos
.left
} );
663 if ( $el
.css( "position" ) === "relative" ) {
665 // $relative.position() includes parent padding; switch to absolute
666 // positioning so we don't double its effects.
667 $el
.css( { position
: "absolute" } );
669 assert
.deepEqual( supportjQuery
.extend( {}, $el
.position() ), pos
,
670 "jQuery('#" + id
+ "').position() round-trips" );
672 // TODO Verify .offset(...)
673 // assert.deepEqual( $el.offset( offset ).offset(), offset )
674 // assert.deepEqual( $el.offset( adjustedOffset ).offset(), adjustedOffset )
675 // assert.deepEqual( $new.offset( offset ).offset(), offset )
682 QUnit
.test( "offsetParent", function( assert
) {
685 var body
, header
, div
, area
;
687 body
= jQuery( "body" ).offsetParent();
688 assert
.equal( body
.length
, 1, "Only one offsetParent found." );
689 assert
.equal( body
[ 0 ], document
.documentElement
, "The html element is the offsetParent of the body." );
691 header
= jQuery( "#qunit" ).offsetParent();
692 assert
.equal( header
.length
, 1, "Only one offsetParent found." );
693 assert
.equal( header
[ 0 ], document
.documentElement
, "The html element is the offsetParent of #qunit." );
695 jQuery( "#qunit-fixture" ).css( "position", "absolute" );
696 div
= jQuery( "#nothiddendivchild" ).offsetParent();
697 assert
.equal( div
.length
, 1, "Only one offsetParent found." );
698 assert
.equal( div
[ 0 ], document
.getElementById( "qunit-fixture" ), "The #qunit-fixture is the offsetParent of #nothiddendivchild." );
699 jQuery( "#qunit-fixture" ).css( "position", "" );
701 jQuery( "#nothiddendiv" ).css( "position", "relative" );
703 div
= jQuery( "#nothiddendivchild" ).offsetParent();
704 assert
.equal( div
.length
, 1, "Only one offsetParent found." );
705 assert
.equal( div
[ 0 ], jQuery( "#nothiddendiv" )[ 0 ], "The div is the offsetParent." );
707 div
= jQuery( "body, #nothiddendivchild" ).offsetParent();
708 assert
.equal( div
.length
, 2, "Two offsetParent found." );
709 assert
.equal( div
[ 0 ], document
.documentElement
, "The html element is the offsetParent of the body." );
710 assert
.equal( div
[ 1 ], jQuery( "#nothiddendiv" )[ 0 ], "The div is the offsetParent." );
712 area
= jQuery( "<map name=\"imgmap\"><area shape=\"rect\" coords=\"0,0,200,50\"></map>" ).appendTo( "body" ).find( "area" );
713 assert
.equal( area
.offsetParent()[ 0 ], document
.documentElement
, "The html element is the offsetParent of a map area." );
716 div
= jQuery( "<div>" ).css( { "position": "absolute" } ).appendTo( "body" );
717 assert
.equal( div
.offsetParent()[ 0 ], document
.documentElement
, "Absolutely positioned div returns html as offset parent, see trac-12139" );
721 QUnit
.test( "fractions (see trac-7730 and trac-7885)", function( assert
) {
724 jQuery( "body" ).append( "<div id='fractions'></div>" );
727 expected
= { "top": 1000, "left": 1000 },
728 div
= jQuery( "#fractions" );
731 "position": "absolute",
732 "left": "1000.7432222px",
733 "top": "1000.532325px",
738 div
.offset( expected
);
740 result
= div
.offset();
742 // Support: Chrome <=45 - 73+
743 // In recent Chrome these values differ a little.
744 assert
.ok( Math
.abs( result
.top
- expected
.top
) < 0.25, "Check top within 0.25 of expected" );
745 assert
.ok( Math
.abs( result
.left
- expected
.left
) < 0.25, "Check left within 0.25 of expected" );
750 QUnit
.test( "iframe scrollTop/Left (see gh-1945)", function( assert
) {
753 var ifDoc
= jQuery( "#iframe" )[ 0 ].contentDocument
;
755 // Support: iOS <=8 - 12+
756 // Mobile Safari resizes the iframe by its content meaning it's not possible to scroll
757 // the iframe but only its parent element.
758 if ( /iphone os|ipad/i.test( navigator
.userAgent
) ) {
759 assert
.equal( true, true, "Can't scroll iframes in this environment" );
760 assert
.equal( true, true, "Can't scroll iframes in this environment" );
764 // Tests scrollTop/Left with iframes
765 jQuery( "#iframe" ).css( "width", "50px" ).css( "height", "50px" );
766 ifDoc
.write( "<div style='width: 1000px; height: 1000px;'></div>" );
768 jQuery( ifDoc
).scrollTop( 200 );
769 jQuery( ifDoc
).scrollLeft( 500 );
771 assert
.equal( jQuery( ifDoc
).scrollTop(), 200, "$($('#iframe')[0].contentDocument).scrollTop()" );
772 assert
.equal( jQuery( ifDoc
).scrollLeft(), 500, "$($('#iframe')[0].contentDocument).scrollLeft()" );