@@ -73,7 +73,7 @@ function $AnchorScrollProvider() {
73
73
<example module="anchorScrollExample">
74
74
<file name="index.html">
75
75
<div id="scrollArea" ng-controller="ScrollController">
76
- <a ng-click="gotoBottom()">Go to bottom</a>
76
+ <a id="top" ng-click="gotoBottom()">Go to bottom</a>
77
77
<a id="bottom"></a> You're at the bottom!
78
78
</div>
79
79
</file>
@@ -102,6 +102,37 @@ function $AnchorScrollProvider() {
102
102
margin-top: 2000px;
103
103
}
104
104
</file>
105
+ <file name="protractor.js" type="protractor">
106
+ function _isElemVisible() {
107
+ var elem = document.getElementById(arguments[0]);
108
+ var rect = elem.getBoundingClientRect();
109
+ var docElem = document.documentElement;
110
+ return (rect.top < docElem.clientHeight) &&
111
+ (rect.bottom > 0) &&
112
+ (rect.left < docElem.clientWidth) &&
113
+ (rect.right > 0);
114
+ }
115
+
116
+ function expectVisible(id, expected) {
117
+ browser.driver.executeScript(_isElemVisible, id).then(function(isVisible) {
118
+ expect(isVisible).toBe(expected);
119
+ });
120
+ }
121
+
122
+ function scrollToTop() {
123
+ browser.driver.executeScript('window.scrollTo(0, 0);');
124
+ }
125
+
126
+ it('should scroll to #bottom upon clicking #top', function() {
127
+ scrollToTop();
128
+ expectVisible('top', true);
129
+ expectVisible('bottom', false);
130
+
131
+ element(by.id('top')).click();
132
+ expectVisible('top', false);
133
+ expectVisible('bottom', true);
134
+ });
135
+ </file>
105
136
</example>
106
137
*
107
138
* <hr />
@@ -112,18 +143,19 @@ function $AnchorScrollProvider() {
112
143
<example module="anchorScrollOffsetExample">
113
144
<file name="index.html">
114
145
<div class="fixed-header" ng-controller="headerCtrl">
115
- <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5 ]">
116
- Go to anchor {{x }}
146
+ <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [0, 1,2,3,4]">
147
+ Go to anchor {{$index + 1 }}
117
148
</a>
118
149
</div>
119
- <div id="anchor{{x }}" class="anchor" ng-repeat="x in [1,2,3,4,5 ]">
120
- Anchor {{x }} of 5
150
+ <div id="anchor{{y }}" class="anchor" ng-repeat="y in [0, 1,2,3,4]">
151
+ Anchor {{$index + 1 }} of 5
121
152
</div>
122
153
</file>
123
154
<file name="script.js">
124
155
angular.module('anchorScrollOffsetExample', [])
125
156
.run(['$anchorScroll', function($anchorScroll) {
126
- $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
157
+ // scroll with a 50px offset from the top of the viewport
158
+ $anchorScroll.yOffset = 50;
127
159
}])
128
160
.controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
129
161
function ($anchorScroll, $location, $scope) {
@@ -144,6 +176,8 @@ function $AnchorScrollProvider() {
144
176
</file>
145
177
<file name="style.css">
146
178
body {
179
+ height: 100%;
180
+ margin: 0;
147
181
padding-top: 50px;
148
182
}
149
183
@@ -164,6 +198,153 @@ function $AnchorScrollProvider() {
164
198
margin: 5px 15px;
165
199
}
166
200
</file>
201
+ <file name="protractor.js" type="protractor">
202
+ function _isElemVisible() {
203
+ var elem = document.getElementById(arguments[0]);
204
+ var rect = elem.getBoundingClientRect();
205
+ var docElem = document.documentElement;
206
+ return (rect.top < docElem.clientHeight) &&
207
+ (rect.bottom > 0) &&
208
+ (rect.left < docElem.clientWidth) &&
209
+ (rect.right > 0);
210
+ }
211
+
212
+ function _getElemTop() {
213
+ var elem = document.getElementById(arguments[0]);
214
+ var rect = elem.getBoundingClientRect();
215
+ return rect.top;
216
+ }
217
+
218
+ function _getViewportHeight() {
219
+ return window.document.documentElement.clientHeight;
220
+ }
221
+
222
+ function _scrollElemIntoView() {
223
+ var elem = document.getElementById(arguments[0]);
224
+ elem.scrollIntoView();
225
+ }
226
+
227
+ function execWithTempViewportHeight(tempHeight, fn) {
228
+ setViewportHeight(tempHeight).then(function(oldHeight) {
229
+ fn();
230
+ setViewportHeight(oldHeight);
231
+ });
232
+ }
233
+
234
+ function execWithTempHash(tempHash, fn) {
235
+ browser.driver.getCurrentUrl().then(function(oldUrl) {
236
+ var newUrl = oldUrl + '#/#' + tempHash;
237
+ browser.get(newUrl);
238
+ fn();
239
+ browser.get(oldUrl);
240
+ });
241
+ }
242
+
243
+ function expectVisible(id, expected) {
244
+ browser.driver.executeScript(_isElemVisible, id).then(function(isVisible) {
245
+ expect(isVisible).toBe(expected);
246
+ });
247
+ }
248
+
249
+ function expectTop(id, expected) {
250
+ browser.driver.executeScript(_getElemTop, id).then(function(top) {
251
+ expect(top).toBe(expected);
252
+ });
253
+ }
254
+
255
+
256
+ function scrollIntoView(id) {
257
+ browser.driver.executeScript(_scrollElemIntoView, id);
258
+ }
259
+
260
+ function scrollTo(y) {
261
+ browser.driver.executeScript('window.scrollTo(0, ' + y + ');');
262
+ }
263
+
264
+ function scrollToTop() {
265
+ scrollTo(0);
266
+ }
267
+
268
+ function setViewportHeight(newHeight) {
269
+ return browser.driver.executeScript(_getViewportHeight).then(function(oldHeight) {
270
+ var heightDiff = newHeight - oldHeight;
271
+ var win = browser.driver.manage().window();
272
+
273
+ return win.getSize().then(function(size) {
274
+ var newWinHeight = size.height + heightDiff;
275
+
276
+ return win.setSize(size.width, newWinHeight).then(function() {
277
+ return oldHeight;
278
+ });
279
+ });
280
+ });
281
+ }
282
+
283
+ describe('scrolling with 50px offset', function() {
284
+ var yOffset = 50;
285
+
286
+ beforeEach(function() {
287
+ scrollToTop();
288
+ expectVisible('anchor0', true);
289
+ expectTop('anchor0', yOffset);
290
+ });
291
+
292
+ it('should scroll to the correct anchor when clicking each link', function() {
293
+ var links = element.all(by.repeater('x in [0,1,2,3,4]'));
294
+ var lastAnchor = element.all(by.repeater('y in [0,1,2,3,4]')).last();
295
+
296
+ // Make sure there is enough room to scroll the last anchor to the top
297
+ lastAnchor.getSize().then(function(size) {
298
+ var tempHeight = size.height - 10;
299
+ execWithTempViewportHeight(tempHeight, function() {
300
+ var idx = 0;
301
+ links.each(function(link) {
302
+ var targetAnchorId = 'anchor' + idx;
303
+ link.click();
304
+ expectVisible(targetAnchorId, true);
305
+ expectTop(targetAnchorId, yOffset);
306
+ idx++;
307
+ });
308
+ });
309
+ });
310
+ });
311
+
312
+ it('should automatically scroll when navigating to a URL with a hash', function() {
313
+ var links = element.all(by.repeater('x in [0,1,2,3,4]'));
314
+ var lastAnchor = element.all(by.repeater('y in [0,1,2,3,4]')).last();
315
+ var targetAnchorId = 'anchor2';
316
+
317
+ // Make sure there is enough room to scroll the last anchor to the top
318
+ lastAnchor.getSize().then(function(size) {
319
+ var tempHeight = size.height - 10;
320
+ execWithTempViewportHeight(tempHeight, function() {
321
+ execWithTempHash(targetAnchorId, function() {
322
+ expectVisible(targetAnchorId, true);
323
+ expectTop(targetAnchorId, yOffset);
324
+ });
325
+ });
326
+ });
327
+ });
328
+
329
+ it('should not scroll "overzealously"', function () {
330
+ var lastLink = element.all(by.repeater('x in [0,1,2,3,4]')).last();
331
+ var lastAnchor = element.all(by.repeater('y in [0,1,2,3,4]')).last();
332
+ var targetAnchorId = 'anchor4';
333
+
334
+ // Make sure there is not enough room to scroll the last anchor to the top
335
+ lastAnchor.getSize().then(function(size) {
336
+ var tempHeight = size.height + (yOffset / 2);
337
+ execWithTempViewportHeight(tempHeight, function() {
338
+ scrollIntoView(targetAnchorId);
339
+ expectTop(targetAnchorId, yOffset / 2);
340
+ lastLink.click();
341
+ expectVisible(targetAnchorId, true);
342
+ expectTop(targetAnchorId, yOffset);
343
+ });
344
+ });
345
+ });
346
+ });
347
+ </file>
167
348
</example>
168
349
*/
169
350
this . $get = [ '$window' , '$location' , '$rootScope' , function ( $window , $location , $rootScope ) {
0 commit comments