mad4milk archive
scroll your internal links smoothly
December 04 2005, 14:03:00
Notice: This article now use obsolete code, as it has been replaced by mootools. Go get it now.
A “quick post” on how to scroll your internal links smoothly with moo.fx, using normal anchors.
1. The Effect
So, I’ve created a basic effect, to smoothly scroll from an object’s position to another.
fx.Scroll = Class.create();
fx.Scroll.prototype = Object.extend(new fx.Base(), {
initialize: function(options) {
this.setOptions(options);
},
scrollTo: function(el){
var desty = Position.cumulativeOffset($(el));
var dest = desty[1];
var client = window.innerHeight || document.documentElement.clientHeight;
var full = document.documentElement.scrollHeight;
var top = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
if (dest+client > full) this.custom(top, dest - client + (full-dest));
else this.custom(top, dest);
},
increase: function(){
window.scrollTo(0, this.now);
}
});
note: I’ve taken advantage of the prototype function cumulativeOffset here. If you want to test this effect, and you’re using prototype.lite from moo.fx download, make sure you add this at the end:
var Position = {
cumulativeOffset: function(element) {
var valueT = 0, valueL = 0;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return [valueL, valueT];
}
}
This does not apply if you’re using the full prototype.js.
2. Gettin all your links
Now I’ve got a nice scroll effect. But it’s pretty useless, I want to use html anchors, or my users without javascript will not be taken to the part of the page I want. Plus, I don’t want to change anything on the html. Just use existing anchors.
This is the “class” I’ve came up with:
var ScrollLinks = {
currentHash: false,
start: function(){
this.scroll = new fx.Scroll({duration: 800, onComplete: function(){ScrollLinks.end();}});
this.allinks = document.getElementsByTagName('a');
for (i=0; i<this.allinks.length; i++){
var lnk = this.allinks[i];
if ((lnk.href && lnk.href.indexOf('#') != -1) && ( (lnk.pathname == location.pathname) || ('/'+lnk.pathname == location.pathname) ) && (lnk.search == location.search)) {
lnk.onclick = function(){
ScrollLinks.scroll.clearTimer();
this.initialHref = this.href;
this.initialHash = this.hash;
this.href = "javascript:void(0)";
setTimeout(function(){this.href = this.initialHref;}.bind(this), 200);
ScrollLinks.click(this);
}
}
}
},
click: function(link){
this.currentHash = link.initialHash.substr(1);
if (this.currentHash) {
for (j=0; j<this.allinks.length; j++){
if (this.allinks[j].id == this.currentHash){
if (!window.opera) this.scroll.scrollTo(this.allinks[j]);
else this.scroll.scrollTo(this.allinks[j].parentNode);
break;
}
}
}
},
end: function(){
window.location.href = "#"+this.currentHash;
this.currentHash = false;
}
}
It takes care of everyting: creates the effect, and scroll the page in a smooth way when you click on an internal link.
Also the usage is pretty basic:
window.onload = function(){
ScrollLinks.start();
}
Opera has some troubles finding the position of an anchor, I guess that’s because it’s empty, but I’m not sure. So in opera the window scrolls to the anchor’s parentNode. Also, Safari seems to behave strangely when I manually set window.location.href.
Do let me know if you find a better solution. When these two nasty things are fixed, this will probably be a part of moo.fx.pack.
P.s. To see this effect in action just click back to top or add a comment.