Permalink
Browse files

feat($anchorScroll): allow scrolling to a specified element

Add an optional argument to `$anchorScroll()` to enable scrolling to an
anchor element different than that related to the current value of
`$location.hash()`. If the argument is omitted or is not a string,
the value of `$location.hash()` will be used instead.

Closes #4568
Closes #9596
  • Loading branch information...
1 parent 06a9f0a commit 731c8b5e2d01a44aa91f967f1a6acbadb8005a8b @gkalpak gkalpak committed Oct 13, 2014
Showing with 112 additions and 38 deletions.
  1. +10 −5 src/ng/anchorScroll.js
  2. +102 −33 test/ng/anchorScrollSpec.js
@@ -38,9 +38,10 @@ function $AnchorScrollProvider() {
* @requires $rootScope
*
* @description
- * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and
- * scrolls to the related element, according to the rules specified in the
- * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
+ * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
+ * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
+ * in the
+ * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
*
* It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
* match any anchor whenever it changes. This can be disabled by calling
@@ -49,6 +50,9 @@ function $AnchorScrollProvider() {
* Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
* vertical scroll-offset (either fixed or dynamic).
*
+ * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
+ * {@link ng.$location#hash $location.hash()} will be used.
+ *
* @property {(number|function|jqLite)} yOffset
* If set, specifies a vertical scroll-offset. This is often useful when there are fixed
* positioned elements at the top of the page, such as navbars, headers etc.
@@ -232,8 +236,9 @@ function $AnchorScrollProvider() {
}
}
- function scroll() {
- var hash = $location.hash(), elm;
+ function scroll(hash) {
+ hash = isString(hash) ? hash : $location.hash();
+ var elm;
// empty hash, scroll to the top of the page
if (!hash) scrollTo(null);
@@ -23,7 +23,6 @@ describe('$anchorScroll', function() {
};
}
-
function addElements() {
var elements = sliceArgs(arguments);
@@ -49,9 +48,9 @@ describe('$anchorScroll', function() {
};
}
- function callAnchorScroll() {
+ function callAnchorScroll(hash) {
return function($anchorScroll) {
- $anchorScroll();
+ $anchorScroll(hash);
};
}
@@ -141,50 +140,120 @@ describe('$anchorScroll', function() {
beforeEach(createMockWindow());
- it('should scroll to top of the window if empty hash', inject(
- changeHashAndScroll(''),
- expectScrollingToTop));
+ describe('and implicitly using `$location.hash()`', function() {
+
+ it('should scroll to top of the window if empty hash', inject(
+ changeHashAndScroll(''),
+ expectScrollingToTop));
+
+
+ it('should not scroll if hash does not match any element', inject(
+ addElements('id=one', 'id=two'),
+ changeHashAndScroll('non-existing'),
+ expectNoScrolling()));
+
+
+ it('should scroll to anchor element with name', inject(
+ addElements('a name=abc'),
+ changeHashAndScroll('abc'),
+ expectScrollingTo('a name=abc')));
+
+
+ it('should not scroll to other than anchor element with name', inject(
+ addElements('input name=xxl', 'select name=xxl', 'form name=xxl'),
+ changeHashAndScroll('xxl'),
+ expectNoScrolling()));
+
+
+ it('should scroll to anchor even if other element with given name exist', inject(
+ addElements('input name=some', 'a name=some'),
+ changeHashAndScroll('some'),
+ expectScrollingTo('a name=some')));
+
+
+ it('should scroll to element with id with precedence over name', inject(
+ addElements('name=abc', 'id=abc'),
+ changeHashAndScroll('abc'),
+ expectScrollingTo('id=abc')));
- it('should not scroll if hash does not match any element', inject(
- addElements('id=one', 'id=two'),
- changeHashAndScroll('non-existing'),
- expectNoScrolling()));
+ it('should scroll to top if hash == "top" and no matching element', inject(
+ changeHashAndScroll('top'),
+ expectScrollingToTop));
- it('should scroll to anchor element with name', inject(
- addElements('a name=abc'),
- changeHashAndScroll('abc'),
- expectScrollingTo('a name=abc')));
+ it('should scroll to element with id "top" if present', inject(
+ addElements('id=top'),
+ changeHashAndScroll('top'),
+ expectScrollingTo('id=top')));
+ });
+
+
+ describe('and specifying a hash', function() {
+
+ it('should ignore the `hash` argument if not a string', inject(
+ spyOnJQLiteDocumentLoaded(),
+ addElements('id=one', 'id=two'),
+ changeHashTo('one'), // won't scroll since `jqLiteDocumentLoaded()` is spied upon
+ callAnchorScroll({}),
+ expectScrollingTo('id=one'),
+ unspyOnJQLiteDocumentLoaded()));
+
+
+ it('should ignore `$location.hash()` if `hash` is passed as argument', inject(
+ spyOnJQLiteDocumentLoaded(),
+ addElements('id=one', 'id=two'),
+ changeHashTo('one'), // won't scroll since `jqLiteDocumentLoaded()` is spied upon
+ callAnchorScroll('two'),
+ expectScrollingTo('id=two'),
+ unspyOnJQLiteDocumentLoaded()));
+
+ it('should scroll to top of the window if empty hash', inject(
+ callAnchorScroll(''),
+ expectScrollingToTop));
- it('should not scroll to other than anchor element with name', inject(
- addElements('input name=xxl', 'select name=xxl', 'form name=xxl'),
- changeHashAndScroll('xxl'),
- expectNoScrolling()));
+ it('should not scroll if hash does not match any element', inject(
+ addElements('id=one', 'id=two'),
+ callAnchorScroll('non-existing'),
+ expectNoScrolling()));
- it('should scroll to anchor even if other element with given name exist', inject(
- addElements('input name=some', 'a name=some'),
- changeHashAndScroll('some'),
- expectScrollingTo('a name=some')));
+ it('should scroll to anchor element with name', inject(
+ addElements('a name=abc'),
+ callAnchorScroll('abc'),
+ expectScrollingTo('a name=abc')));
- it('should scroll to element with id with precedence over name', inject(
- addElements('name=abc', 'id=abc'),
- changeHashAndScroll('abc'),
- expectScrollingTo('id=abc')));
+ it('should not scroll to other than anchor element with name', inject(
+ addElements('input name=xxl', 'select name=xxl', 'form name=xxl'),
+ callAnchorScroll('xxl'),
+ expectNoScrolling()));
- it('should scroll to top if hash == "top" and no matching element', inject(
- changeHashAndScroll('top'),
- expectScrollingToTop));
+ it('should scroll to anchor even if other element with given name exist', inject(
+ addElements('input name=some', 'a name=some'),
+ callAnchorScroll('some'),
+ expectScrollingTo('a name=some')));
- it('should scroll to element with id "top" if present', inject(
- addElements('id=top'),
- changeHashAndScroll('top'),
- expectScrollingTo('id=top')));
+
+ it('should scroll to element with id with precedence over name', inject(
+ addElements('name=abc', 'id=abc'),
+ callAnchorScroll('abc'),
+ expectScrollingTo('id=abc')));
+
+
+ it('should scroll to top if hash == "top" and no matching element', inject(
+ callAnchorScroll('top'),
+ expectScrollingToTop));
+
+
+ it('should scroll to element with id "top" if present', inject(
+ addElements('id=top'),
+ callAnchorScroll('top'),
+ expectScrollingTo('id=top')));
+ });
});

0 comments on commit 731c8b5

Please sign in to comment.