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