Thursday, October 18, 2007

jQuery.ScrollTo

Notice

I've pretty much stopped updating this blog, but the plugin development is still on-going. You can find the link to the Github project page at the bottom of the article.

Introduction

An article about animated scrolling with jQuery inspired me to make a small, customizable plugin for scrolling elements, or the window itself.

How to specify what to scroll ?

Simple, all the matched elements will be scrolled, for example:
$('div.pane').scrollTo(...);//all divs w/class pane
If you need to scroll the window (screen), then use:
$.scrollTo(...);//the plugin will take care of this

How to specify where ?

There are many different ways to specify the target position.
These are:
  • A raw number
  • A string('44', '100px', '+=30px', etc )
  • A DOM element (logically, child of the scrollable element)
  • A selector, that will be relative to the scrollable element
  • The string 'max' to scroll to the end.
  • A string specifying a percentage to scroll to that part of the container (f.e: 50% goes to to the middle).
  • A hash { top:x, left:y }, x and y can be any kind of number/string like above.
Note that you only need to use the hash form, if you are scrolling on both axes, and they have different positions.
Don't use the hash to scroll on both axes. Instead, keep reading :)

Settings

The plugin offers you many options to customize the animation and also the final position.
The settings are:
  • axis: Axes to be scrolled, 'x', 'y', 'xy' or 'yx'. 'xy' is the default.
  • duration: Length of the animation, none (sync) by default.
  • easing: Name of the easing equation.
  • margin: If true, target's border and margin are deducted.
  • offset: Number or hash {left: x, top:y }. This will be added to the final position(can be negative).
  • over: Add a fraction of the element's height/width (can also be negative).
  • queue: If true and both axes are scrolled, it will animate on one axis, and then on the other. Note that 'axis' being 'xy' or 'yx', concludes the order
  • onAfterFirst: If queing, a function to be called after scrolling the first axis.
  • onAfter: A function to be called after the whole animation ended.
These settings are very well explained in the demo. Make sure to check it to understand them all.

Getting the real scrollable element out of a node

In order to find the real element whose attributes will be animated, you need to call $.fn._scrollable. The underscore has been added to avoid conflicts with other jQuery plugins. Here's an example:

$(window)._scrollable(); // When scrolling the window

Manually finding the scrolling limit

ScrollTo always had an internal function that calculates the scrolling limit for both axes. Since 1.4.2, this function is exposed as $.scrollTo.max.

It's not too nice to use yet but it's probably not something you'll need, you must pass two arguments: a jQuery object and an axis string ('x' or 'y') and it will return the max number.


Overloading

This plugin accepts the arguments in two ways, like $.animate().
$(...).scrollTo( target, duration, settings );
$(...).scrollTo( target, settings );
In this second case, you can specify the duration in the hash. You don't need to include any setting, not even the duration, everything has defaults.
The hash of defaults can be accessed at: $.scrollTo.defaults.

jQuery.ScrollTo's sons

Indeed, jQuery.ScrollTo has offspring :)
  • jQuery.LocalScroll: Will add a scroll effect, to any anchor navigation. Ideal for slides, table of contents, etc. It's small, and incredibly easy to implement.
  • jQuery.SerialScroll: It's a multi-purpose plugin to animate any kind of sequential navigation(prev/next). It's also small and highly customizable.
These plugins require jQuery.ScrollTo and can use its settings!.
That makes around 20 options to fully customize each of them.
They are wrappers for common situations where you might need this plugin.
Using them will save you a lot of time and will give you even more customization.
They can be safely used simultaneously and the 3 of them minified, take merely 3.5kb altogether!

Troubleshooting

Doesn't scroll on IE
Sometimes, you need to set a position (relative or absolute) to the container and give it fixed dimensions, in order to hide the overflow.
If this doesn't work, try giving the container fixed dimensions (height & width).

Links

55 comments:

Anonymous said...

Can this plugin be made to default override the anchor scrollto behavior of the browser?

would be nice if you just put scrollTo(); in a script header and voila... smooth scrolling for all named anchor links.

Flesler said...

Indeed, check the article I mentioned at the beginning of the post: http://www.learningjquery.com/2007/09/animated-scrolling-with-jquery-12

I adapted that code, to work with the plugin. Check the links up there, just add the js and it will do what you asked for.

eshu said...

thx for this nice plugin...
i was thinking of some adjustment..
im js novice so it would be nice of you to help me with this one..

first of all i was thinking about is: can you please show me how to get a links form your examples (ie. instead of puting the numbers in box and hitting the scroll button i have a clickable links like 1., 2.,3., etc so i can put them in form of unordered list or so...?)

i would like to have something like unordered list from your example, so i can click them and then that takes me to some part of a ´page´

what i have in mind, as a bottom line, is to mix this horizontal and vertical scrolling functionality in one...like having a navigation bar with subnavigation list.. Main navigation bars having a vertical scrolling capabilities and subnavigation one horizontal..
i hope you understand what i just asked :-))

thx!

Flesler said...

There, I turned it into a plugin..
Check the demo, I tried to make more or less what you were asking for...

http://flesler.blogspot.com/2007/10/jquerylocalscroll-10.html

Flesler said...

Thanks :) the 2nd and 3rd release were small bug fixes. Usually happens I guess. I'm glad you like it :)

I really like jQuery.ScrollTo and it's very useful for other plugins like LocalScroll. I'm working on a slideshow using ScrollTo. I'll upload soon.

Sterling said...

I'll look forward to it!

bawb said...

This is beautiful. Thank you.

Marcus said...

Hi,

Question for you. Do you have any idea what would be involved in making the area scroll based on mouse movement, rather than via links? Kind of like what MooTools shows here (but using JQuery instead): http://demos.mootools.net/Scroller

Any info would be excellent. Thanks.

Ariel Flesler said...

@bawb
  Thanks!

@marcus
  LocalScroll supports different events, the setting named 'event' can be changed to 'mouseover' for example.

If what you want, is not using anchors... then you just bind to some element's mousemove/mouseover and you make a call to jQuery.ScrollTo inside it.
If you plan to use mousemove, then you should limit the calls, I think it can be done using the plugin 'hoverIntent' of jQuery.
There was also a plugin called auto-scroll I think, maybe that can solve it too.

Cheers

ap said...

Hi Ariel, great plugin! I don't know if this is at all possible, but how could I make the page scroll when coming from another page via a deep link?

Thanks!

Ariel Flesler said...

Hi ap
You mean, when the url has a hash (#abcdef) ?
If that's the case, check LocalScroll, in this blog, it has a function $.localScroll.hash() to check that, and autoscroll.

Cheers

Pistos said...

$( 'something' ).scrollTo() works fine, but $.scrollTo() appears broken: Bug demo page

Ariel Flesler said...

Hi Pistos

Thanks for having a demo right away, I tried it and indeed, it didn't work.
I tried adding a doctype, and the problem was gone. This is some wicked issue of the browser.

Try adding one, right before the <html>.
HERE you have many. I'd advice you to use XHTML Transitional.

Cheers

Pistos said...

Okay, that helps for Firefox, but then it breaks Opera. But that's okay for our purposes, we can ignore Opera breakage.

Thank you for the fast and helpful response.

thegreycoin said...

Great plugin, very useful stuff...

I was wondering, however, if it's possible to use this plugin for dynamic content. I'm kind of a newbie at jQuery, so forgive me if my question sounds silly... but for example, say I'm creating a chat-like web app with large amounts of continuously updated content: so it's a fixed-size div with dynamic scrolling height that increases every time content is added. I basically just want a way to keep the focus on the bottom of the content in the div, so the user is always viewing the most recently appended content.

I have been able to do this using straight-up Javascript by taking advantage of the scrollHeight and scrollTop properties. However, I would like to integrate this with other jQuery animations and whatnot that I'm using, and have been having a lot of trouble.

Is there an easy way of doing this with this plugin? or with any other plugin you know of? I've scoured the web for the past two days looking for a solution to this problem, but have come up empty.

Thanks a ton!

Ariel Flesler said...

Hi

This plugin can handle that situation. It makes all the calculations, each time you call it, so dynamic content is just fine.

There's no specific method to scroll to the end.
But if you scroll over the bottom limit, it will make your number shorter.
That means that:

$('#pane').scrollTo( 99999, 1000 );

should work just fine, make that number as high as you want.

Håvard said...

hmm.. doesen't get the negative over value to work.. would be nice to have it scroll down to about 200px over the content i want to show.

you can see the stuff im working on at the url

Ariel Flesler said...

Hi Håvard

You can use negative 'over'.
What you can't do (browser restriction) is to scroll over thel limits, so you can't go above the start.

If you want a fixed margin (200px you said), you can use 'offset' instead.

Håvard said...

Thanks a lot.! nice plugin!! :D

andy said...

Ariel,

What I was saying is that SerialScroller works well if you've got a div with 20 images in it.

What I've got instead is a div with a bunch of random html inside of it, which I want to scroll through 900px at a time. As I understood SerialScroller, you have to specify your "items" which you want to scroll through. I don't have a regular list of "items" in my code.

Since my scrolling isn't item-based but rather distance-based (like 900px at a time), I didn't think it was applicable to me.

Ariel Flesler said...

Right, got it.
That's easy. You do:

$('#prev').click(function(){
  $('#pane').scrollTo('-=900');
});

That's to scroll left or up. Use += for right/bottom.
Of course you can specify speed, and any other setting.

Anonymous said...

Brilliant! This plug-in has saved me 3 days work ;)

If I was going to make a suggestion (beg for a feature) I'd split the 'easing' into two, so that you can have an 'easeIn' and 'easeOut' - That would make it really smart :)

Ariel Flesler said...

Hi

The easing setting is just passed on to jQuery.animate() so I won't be changing much of that.

If you haven't yet, you should check the easing plugin.

Anonymous said...

Hi,
I am using this
where 'bo' is tbody and table is the table and rowid is id which i pass dynamically.

$('#bo').($("#table #"+rowId));

This is not working in IE. Can any one help me on this.
_thanks in advance

Ariel Flesler said...

Scrolling tbodys isn't possible on IE.
I think there are some hacky alternatives out there...

Dan G. Switzer, II said...

@Ariel:

Any tips for preventing scrolling if the element is already in the viewport? I'd prefer not to do the scrolling if the element is already visible.

Ariel Flesler said...

Hi Dan

Try something like this:
http://flesler.pastebin.com/f6fd4efc9

You probably want only the vertical part.
The code I gave you only handles "below the fold".
That is, it doesn't check whether the element is actually above the fold.
You'll surely figure it out if you need that ;)

Dan G. Switzer, II said...

@Ariel:

I've got some code from another project I wrote, that I may just isolate into a plug-in of itself. While $.ScrollTo works great, it really doesn't have the behavior I was looking for. Technically, I want to make sure the element is in the viewport. So if it's already completely visible, I don't want to scroll at all. If only a small portion of the element is hidden, I just want to scroll to get the element fully visible.

Ariel Flesler said...

Dan

You can make that a formal plugin, that uses $.scrollTo (if you want).

Zachary Abresch said...

I love this plug-in. It allowed us to implement an interface idea without having to do the heavy lifting. I have one question though. When using the $(container).trigger('goto',[0]); is there a way to use a different duration for that scroll? I want the scroll to be super quick when going to the start but I use a duration:2000 for normal next and previous scrolling.

Ariel Flesler said...

Hi Zachary

That is actually SerialScroll, this plugin (ScrollTo) is used underneath.
Now the answer: you can't, but you'll probably get the desired effect if you set the option 'constant' to false.

alan said...

Really cool plug in. Even javascript ignorant persons like me can use it quite easy. The only problem that i cant get past is that scrolling whole window with double axis seems not to work. It only scrolls window to x or y axis (whichever is last in axis config) and then stops. Is it bug or am i just too damn stupid?

Ariel Flesler said...

Can you provide an online demo of the problem ?

alan said...

There it is: http://www.frukt.ee/public/alan/html/index.html

Thats the page where i learn and test javascript and jqueri

buildakicker said...

Hi Ariel,

Thanks for putting this up. I have been playing with it for a while. I have a question:

can I have it set up so that I can have it scroll to one place then to click on a link and have it scroll to another without going to the .eq(0) element first? Just fluid like?

Thanks!

Ariel Flesler said...

Hi Buildakicker

I'm not sure I'm getting this well. Are you talking about the behavior from the demo ?
In the demo, I added a call to a reset function, that returns to start. That's there just for the demo.
If you call $().scrollTo() normally it will jump from one place to the other.

Eric Corriel said...

I'm having trouble getting scrollTo to work going horizontally on divs where style="position:relative;float:left". This page illustrates what specifically isn't working:

http://www.ericcorriel.com/test/test.scroll4.html

Any help would be greatly appreciated. Thank you.

Håvard Fossli said...

hi. great plugin. works very nicely in camino, fx etc.
but it's not working in opera and safari.

could you take a look at this?

without hash:true
http://alltidmuligheter.no/dev/scrollto.html

with hash:true
http://alltidmuligheter.no/dev/scrolltowhashtrue.html

it's aligning to the right and stuff in opera and safari and also going to the beginning every time.

i used position:absolute; on the elements. also tried with float:left margin and stuff..

any ideas what i'm doing wrong (if i am)??

Sexfilms said...

hello,

Was wondering what i would need to run scrollTo, with the next thing.

I have a box just like in the example i have images in it with width and height set to 100% and now i want them to scroll so when i press the link for photo 1 that foto scrolls in the foto div. etc etc

Ariel Flesler said...

@eric
The scrolled element needs to have overflow (auto|hidden|visible) and fixed dimensions (width|height).

@sexfilms
You should use LocalScroll, linking by href (#id).
Or SerialScroll, using the option 'navigation' which relates by index.

Both plugins are available and documented on this blog, check the demos.

@havard

It's working fine for me on Opera 9.25. You can't scroll the window beyond the limits, so the last element cannot be left aligned unless you enlarge the body.

Eric Corriel said...

Thank you Ariel. You are clearly a positive force on the internet.

Ariel Flesler said...

Thanks a bunch for donation Eric!! much appreciated :)

iKindred said...

Ariel: Felicitaciones por este plugin fabuloso que hiciste, me sorprendió mucho saber que habia sido programado por un compatriota :)

Muchas gracias, Saludos!

Ariel Flesler said...

Muchas gracias :)

Michael said...

Very nice Plugin, but I have difficulties scrolling 2 windows at the same time. I have a small scrollable thumb bar and a large contend div. I have arrows for the small and the big box. I want, that if I scroll the small box, also the big box moves some pixels.

My code:
var $paneTarget = $('#referenzenhaupt');
var $paneTargetb = $('#thumbshaupt');

$('#nachlinks').click(function(){
$paneTarget.scrollTo( {left:'-=738'}, 800, {
onAfter:function(){
$paneTargetb.scrollTo( {left:'-=107'}, 800);
}
});
});


It works, but the OnAfter Funktion moves twice. I would like them to move at the same time, but that did not work for me.

Can somebody give me an advice please.

Ariel Flesler said...

@Michael

Why not just put both calls to scrollTo one after the other ?
The callback being called twice sounds bad, do you have a demo online ? are you using scrollTo 1.4 ?

Michael said...

Hello Ariel,

$('#nachlinks').click(function(){
$paneTarget.stop().scrollTo( {left:'-=738'}, 800 );
$paneTargetb.stop().scrollTo( {left:'-=100'}, 800 );
});

does not work.

I had to remove the onAfter because IE stops working when pressing the button the first time.

How can I get "parallel" scrolling to work?
The demo you can see on www.realmaker.de/referenzen.php

Michael said...

P.S.
Yes I am using scrollTo 1.4.

Ariel Flesler said...

Hi Michael

As the docs/demo say. To scroll horizontally, you need to play with the setting 'axis'. So make it something like this:

$('#nachlinks').click(function(){
$paneTarget.stop().scrollTo( '-=738', 800, {axis:'x'} );
$paneTargetb.stop().scrollTo( '-=100', 800, {axis:'x'} );
return false;
});

Amir Hossein said...

Hello,
Thank you for this aesome plugin.
I have problem with easing setting and in fact it does't work!
For example:
$.scrollTo("#idName",3000,{easing:'elasout'});
The easing setting doesn't work ,despite I added easing pluging in the head tag of my code!
Please tell me why?

Niels Meijer said...

I can not make the plugin work in IE8, also the demo doesn't work for this browser??

I think the click event isn't working. when I use an alert instead of scrolling it does'nt work either.

All other browsers do work fine.
Please help!

jase said...

First of all, you are my hero. I have been trying (unsuccessfully) to code something similar to this, but much much more clunky and miserable. You have saved me months of work at this rate.

I think I have a pretty good understanding of your ajax demo, but I was wondering if it is plausible to bind a single class to all content links and have the script check to see if the link is local or if the content needs to be loaded.

In your current demo for Ajax, you separate the logic between ajax-loading links and "local links" which is terrific for almost everything. I would just like to be able to have a menu with links from 5 different "sections" (as you called them in your LocalScroll Ajax demo" and numerous "subs" in each section..


So the side menu would look like:

SECTION ONE
sub one
sub two
sub three

SECTION TWO
sub one
sub two
sub three

and clicking on the SECTION TWO sub three link would know to load SECTION TWO (even if SECTION ONE was currently loaded) and then display sub three of SECTION TWO?

If I'm not making sense, I apologize. If I have dynamically loaded SECTION ONE and need to provide an inline link to a separate section AND subsection, it would be nice to just have the href indicate the full URL like href="SECTIONONE.html#subthree" and if the locally loaded content is SECTIONONE.html, scroll to #subthree, if not, load SECTIONONE (and before displaying it, set the position to #subthree) and then swap the old content for the new.

I know you wrote this forever ago, and I won't be upset if you can't/don't answer. Just know that you have REALLY helped me out a lot. Thanks either way!

appleLisa said...

Hi,

I have read thru the comments & haven't found anything for the absolute beginner.

with localScroll i tried:
$('#proj').localScroll({
target: '#projectsp1',
axis: 'x',
lazy: 'true',
hash: 'true',
easing: 'easeInOutCirc',

});

each nav item in the HTML has an id. i am trying to horizontally scroll entire pages (one wrapper broken down into many div pages)

I have both localScroll & scrollTo in the head along with jquery easing. unable to get easing to work, either.

could really use some help. Thank you so much!!

zimurg said...

Hello,

I'm building a dynamic page with horizontal scrolling. I used ScroolTo script.

I was wondering how to set the end of scrolling forward. because it is scrolling infinitely.

http://www.magistrala.org/index1.php

tnx,

zi

Angel Outon said...

Ariel, buen día.

I am trying to do do a page using your fantastic script. The idea is to have a 3x3 sections (working fine). My issue and I hop you have the chance to help me with is I like to start in the middle (box 5) instead of the top (box 1)

Is there a way to do this? I tried the following just after doe { in document. ready:

$('#wrapper').scrollTo($('a-inicio').attr('href'), 10);

but this just messed up everything and the console marked an error and no scrolling at all.

Gacias por tu tiempo.