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