Skip to content

Commit

Permalink
feat($ionicScrollDelegate): add .anchorScroll() function
Browse files Browse the repository at this point in the history
Scrolls to the location of element with id matching $location.hash(). If
$location.hash() is blank or the id does not exist, it will scroll to
the top.
  • Loading branch information
ajoslin committed Feb 9, 2014
1 parent a970f0b commit c2bbd9e
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 10 deletions.
38 changes: 29 additions & 9 deletions js/ext/angular/src/service/delegates/ionicScrollDelegate.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

angular.module('ionic.ui.service.scrollDelegate', [])

.factory('$ionicScrollDelegate', ['$rootScope', '$timeout', '$q', function($rootScope, $timeout, $q) {
.factory('$ionicScrollDelegate', ['$rootScope', '$timeout', '$q', '$anchorScroll', '$location', '$document', function($rootScope, $timeout, $q, $anchorScroll, $location, $document) {
return {
/**
* Trigger a scroll-to-top event on child scrollers.
Expand All @@ -17,6 +17,9 @@ angular.module('ionic.ui.service.scrollDelegate', [])
resize: function() {
$rootScope.$broadcast('scroll.resize');
},
anchorScroll: function() {
$rootScope.$broadcast('scroll.anchorScroll');
},
tapScrollToTop: function(element) {
var _this = this;

Expand Down Expand Up @@ -47,29 +50,46 @@ angular.module('ionic.ui.service.scrollDelegate', [])
* $scope {Scope} the scope to register and listen for events
*/
register: function($scope, $element) {
//Get scroll controller from parent
var scrollCtrl = $element.controller('$ionicScroll');
if (!scrollCtrl) {
return;
}
var scrollView = scrollCtrl.scrollView;
var scrollEl = scrollCtrl.element;

function scrollViewResize() {
// Run the resize after this digest
return $timeout(function() {
$scope.$parent.scrollView && $scope.$parent.scrollView.resize();
scrollView.resize();
});
}

$element.bind('scroll', function(e) {
$scope.onScroll({
$scope.onScroll && $scope.onScroll({
event: e,
scrollTop: e.detail ? e.detail.scrollTop : e.originalEvent ? e.originalEvent.detail.scrollTop : 0,
scrollLeft: e.detail ? e.detail.scrollLeft: e.originalEvent ? e.originalEvent.detail.scrollLeft : 0
});
});

$scope.$parent.$on('scroll.resize', function(e) {
scrollViewResize();
});
$scope.$parent.$on('scroll.resize', scrollViewResize);

// Called to stop refreshing on the scroll view
$scope.$parent.$on('scroll.refreshComplete', function(e) {
$scope.$parent.scrollView && $scope.$parent.scrollView.finishPullToRefresh();
scrollView.finishPullToRefresh();
});

$scope.$parent.$on('scroll.anchorScroll', function() {
var hash = $location.hash();
var elm;
//If there are multiple with this id, go to first one
if (hash && (elm = $document.body.querySelectorAll('#' + hash)[0])) {
var scroll = ionic.DomUtil.getPositionInParent(elm, scrollEl);
scrollView.scrollTo(scroll.left, scroll.top);
} else {
scrollView.scrollTo(0,0);
}
});

/**
Expand All @@ -79,12 +99,12 @@ angular.module('ionic.ui.service.scrollDelegate', [])
*/
$scope.$parent.$on('scroll.scrollTop', function(e, animate) {
scrollViewResize().then(function() {
$scope.$parent.scrollView && $scope.$parent.scrollView.scrollTo(0, 0, animate === false ? false : true);
scrollView.scrollTo(0, 0, animate === false ? false : true);
});
});
$scope.$parent.$on('scroll.scrollBottom', function(e, animate) {
scrollViewResize().then(function() {
var sv = $scope.$parent.scrollView;
var sv = scrollView;
if (sv) {
var max = sv.getScrollMax();
sv.scrollTo(0, max.top, animate === false ? false : true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('Ionic ScrollDelegate Service', function() {
expect(sv.getValues().top).toBe(0);
});

it('Should resize & scroll top', function() {
it('Should resize & scroll bottom', function() {
var scope = rootScope.$new();
var el = compile('<content start-y="100"></content>')(scope);

Expand Down Expand Up @@ -83,3 +83,76 @@ describe('Ionic ScrollDelegate Service', function() {
});
});

describe('anchorScroll', function() {
function setLocationHash(hash) {
inject(function($location) {
$location.hash = function() { return hash; };
});
}

beforeEach(module('ionic'));

var contentEl, scope, del;
beforeEach(inject(function($rootScope, $compile, $timeout, $document, $ionicScrollDelegate) {
scope = $rootScope.$new();
contentEl = $compile('<content></content>')(scope);

mockBody = angular.element('<div>').append(contentEl);
$document.body = mockBody[0];
del = $ionicScrollDelegate
}));

it('should anchorScroll to an element with id', function() {
var anchorMe = angular.element('<div id="anchorMe">');
var sv = del.getScrollView(scope);
spyOn(sv, 'scrollTo');

setLocationHash('anchorMe');
contentEl.append(anchorMe);

var pos = ionic.DomUtil.getPositionInParent(anchorMe[0], contentEl[0]);
del.anchorScroll();
expect(sv.scrollTo).toHaveBeenCalledWith(pos.left, pos.top);
});

it('should anchorScroll to top if !$location.hash()', function() {
var sv = del.getScrollView(scope);
spyOn(sv, 'scrollTo');
del.anchorScroll();
expect(sv.scrollTo).toHaveBeenCalledWith(0, 0);
});

it('should anchorScroll to top if element with hash id doesnt exist', function() {
var sv = del.getScrollView(scope);
spyOn(sv, 'scrollTo');

setLocationHash('doesnotexist');
del.anchorScroll();

expect(sv.scrollTo).toHaveBeenCalledWith(0, 0);
});

it('should anchorScroll to first element with id if multiple exist', function() {
var foo1 = angular.element('<div id="foo">hello</div>');
var foo2 = angular.element('<div id="foo">hola</div>');
var sv = del.getScrollView(scope);

contentEl.append(foo1).append(foo2);

//Fake the top/left because dom doesn't have time to load in a test
spyOn(ionic.DomUtil, 'getPositionInParent').andCallFake(function(el) {
return el === foo1[0] ? {left: 20, top: 40} : {left: 30, top: 50};
});
var pos1 = ionic.DomUtil.getPositionInParent(foo1[0], contentEl[0]);
var pos2 = ionic.DomUtil.getPositionInParent(foo2[0], contentEl[0]);

spyOn(sv, 'scrollTo');
setLocationHash('foo');
del.anchorScroll();
expect(sv.scrollTo.callCount).toBe(1);
expect(sv.scrollTo).toHaveBeenCalledWith(pos1.left, pos1.top);
expect(sv.scrollTo).not.toHaveBeenCalledWith(pos2.left, pos2.top);
});

});

0 comments on commit c2bbd9e

Please sign in to comment.