3 * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
4 * Dual licensed under MIT and GPL.
7 * @projectDescription Easy element scrolling using jQuery.
8 * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
9 * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP.
11 * @author Ariel Flesler
15 * @id jQuery.fn.scrollTo
16 * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements.
17 * The different options for target are:
18 * - A number position (will be applied to all axes).
19 * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes
20 * - A jQuery/DOM element ( logically, child of the element to scroll )
21 * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc )
22 * - A hash { top:x, left:y }, x and y can be any kind of number/string like above.
23 * - A percentage of the container's dimension/s, for example: 50% to go to the middle.
24 * - The string 'max' for go-to-end.
25 * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead.
26 * @param {Object,Function} settings Optional set of settings or the onAfter callback.
27 * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'.
28 * @option {Number} duration The OVERALL length of the animation.
29 * @option {String} easing The easing method for the animation.
30 * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position.
31 * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }.
32 * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes.
33 * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends.
34 * @option {Function} onAfter Function to be called after the scrolling ends.
35 * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends.
36 * @return {jQuery} Returns the same jQuery object, for chaining.
38 * @desc Scroll to a fixed position
39 * @example $('div').scrollTo( 340 );
41 * @desc Scroll relatively to the actual position
42 * @example $('div').scrollTo( '+=340px', { axis:'y' } );
44 * @dec Scroll using a selector (relative to the scrolled element)
45 * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } );
47 * @ Scroll to a DOM element (same for jQuery object)
48 * @example var second_child = document.getElementById('container').firstChild.nextSibling;
49 * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){
50 * alert('scrolled!!');
53 * @desc Scroll on both axes, to different values
54 * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } );
58 var $scrollTo
= $.scrollTo = function( target
, duration
, settings
){
59 $(window
).scrollTo( target
, duration
, settings
);
62 $scrollTo
.defaults
= {
64 duration
: parseFloat($.fn
.jquery
) >= 1.3 ? 0 : 1
67 // Returns the element that needs to be animated to scroll the window.
68 // Kept for backwards compatibility (specially for localScroll & serialScroll)
69 $scrollTo
.window = function( scope
){
70 return $(window
)._scrollable();
73 // Hack, hack, hack :)
74 // Returns the real elements to scroll (supports window/iframes, documents and regular nodes)
75 $.fn
._scrollable = function(){
76 return this.map(function(){
78 isWin
= !elem
.nodeName
|| $.inArray( elem
.nodeName
.toLowerCase(), ['iframe','#document','html','body'] ) != -1;
83 var doc
= (elem
.contentWindow
|| elem
).document
|| elem
.ownerDocument
|| elem
;
85 return $.browser
.safari
|| doc
.compatMode
== 'BackCompat' ?
91 $.fn
.scrollTo = function( target
, duration
, settings
){
92 if( typeof duration
== 'object' ){
96 if( typeof settings
== 'function' )
97 settings
= { onAfter
:settings
};
102 settings
= $.extend( {}, $scrollTo
.defaults
, settings
);
103 // Speed is still recognized for backwards compatibility
104 duration
= duration
|| settings
.speed
|| settings
.duration
;
105 // Make sure the settings are given right
106 settings
.queue
= settings
.queue
&& settings
.axis
.length
> 1;
109 // Let's keep the overall duration
111 settings
.offset
= both( settings
.offset
);
112 settings
.over
= both( settings
.over
);
114 return this._scrollable().each(function(){
117 targ
= target
, toff
, attr
= {},
118 win
= $elem
.is('html,body');
120 switch( typeof targ
){
121 // A number will pass the regex
124 if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ
) ){
129 // Relative selector, no break!
132 // DOMElement / jQuery
133 if( targ
.is
|| targ
.style
)
134 // Get the real position of the target
135 toff
= (targ
= $(targ
)).offset();
137 $.each( settings
.axis
.split(''), function( i
, axis
){
138 var Pos
= axis
== 'x' ? 'Left' : 'Top',
139 pos
= Pos
.toLowerCase(),
140 key
= 'scroll' + Pos
,
142 max
= $scrollTo
.max(elem
, axis
);
144 if( toff
){// jQuery / DOMElement
145 attr
[key
] = toff
[pos
] + ( win
? 0 : old
- $elem
.offset()[pos
] );
147 // If it's a dom element, reduce the margin
148 if( settings
.margin
){
149 attr
[key
] -= parseInt(targ
.css('margin'+Pos
)) || 0;
150 attr
[key
] -= parseInt(targ
.css('border'+Pos
+'Width')) || 0;
153 attr
[key
] += settings
.offset
[pos
] || 0;
155 if( settings
.over
[pos
] )
156 // Scroll to a fraction of its width/height
157 attr
[key
] += targ
[axis
=='x'?'width':'height']() * settings
.over
[pos
];
160 // Handle percentage values
161 attr
[key
] = val
.slice
&& val
.slice(-1) == '%' ?
162 parseFloat(val
) / 100 * max
166 // Number or 'number'
167 if( /^\d+$/.test(attr
[key
]) )
169 attr
[key
] = attr
[key
] <= 0 ? 0 : Math
.min( attr
[key
], max
);
172 if( !i
&& settings
.queue
){
173 // Don't waste time animating, if there's no need.
174 if( old
!= attr
[key
] )
175 // Intermediate animation
176 animate( settings
.onAfterFirst
);
177 // Don't animate this axis again in the next iteration.
182 animate( settings
.onAfter
);
184 function animate( callback
){
185 $elem
.animate( attr
, duration
, settings
.easing
, callback
&& function(){
186 callback
.call(this, target
, settings
);
193 // Max scrolling position, works on quirks mode
194 // It only fails (not too badly) on IE, quirks mode.
195 $scrollTo
.max = function( elem
, axis
){
196 var Dim
= axis
== 'x' ? 'Width' : 'Height',
197 scroll
= 'scroll'+Dim
;
199 if( !$(elem
).is('html,body') )
200 return elem
[scroll
] - $(elem
)[Dim
.toLowerCase()]();
202 var size
= 'client' + Dim
,
203 html
= elem
.ownerDocument
.documentElement
,
204 body
= elem
.ownerDocument
.body
;
206 return Math
.max( html
[scroll
], body
[scroll
] )
207 - Math
.min( html
[size
] , body
[size
] );
211 function both( val
){
212 return typeof val
== 'object' ? val
: { top
:val
, left
:val
};