Most all modern browsers contain the functionality to open a page at a certain anchor element on the page when loaded with a hash element in the link. For example, a link to http://www.mysite.com/#super-element would open the page and display the element with the id=”super-element” attribute. I recently had a request to load the page and gently scroll to the element after the page had fully loaded to show that there was more content on the page.
Unfortunately there is no way to override how the browser treats the hash element but we can modify the hash element to an id that does not exist on the page. For example, if we link to http://www.mysite.com/#_super-element then nothing at all would happen. With this in mind we can create an autoscrolling object that will scroll to an element on the current page or modify the outbound link to add an underscore to the hash before redirecting. This is a great solution for single page websites or sectional websites with long pages where scrolling is beneficial to the user experience.
jQuery(function($){ $('.hash-link a').on('click', scroller.hashLinkClicked); scroller.loaded(); }); (function($){ scroller = { topScrollOffset: -84, scrollTiming: 1000, pageLoadScrollDelay: 1000, hashLinkClicked: function(e){ // current path var temp = window.location.pathname.split('#'); var curPath = scroller.addTrailingSlash(temp[0]); // target path var link = $(this).attr('href'); var linkArray = link.split('#'); var navId = (typeof linkArray[1] !== 'undefined') ? linkArray[1] : null; var targetPath = scroller.addTrailingSlash(linkArray[0]); // scrollTo the hash id if the target is on the same page if (targetPath == curPath && navId) { e.preventDefault(); scroller.scrollToElement('#'+navId); window.location.hash = scroller.generateTempNavId(navId); // otherwise add '_' to hash } else if (navId) { e.preventDefault(); navId = scroller.generateTempNavId(navId); window.location = targetPath+'#'+navId; } }, addTrailingSlash: function(str){ lastChar = str.substring(str.length-1, str.length); if (lastChar != '/') str = str+'/'; return str; }, scrollToElement: function(whereTo){ $.scrollTo(whereTo, scroller.scrollTiming, { offset: { top: scroller.topScrollOffset }, easing: 'easeInOutQuart' }); }, generateTempNavId: function(navId){ return '_'+navId; }, getNavIdFromHash: function(){ var hash = window.location.hash; if (scroller.hashIsTempNavId()) { hash = hash.substring(2); } return hash; }, hashIsTempNavId: function(){ var hash = window.location.hash; return hash.substring(0,2) === '#_'; }, loaded: function(){ if (scroller.hashIsTempNavId()) { setTimeout(function(){scroller.scrollToElement('#'+scroller.getNavIdFromHash());},scroller.pageLoadScrollDelay); } } }; })(jQuery);
This will scroll to the hash element on the current page or load a new page with an underscore in the hash element. On page load the scroller.loaded function will then scroll to the element on the current page if it exists. I am using the jQuery scrollTo plugin but you could easily replace the scroller.scrollToElement() function to look something like this for a similar effect:
scrollToElement: function(whereTo){ $('html, body').animate({ scrollTop: $(whereTo).offset().top }, scroller.scrollTiming); },
Hi there,
Thank you for the script. It almost worked;)
I had to change “a few” things to make it work, e.g. line 16 (from “pathname” to “href”). I prefer functional programming in JS. The code uses Vanilla JS + Velocity.js for animation + stopDef helper (from http://javascript.about.com/od/byexample/a/events-preventdefault-example.htm). You can find the whole script below.
For simplicity I got rid of all the code for addingEventListeners for each link click so if anybody wants to use it properly they should configure them and change the first line of code.
var hashLinks = document.querySelectorAll('.hashLink')[0],
hash = window.location.hash;
function smoothScroll(e){
var targetEl = e.target;
// make current path
var tempPath = window.location.href.split('#'),
curPath = addTrailingSlash(tempPath[0]),
// make target path
link = targetEl.getAttribute('href'),
linkArray = link.split('#'),
hookID = (typeof linkArray[1] !== 'undefined') ? linkArray[1] : null,
targetPath = addTrailingSlash(linkArray[0]);
// scroll to the hash id if the target is on the same page
if (targetPath == curPath && hookID) {
qqStopDef(e,false);
scrollToElement(hookID);
// otherwise add '_' to hash
} else if (hookID) {
qqStopDef(e,false);
window.location = targetPath+'#_'+hookID;
}
}
function addTrailingSlash (str){
lastChar = str.substring(str.length-1, str.length);
if (lastChar != '/') str = str+'/';
return str;
}
function scrollToElement (hookID){
Velocity(
document.getElementById(hookID),
'scroll'
, {
duration: 300,
complete: function(){
window.location.hash = '#'+hookID;
}
}
);
}
if (hash.substring(0,2) === '#_') {
var hookID = hash.substring(2);
setTimeout(
function(){
scrollToElement(hookID);
},
1000
);
}
Hi, Im kinda new to javascript.
Could you make this script so it animates the scroll for like a second, and also make it so that it scrolls to the element – 100px?
Or explain to me how I can change these settings by myself?
Is it possible to remove the # and all after it, after performing the action?
@Jens: you can remove the hash with
location.hash = “”;