Build: Bump the github-actions group across 1 directory with 2 updates
[jquery.git] / test / unit / offset.js
blob5cece84f41d8ce7907c0e2f0b224cfb8837e7b89
1 ( function() {
3 if ( !includesModule( "offset" ) ) {
4 return;
7 var alwaysScrollable,
8 forceScroll = supportjQuery( "<div></div>" ).css( { width: 2000, height: 2000 } ),
9 checkSupport = function( assert ) {
11 // Only run once
12 checkSupport = false;
14 // Append forceScroll to the body instead of #qunit-fixture because the latter is hidden
15 forceScroll.appendTo( "body" );
16 window.scrollTo( 200, 200 );
17 forceScroll.detach();
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();
23 testIframe(
24 null,
25 "offset/boxes.html",
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;
32 done();
34 function mockQUnit_test( _, testCallback ) {
35 setTimeout( function() {
36 testCallback( assert );
37 } );
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 );
51 forceScroll.detach();
52 }, afterEach: moduleTeardown } );
54 QUnit.test( "empty set", function( assert ) {
55 assert.expect( 2 );
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)" );
58 } );
60 QUnit.test( "disconnected element", function( assert ) {
61 assert.expect( 4 );
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)" );
72 } );
74 QUnit.test( "hidden (display: none) element", function( assert ) {
75 assert.expect( 4 );
77 var node = jQuery( "<div style='display: none'></div>" ).appendTo( "#qunit-fixture" ),
78 result = node.offset();
80 node.remove();
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)" );
89 } );
91 QUnit.test( "0 sized element", function( assert ) {
92 assert.expect( 4 );
94 var node = jQuery( "<div style='margin: 5px; width: 0; height: 0'></div>" ).appendTo( "#qunit-fixture" ),
95 result = node.offset();
97 node.remove();
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)" );
103 } );
105 QUnit.test( "hidden (visibility: hidden) element", function( assert ) {
106 assert.expect( 4 );
108 var node = jQuery( "<div style='margin: 5px; visibility: hidden'></div>" ).appendTo( "#qunit-fixture" ),
109 result = node.offset();
111 node.remove();
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)" );
117 } );
119 QUnit.test( "normal element", function( assert ) {
120 assert.expect( 4 );
122 var node = jQuery( "<div>" ).appendTo( "#qunit-fixture" ),
123 offset = node.offset(),
124 position = node.position();
126 node.remove();
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)" );
133 } );
135 testIframe( "absolute", "offset/absolute.html", function( assert, $, iframe ) {
136 assert.expect( 4 );
138 var doc = iframe.document,
139 tests;
141 // get offset
142 tests = [
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" );
148 } );
150 // get position
151 tests = [
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" );
157 } );
158 } );
160 testIframe( "absolute", "offset/absolute.html", function( assert, $ ) {
161 assert.expect( 178 );
163 var tests, offset;
165 // get offset tests
166 tests = [
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" );
175 } );
177 // get position
178 tests = [
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" );
187 } );
189 // test trac-5781
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." );
194 // set offset
195 tests = [
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 };
224 } );
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 ) + " })" );
228 $( this.id )
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 ) {
235 $( this ).css( {
236 "top": props.top + 1,
237 "left": props.left + 1
238 } );
239 } } );
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 })" );
242 } );
243 } );
245 testIframe( "relative", "offset/relative.html", function( assert, $ ) {
246 assert.expect( 64 );
248 // get offset
249 var tests = [
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" );
258 } );
260 // get position
261 tests = [
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" );
270 } );
272 // set offset
273 tests = [
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 ) {
293 $( this ).css( {
294 "top": props.top + 1,
295 "left": props.left + 1
296 } );
297 } } );
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 })" );
300 } );
301 } );
303 testIframe( "static", "offset/static.html", function( assert, $ ) {
304 assert.expect( 80 );
306 // get offset
307 var tests = [
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" );
316 } );
318 // get position
319 tests = [
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" );
328 } );
330 // set offset
331 tests = [
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 ) {
355 $( this ).css( {
356 "top": props.top + 1,
357 "left": props.left + 1
358 } );
359 } } );
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 })" );
362 } );
363 } );
365 testIframe( "fixed", "offset/fixed.html", function( assert, $ ) {
366 assert.expect( 38 );
368 var tests, $noTopLeft;
370 tests = [
372 "id": "#fixed-1",
373 "offsetTop": 1001,
374 "offsetLeft": 1001,
375 "positionTop": 0,
376 "positionLeft": 0
379 "id": "#fixed-2",
380 "offsetTop": 1021,
381 "offsetLeft": 1021,
382 "positionTop": 20,
383 "positionLeft": 20
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" );
394 } );
396 tests = [
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 ) {
411 $( this ).css( {
412 "top": props.top + 1,
413 "left": props.left + 1
414 } );
415 } } );
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 })" );
418 } );
420 // Bug 8316
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" );
425 } );
427 testIframe( "table", "offset/table.html", function( assert, $ ) {
428 assert.expect( 6 );
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" );
438 } );
440 testIframe( "scroll", "offset/scroll.html", function( assert, $, win ) {
441 assert.expect( 26 );
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)" );
466 win.name = "test";
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" );
489 } );
491 testIframe( "body", "offset/body.html", function( assert, $ ) {
492 assert.expect( 4 );
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" );
498 } );
500 QUnit.test( "chaining", function( assert ) {
501 assert.expect( 3 );
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)" );
507 } );
509 // Test complex content under a variety of <html>/<body> positioning styles
510 ( function() {
511 var POSITION_VALUES = [ "static", "relative", "absolute", "fixed" ],
513 // Use shorthands for describing an element's relevant properties
514 BOX_PROPS =
515 ( "top left marginTop marginLeft borderTop borderLeft paddingTop paddingLeft" +
516 " style parent" ).split( /\s+/g ),
517 props = function() {
518 var propObj = {};
519 supportjQuery.each( arguments, function( i, value ) {
520 propObj[ BOX_PROPS[ i ] ] = value;
521 } );
522 return propObj;
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
542 var expectations = {
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
561 pos = props.pos = {
562 top: props.top,
563 left: props.left
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 ] ) {
580 // position:fixed
581 if ( props.style === "fixed" ) {
582 break;
585 // position:absolute bypass
586 if ( props.style === "absolute" && parent.style === "static" ) {
587 continue;
590 // Offset update
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;
605 break;
608 // Viewport scroll affects position:fixed elements, except when the page is
609 // unscrollable.
610 if ( props.style === "fixed" &&
611 ( alwaysScrollable || expectations.documentElement.style !== "fixed" ) ) {
613 offset.top += scrollTop;
614 offset.left += scrollLeft;
616 } );
618 return expectations;
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 ) {
641 assert.deepEqual(
642 supportjQuery.extend( {}, $( "#" + id ).offset() ),
643 descriptor.offset,
644 "jQuery('#" + id + "').offset(): top " + descriptor.offset.top +
645 ", left " + descriptor.offset.left );
646 } );
648 // Verify expected relative position
649 supportjQuery.each( expectations, function( id, descriptor ) {
650 assert.deepEqual(
651 supportjQuery.extend( {}, $( "#" + id ).position() ),
652 descriptor.pos,
653 "jQuery('#" + id + "').position(): top " + descriptor.pos.top +
654 ", left " + descriptor.pos.left );
655 } );
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 )
676 } );
677 } );
678 } );
679 } );
680 } )();
682 QUnit.test( "offsetParent", function( assert ) {
683 assert.expect( 13 );
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." );
714 area.remove();
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" );
718 div.remove();
719 } );
721 QUnit.test( "fractions (see trac-7730 and trac-7885)", function( assert ) {
722 assert.expect( 2 );
724 jQuery( "body" ).append( "<div id='fractions'></div>" );
726 var result,
727 expected = { "top": 1000, "left": 1000 },
728 div = jQuery( "#fractions" );
730 div.css( {
731 "position": "absolute",
732 "left": "1000.7432222px",
733 "top": "1000.532325px",
734 "width": 100,
735 "height": 100
736 } );
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" );
747 div.remove();
748 } );
750 QUnit.test( "iframe scrollTop/Left (see gh-1945)", function( assert ) {
751 assert.expect( 2 );
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" );
762 } else {
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()" );
774 } );
776 } )();