diff --git a/src/ng/anchorScroll.js b/src/ng/anchorScroll.js
index baaac2f3fda0..5efdfcbb9f20 100644
--- a/src/ng/anchorScroll.js
+++ b/src/ng/anchorScroll.js
@@ -73,7 +73,7 @@ function $AnchorScrollProvider() {
@@ -102,6 +102,37 @@ function $AnchorScrollProvider() {
margin-top: 2000px;
}
+
+ function _isElemVisible() {
+ var elem = document.getElementById(arguments[0]);
+ var rect = elem.getBoundingClientRect();
+ var docElem = document.documentElement;
+ return (rect.top < docElem.clientHeight) &&
+ (rect.bottom > 0) &&
+ (rect.left < docElem.clientWidth) &&
+ (rect.right > 0);
+ }
+
+ function expectVisible(id, expected) {
+ browser.driver.executeScript(_isElemVisible, id).then(function(isVisible) {
+ expect(isVisible).toBe(expected);
+ });
+ }
+
+ function scrollToTop() {
+ browser.driver.executeScript('window.scrollTo(0, 0);');
+ }
+
+ it('should scroll to #bottom upon clicking #top', function() {
+ scrollToTop();
+ expectVisible('top', true);
+ expectVisible('bottom', false);
+
+ element(by.id('top')).click();
+ expectVisible('top', false);
+ expectVisible('bottom', true);
+ });
+
*
*
@@ -112,18 +143,19 @@ function $AnchorScrollProvider() {
-
- Anchor {{x}} of 5
+
+ Anchor {{$index + 1}} of 5
angular.module('anchorScrollOffsetExample', [])
.run(['$anchorScroll', function($anchorScroll) {
- $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
+ // scroll with a 50px offset from the top of the viewport
+ $anchorScroll.yOffset = 50;
}])
.controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
function ($anchorScroll, $location, $scope) {
@@ -144,6 +176,8 @@ function $AnchorScrollProvider() {
body {
+ height: 100%;
+ margin: 0;
padding-top: 50px;
}
@@ -164,6 +198,153 @@ function $AnchorScrollProvider() {
margin: 5px 15px;
}
+
+ function _isElemVisible() {
+ var elem = document.getElementById(arguments[0]);
+ var rect = elem.getBoundingClientRect();
+ var docElem = document.documentElement;
+ return (rect.top < docElem.clientHeight) &&
+ (rect.bottom > 0) &&
+ (rect.left < docElem.clientWidth) &&
+ (rect.right > 0);
+ }
+
+ function _getElemTop() {
+ var elem = document.getElementById(arguments[0]);
+ var rect = elem.getBoundingClientRect();
+ return rect.top;
+ }
+
+ function _getViewportHeight() {
+ return window.document.documentElement.clientHeight;
+ }
+
+ function _scrollElemIntoView() {
+ var elem = document.getElementById(arguments[0]);
+ elem.scrollIntoView();
+ }
+
+ function execWithTempViewportHeight(tempHeight, fn) {
+ setViewportHeight(tempHeight).then(function(oldHeight) {
+ fn();
+ setViewportHeight(oldHeight);
+ });
+ }
+
+ function execWithTempHash(tempHash, fn) {
+ browser.driver.getCurrentUrl().then(function(oldUrl) {
+ var newUrl = oldUrl + '#/#' + tempHash;
+ browser.get(newUrl);
+ fn();
+ browser.get(oldUrl);
+ });
+ }
+
+ function expectVisible(id, expected) {
+ browser.driver.executeScript(_isElemVisible, id).then(function(isVisible) {
+ expect(isVisible).toBe(expected);
+ });
+ }
+
+ function expectTop(id, expected) {
+ browser.driver.executeScript(_getElemTop, id).then(function(top) {
+ expect(top).toBe(expected);
+ });
+ }
+
+
+ function scrollIntoView(id) {
+ browser.driver.executeScript(_scrollElemIntoView, id);
+ }
+
+ function scrollTo(y) {
+ browser.driver.executeScript('window.scrollTo(0, ' + y + ');');
+ }
+
+ function scrollToTop() {
+ scrollTo(0);
+ }
+
+ function setViewportHeight(newHeight) {
+ return browser.driver.executeScript(_getViewportHeight).then(function(oldHeight) {
+ var heightDiff = newHeight - oldHeight;
+ var win = browser.driver.manage().window();
+
+ return win.getSize().then(function(size) {
+ var newWinHeight = size.height + heightDiff;
+
+ return win.setSize(size.width, newWinHeight).then(function() {
+ return oldHeight;
+ });
+ });
+ });
+ }
+
+ describe('scrolling with 50px offset', function() {
+ var yOffset = 50;
+
+ beforeEach(function() {
+ scrollToTop();
+ expectVisible('anchor0', true);
+ expectTop('anchor0', yOffset);
+ });
+
+ it('should scroll to the correct anchor when clicking each link', function() {
+ var links = element.all(by.repeater('x in [0,1,2,3,4]'));
+ var lastAnchor = element.all(by.repeater('y in [0,1,2,3,4]')).last();
+
+ // Make sure there is enough room to scroll the last anchor to the top
+ lastAnchor.getSize().then(function(size) {
+ var tempHeight = size.height - 10;
+ execWithTempViewportHeight(tempHeight, function() {
+ var idx = 0;
+ links.each(function(link) {
+ var targetAnchorId = 'anchor' + idx;
+ link.click();
+ expectVisible(targetAnchorId, true);
+ expectTop(targetAnchorId, yOffset);
+ idx++;
+ });
+ });
+ });
+ });
+
+ it('should automatically scroll when navigating to a URL with a hash', function() {
+ var links = element.all(by.repeater('x in [0,1,2,3,4]'));
+ var lastAnchor = element.all(by.repeater('y in [0,1,2,3,4]')).last();
+ var targetAnchorId = 'anchor2';
+
+ // Make sure there is enough room to scroll the last anchor to the top
+ lastAnchor.getSize().then(function(size) {
+ var tempHeight = size.height - 10;
+ execWithTempViewportHeight(tempHeight, function() {
+ execWithTempHash(targetAnchorId, function() {
+ expectVisible(targetAnchorId, true);
+ expectTop(targetAnchorId, yOffset);
+ });
+ });
+ });
+ });
+
+ it('should not scroll "overzealously"', function () {
+ var lastLink = element.all(by.repeater('x in [0,1,2,3,4]')).last();
+ var lastAnchor = element.all(by.repeater('y in [0,1,2,3,4]')).last();
+ var targetAnchorId = 'anchor4';
+
+ // Make sure there is not enough room to scroll the last anchor to the top
+ lastAnchor.getSize().then(function(size) {
+ var tempHeight = size.height + (yOffset / 2);
+ execWithTempViewportHeight(tempHeight, function() {
+ scrollIntoView(targetAnchorId);
+ expectTop(targetAnchorId, yOffset / 2);
+ lastLink.click();
+ expectVisible(targetAnchorId, true);
+ expectTop(targetAnchorId, yOffset);
+ });
+ });
+ });
+ });
+
*/
this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {