Skip to content

Instantly share code, notes, and snippets.

@CMCDragonkai
Last active December 31, 2015 22:49
Show Gist options
  • Save CMCDragonkai/8055961 to your computer and use it in GitHub Desktop.
Save CMCDragonkai/8055961 to your computer and use it in GitHub Desktop.
JS: AngularJS Asynchronous Anchor Scroll - There are 3 ways of doing hash scrolling in AngularJS, put a hash into the URL via links, put a hash onto the URL via the $location API, or using $anchorScroll. All three ways rely on the URL having the hash component. The problem comes when the resource you want to scroll is loaded asynchronously, that…
app.directive('asyncAnchor', [
'$location',
'$anchorScroll',
'$timeout',
function($location, $anchorScroll, $timeout){
return {
link: function(scope, element, attributes){
var id = attributes.asyncAnchor || attributes.id || attributes.name;
var delay = attributes.asyncAnchorDelay;
var eventName = attributes.asyncAnchorEvent;
var firstTimeScrolling = true;
var scrollToHash = function(hash){
if(id && hash && id === hash){
if(delay && firstTimeScrolling){
$timeout(function(){
$anchorScroll();
}, delay);
}else{
$anchorScroll();
}
//only run the delay the first time this scrolling function executes
//if the hash didn't match, then this function didn't execute!
firstTimeScrolling = false;
}
};
//listen for a custom event, useful if you're waiting on something else to be fully loaded as well
if(eventName){
scope.$on(eventName, function(){
scrollToHash($location.hash());
});
}
//hash may be asynchronously changed, the directive may load before the hash is added
scope.$watch(function(){
return $location.hash();
}, function(hash){
scrollToHash(hash);
});
}
};
}
]);
@joshualinog
Copy link

do you mind offering up an example of usage on the html? i employed angular outside of the SPA approach. i am navigating from one page to another and i am reaching the requested internally linked element id, but the issue is just as you've discussed: asynchronous loading of assets. in my case it's the xhr requested templates for custom dropbox elements coming after the navigation occurs. thus, the page scrolls down to the appropriate element, but as the template comes later, mid scroll the page flickers and jolts as the templates are loaded in, forcing the originally requested page location to now be off in terms off by the amount of pixels of the asynchronously loaded elements above.

@CMCDragonkai
Copy link
Author

The best way to resolve that problem is to use the resolve property in the routes to load everything before the page is transitioned. Check my blog http://polycademy.com/blog/id/147/preloading_page_content_like_youtube_using_angularjs for more info on that.

A quicker solution is to use the delay or use the event listener to listen for an event that you broadcast when the content is loaded. This is a bit hacky and it will cause a flicker, but I used on a site to quickly resolve the issue.

@CMCDragonkai
Copy link
Author

Updated to make the delay only occur on the first time the hash scrolling executes. It shouldn't delay every time. The purpose of the delay was to allow other things to occur before the first time you scroll. No point in delaying the scroll every time.

@mlenser
Copy link

mlenser commented Oct 13, 2014

If anyone stumbles across this at a later point: AngularJS has this built in.

Add autoscroll="true" on your ng-view element and the page will scroll for you when the page loads (even with async content - I resolve that in the routing if it maters)

@litera
Copy link

litera commented Mar 26, 2015

@mlenser of course it matters. If you don't load your data using route resolve and load view model when controller i instatiated, then it's already too late for the $anchorScroll which will do that immediately when the view is being displayed (which is before you asynchronously load your data)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment