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