Fixed an issue with setting offset of absolutely positioned element that has no posit...
[jquery.git] / test / unit / offset.js
1 module("offset");
2
3 testoffset("absolute"/* in iframe */, function($, iframe) {
4         expect(4);
5         
6         var doc = iframe.document, tests;
7         
8         // force a scroll value on the main window
9         // this insures that the results will be wrong
10         // if the offset method is using the scroll offset
11         // of the parent window
12         var forceScroll = jQuery('<div>', { width: 2000, height: 2000 }).appendTo('body');
13         window.scrollTo(1, 1);
14         
15         // get offset
16         tests = [
17                 { id: '#absolute-1', top: 1, left: 1 }
18         ];
19         jQuery.each( tests, function() {
20                 equals( jQuery( this.id, doc ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
21                 equals( jQuery( this.id, doc ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
22         });
23
24
25         // get position
26         tests = [
27                 { id: '#absolute-1', top: 0, left: 0 }
28         ];
29         jQuery.each( tests, function() {
30                 equals( jQuery( this.id, doc ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
31                 equals( jQuery( this.id, doc ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
32         });
33         
34         forceScroll.remove();
35 });
36
37 testoffset("absolute", function( jQuery ) {
38         expect(178);
39         
40         // get offset tests
41         var tests = [
42                 { id: '#absolute-1',     top:  1, left:  1 }, 
43                 { id: '#absolute-1-1',   top:  5, left:  5 },
44                 { id: '#absolute-1-1-1', top:  9, left:  9 },
45                 { id: '#absolute-2',     top: 20, left: 20 }
46         ];
47         jQuery.each( tests, function() {
48                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
49                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
50         });
51         
52         
53         // get position
54         tests = [
55                 { id: '#absolute-1',     top:  0, left:  0 },
56                 { id: '#absolute-1-1',   top:  1, left:  1 },
57                 { id: '#absolute-1-1-1', top:  1, left:  1 },
58                 { id: '#absolute-2',     top: 19, left: 19 }
59         ];
60         jQuery.each( tests, function() {
61                 equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
62                 equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
63         });
64         
65         // test #5781
66         var offset = jQuery( '#positionTest' ).offset({ top: 10, left: 10 }).offset();
67         equals( offset.top,  10, "Setting offset on element with position absolute but 'auto' values." )
68         equals( offset.left, 10, "Setting offset on element with position absolute but 'auto' values." )
69         
70         
71         // set offset
72         tests = [
73                 { id: '#absolute-2',     top: 30, left: 30 },
74                 { id: '#absolute-2',     top: 10, left: 10 },
75                 { id: '#absolute-2',     top: -1, left: -1 },
76                 { id: '#absolute-2',     top: 19, left: 19 },
77                 { id: '#absolute-1-1-1', top: 15, left: 15 },
78                 { id: '#absolute-1-1-1', top:  5, left:  5 },
79                 { id: '#absolute-1-1-1', top: -1, left: -1 },
80                 { id: '#absolute-1-1-1', top:  9, left:  9 },
81                 { id: '#absolute-1-1',   top: 10, left: 10 },
82                 { id: '#absolute-1-1',   top:  0, left:  0 },
83                 { id: '#absolute-1-1',   top: -1, left: -1 },
84                 { id: '#absolute-1-1',   top:  5, left:  5 },
85                 { id: '#absolute-1',     top:  2, left:  2 },
86                 { id: '#absolute-1',     top:  0, left:  0 },
87                 { id: '#absolute-1',     top: -1, left: -1 },
88                 { id: '#absolute-1',     top:  1, left:  1 }
89         ];
90         jQuery.each( tests, function() {
91                 jQuery( this.id ).offset({ top: this.top, left: this.left });
92                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
93                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
94                 
95                 var top = this.top, left = this.left;
96                 
97                 jQuery( this.id ).offset(function(i, val){
98                         equals( val.top, top, "Verify incoming top position." );
99                         equals( val.left, left, "Verify incoming top position." );
100                         return { top: top + 1, left: left + 1 };
101                 });
102                 equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + " })" );
103                 equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + " })" );
104                 
105                 jQuery( this.id )
106                         .offset({ left: this.left + 2 })
107                         .offset({ top:  this.top  + 2 });
108                 equals( jQuery( this.id ).offset().top,  this.top  + 2, "Setting one property at a time." );
109                 equals( jQuery( this.id ).offset().left, this.left + 2, "Setting one property at a time." );
110                 
111                 jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
112                         jQuery( this ).css({
113                                 top:  props.top  + 1,
114                                 left: props.left + 1
115                         });
116                 }});
117                 equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + ", using: fn })" );
118                 equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + ", using: fn })" );
119         });
120 });
121
122 testoffset("relative", function( jQuery ) {
123         expect(60);
124         
125         // IE is collapsing the top margin of 1px
126         var ie = jQuery.browser.msie && parseInt( jQuery.browser.version, 10 ) < 8;
127         
128         // get offset
129         var tests = [
130                 { id: '#relative-1',   top: ie ?   6 :   7, left:  7 },
131                 { id: '#relative-1-1', top: ie ?  13 :  15, left: 15 },
132                 { id: '#relative-2',   top: ie ? 141 : 142, left: 27 }
133         ];
134         jQuery.each( tests, function() {
135                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
136                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
137         });
138         
139         
140         // get position
141         tests = [
142                 { id: '#relative-1',   top: ie ?   5 :   6, left:  6 },
143                 { id: '#relative-1-1', top: ie ?   4 :   5, left:  5 },
144                 { id: '#relative-2',   top: ie ? 140 : 141, left: 26 }
145         ];
146         jQuery.each( tests, function() {
147                 equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
148                 equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
149         });
150         
151         
152         // set offset
153         tests = [
154                 { id: '#relative-2',   top: 200, left:  50 },
155                 { id: '#relative-2',   top: 100, left:  10 },
156                 { id: '#relative-2',   top:  -5, left:  -5 },
157                 { id: '#relative-2',   top: 142, left:  27 },
158                 { id: '#relative-1-1', top: 100, left: 100 },
159                 { id: '#relative-1-1', top:   5, left:   5 },
160                 { id: '#relative-1-1', top:  -1, left:  -1 },
161                 { id: '#relative-1-1', top:  15, left:  15 },
162                 { id: '#relative-1',   top: 100, left: 100 },
163                 { id: '#relative-1',   top:   0, left:   0 },
164                 { id: '#relative-1',   top:  -1, left:  -1 },
165                 { id: '#relative-1',   top:   7, left:   7 }
166         ];
167         jQuery.each( tests, function() {
168                 jQuery( this.id ).offset({ top: this.top, left: this.left });
169                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
170                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
171                 
172                 jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
173                         jQuery( this ).css({
174                                 top:  props.top  + 1,
175                                 left: props.left + 1
176                         });
177                 }});
178                 equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + ", using: fn })" );
179                 equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + ", using: fn })" );
180         });
181 });
182
183 testoffset("static", function( jQuery ) {
184         expect(80);
185         
186         // IE is collapsing the top margin of 1px
187         var ie = jQuery.browser.msie && parseInt( jQuery.browser.version, 10 ) < 8;
188         
189         // get offset
190         var tests = [
191                 { id: '#static-1',     top: ie ?   6 :   7, left:  7 },
192                 { id: '#static-1-1',   top: ie ?  13 :  15, left: 15 },
193                 { id: '#static-1-1-1', top: ie ?  20 :  23, left: 23 },
194                 { id: '#static-2',     top: ie ? 121 : 122, left:  7 }
195         ];
196         jQuery.each( tests, function() {
197                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
198                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
199         });
200         
201         
202         // get position
203         tests = [
204                 { id: '#static-1',     top: ie ?   5 :   6, left:  6 },
205                 { id: '#static-1-1',   top: ie ?  12 :  14, left: 14 },
206                 { id: '#static-1-1-1', top: ie ?  19 :  22, left: 22 },
207                 { id: '#static-2',     top: ie ? 120 : 121, left:  6 }
208         ];
209         jQuery.each( tests, function() {
210                 equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.top  + "').position().top" );
211                 equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.left +"').position().left" );
212         });
213         
214         
215         // set offset
216         tests = [
217                 { id: '#static-2',     top: 200, left: 200 },
218                 { id: '#static-2',     top: 100, left: 100 },
219                 { id: '#static-2',     top:  -2, left:  -2 },
220                 { id: '#static-2',     top: 121, left:   6 },
221                 { id: '#static-1-1-1', top:  50, left:  50 },
222                 { id: '#static-1-1-1', top:  10, left:  10 },
223                 { id: '#static-1-1-1', top:  -1, left:  -1 },
224                 { id: '#static-1-1-1', top:  22, left:  22 },
225                 { id: '#static-1-1',   top:  25, left:  25 },
226                 { id: '#static-1-1',   top:  10, left:  10 },
227                 { id: '#static-1-1',   top:  -3, left:  -3 },
228                 { id: '#static-1-1',   top:  14, left:  14 },
229                 { id: '#static-1',     top:  30, left:  30 },
230                 { id: '#static-1',     top:   2, left:   2 },
231                 { id: '#static-1',     top:  -2, left:  -2 },
232                 { id: '#static-1',     top:   7, left:   7 }
233         ];
234         jQuery.each( tests, function() {
235                 jQuery( this.id ).offset({ top: this.top, left: this.left });
236                 equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
237                 equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
238                 
239                 jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
240                         jQuery( this ).css({
241                                 top:  props.top  + 1,
242                                 left: props.left + 1
243                         });
244                 }});
245                 equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + ", using: fn })" );
246                 equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + ", using: fn })" );
247         });
248 });
249
250 testoffset("fixed", function( jQuery ) {
251         expect(28);
252         
253         jQuery.offset.initialize();
254         
255         var tests = [
256                 { id: '#fixed-1', top: 1001, left: 1001 },
257                 { id: '#fixed-2', top: 1021, left: 1021 }
258         ];
259         jQuery.each( tests, function() {
260                 if ( jQuery.offset.supportsFixedPosition ) {
261                         equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
262                         equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
263                 } else {
264                         // need to have same number of assertions
265                         ok( true, 'Fixed position is not supported' );
266                         ok( true, 'Fixed position is not supported' );
267                 }
268         });
269         
270         tests = [
271                 { id: '#fixed-1', top: 100, left: 100 },
272                 { id: '#fixed-1', top:   0, left:   0 },
273                 { id: '#fixed-1', top:  -4, left:  -4 },
274                 { id: '#fixed-2', top: 200, left: 200 },
275                 { id: '#fixed-2', top:   0, left:   0 },
276                 { id: '#fixed-2', top:  -5, left:  -5 }
277         ];
278         
279         jQuery.each( tests, function() {
280                 if ( jQuery.offset.supportsFixedPosition ) {
281                         jQuery( this.id ).offset({ top: this.top, left: this.left });
282                         equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
283                         equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
284                 
285                         jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
286                                 jQuery( this ).css({
287                                         top:  props.top  + 1,
288                                         left: props.left + 1
289                                 });
290                         }});
291                         equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + ", using: fn })" );
292                         equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + ", using: fn })" );
293                 } else {
294                         // need to have same number of assertions
295                         ok( true, 'Fixed position is not supported' );
296                         ok( true, 'Fixed position is not supported' );
297                         ok( true, 'Fixed position is not supported' );
298                         ok( true, 'Fixed position is not supported' );
299                 }
300         });
301 });
302
303 testoffset("table", function( jQuery ) {
304         expect(4);
305         
306         equals( jQuery('#table-1').offset().top, 6, "jQuery('#table-1').offset().top" );
307         equals( jQuery('#table-1').offset().left, 6, "jQuery('#table-1').offset().left" );
308         
309         equals( jQuery('#th-1').offset().top, 10, "jQuery('#th-1').offset().top" );
310         equals( jQuery('#th-1').offset().left, 10, "jQuery('#th-1').offset().left" );
311 });
312
313 testoffset("scroll", function( jQuery, win ) {
314         expect(16);
315         
316         var ie = jQuery.browser.msie && parseInt( jQuery.browser.version, 10 ) < 8;
317         
318         // IE is collapsing the top margin of 1px
319         equals( jQuery('#scroll-1').offset().top, ie ? 6 : 7, "jQuery('#scroll-1').offset().top" );
320         equals( jQuery('#scroll-1').offset().left, 7, "jQuery('#scroll-1').offset().left" );
321         
322         // IE is collapsing the top margin of 1px
323         equals( jQuery('#scroll-1-1').offset().top, ie ? 9 : 11, "jQuery('#scroll-1-1').offset().top" );
324         equals( jQuery('#scroll-1-1').offset().left, 11, "jQuery('#scroll-1-1').offset().left" );
325         
326         
327         // scroll offset tests .scrollTop/Left
328         equals( jQuery('#scroll-1').scrollTop(), 5, "jQuery('#scroll-1').scrollTop()" );
329         equals( jQuery('#scroll-1').scrollLeft(), 5, "jQuery('#scroll-1').scrollLeft()" );
330         
331         equals( jQuery('#scroll-1-1').scrollTop(), 0, "jQuery('#scroll-1-1').scrollTop()" );
332         equals( jQuery('#scroll-1-1').scrollLeft(), 0, "jQuery('#scroll-1-1').scrollLeft()" );
333         
334         // equals( jQuery('body').scrollTop(), 0, "jQuery('body').scrollTop()" );
335         // equals( jQuery('body').scrollLeft(), 0, "jQuery('body').scrollTop()" );
336         
337         win.name = "test";
338         
339         equals( jQuery(win).scrollTop(), 1000, "jQuery(window).scrollTop()" );
340         equals( jQuery(win).scrollLeft(), 1000, "jQuery(window).scrollLeft()" );
341         
342         equals( jQuery(win.document).scrollTop(), 1000, "jQuery(document).scrollTop()" );
343         equals( jQuery(win.document).scrollLeft(), 1000, "jQuery(document).scrollLeft()" );
344         
345         // test jQuery using parent window/document
346         // jQuery reference here is in the iframe
347         window.scrollTo(0,0);
348         equals( jQuery(window).scrollTop(), 0, "jQuery(window).scrollTop() other window" );
349         equals( jQuery(window).scrollLeft(), 0, "jQuery(window).scrollLeft() other window" );
350         equals( jQuery(document).scrollTop(), 0, "jQuery(window).scrollTop() other document" );
351         equals( jQuery(document).scrollLeft(), 0, "jQuery(window).scrollLeft() other document" );
352 });
353
354 testoffset("body", function( jQuery ) {
355         expect(2);
356         
357         equals( jQuery('body').offset().top, 1, "jQuery('#body').offset().top" );
358         equals( jQuery('body').offset().left, 1, "jQuery('#body').offset().left" );
359 });
360
361 test("Chaining offset(coords) returns jQuery object", function() {
362         expect(2);
363         var coords = { top:  1, left:  1 };
364         equals( jQuery("#absolute-1").offset(coords).selector, "#absolute-1", "offset(coords) returns jQuery object" );
365         equals( jQuery("#non-existent").offset(coords).selector, "#non-existent", "offset(coords) with empty jQuery set returns jQuery object" );
366 });
367
368 test("offsetParent", function(){
369         expect(11);
370
371         var body = jQuery("body").offsetParent();
372         equals( body.length, 1, "Only one offsetParent found." );
373         equals( body[0], document.body, "The body is its own offsetParent." );
374
375         var header = jQuery("#qunit-header").offsetParent();
376         equals( header.length, 1, "Only one offsetParent found." );
377         equals( header[0], document.body, "The body is the offsetParent." );
378
379         var div = jQuery("#nothiddendivchild").offsetParent();
380         equals( div.length, 1, "Only one offsetParent found." );
381         equals( div[0], document.body, "The body is the offsetParent." );
382
383         jQuery("#nothiddendiv").css("position", "relative");
384
385         div = jQuery("#nothiddendivchild").offsetParent();
386         equals( div.length, 1, "Only one offsetParent found." );
387         equals( div[0], jQuery("#nothiddendiv")[0], "The div is the offsetParent." );
388
389         div = jQuery("body, #nothiddendivchild").offsetParent();
390         equals( div.length, 2, "Two offsetParent found." );
391         equals( div[0], document.body, "The body is the offsetParent." );
392         equals( div[1], jQuery("#nothiddendiv")[0], "The div is the offsetParent." );
393 });
394
395 function testoffset(name, fn) {
396         
397         test(name, function() {
398                 // pause execution for now
399                 stop();
400                 
401                 // load fixture in iframe
402                 var iframe = loadFixture(),
403                         win = iframe.contentWindow,
404                         interval = setInterval( function() {
405                                 if ( win && win.jQuery && win.jQuery.isReady ) {
406                                         clearInterval( interval );
407                                         // continue
408                                         start();
409                                         // call actual tests passing the correct jQuery isntance to use
410                                         fn.call( this, win.jQuery, win );
411                                         document.body.removeChild( iframe );
412                                         iframe = null;
413                                 }
414                         }, 15 );
415         });
416         
417         function loadFixture() {
418                 var src = './data/offset/' + name + '.html?' + parseInt( Math.random()*1000, 10 ),
419                         iframe = jQuery('<iframe />').css({
420                                 width: 500, height: 500, position: 'absolute', top: -600, left: -600, visiblity: 'hidden'
421                         }).appendTo('body')[0];
422                 iframe.contentWindow.location = src;
423                 return iframe;
424         }
425 }