fix(aio): autoscroll & add #top-of-page
* Scrolls to hash element or top of page when no hash. * Scrolls down a bit (80px) to account for top menu overhang. * No longer scrolls when the hash element is not found. * Adds `<a id="top-of-page"></a>` which will benefit future efforts to navigate there from within a page.
This commit is contained in:
parent
ab03852234
commit
800b1b060e
@ -9,6 +9,7 @@
|
|||||||
<span class="fill-remaining-space"></span>
|
<span class="fill-remaining-space"></span>
|
||||||
</md-toolbar>
|
</md-toolbar>
|
||||||
|
|
||||||
|
<a id="top-of-page"></a>
|
||||||
<md-sidenav-container class="sidenav-container" role="main">
|
<md-sidenav-container class="sidenav-container" role="main">
|
||||||
|
|
||||||
<md-sidenav [ngClass]="{'collapsed': !isSideBySide }" #sidenav class="sidenav" [opened]="isOpened" [mode]="mode">
|
<md-sidenav [ngClass]="{'collapsed': !isSideBySide }" #sidenav class="sidenav" [opened]="isOpened" [mode]="mode">
|
||||||
|
@ -286,14 +286,14 @@ describe('AppComponent', () => {
|
|||||||
const scrollService: AutoScrollService = fixture.debugElement.injector.get(AutoScrollService);
|
const scrollService: AutoScrollService = fixture.debugElement.injector.get(AutoScrollService);
|
||||||
spyOn(scrollService, 'scroll');
|
spyOn(scrollService, 'scroll');
|
||||||
locationService.go('some/url#fragment');
|
locationService.go('some/url#fragment');
|
||||||
expect(scrollService.scroll).toHaveBeenCalledWith(jasmine.any(HTMLElement));
|
expect(scrollService.scroll).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be called when a document has been rendered', () => {
|
it('should be called when a document has been rendered', () => {
|
||||||
const scrollService: AutoScrollService = fixture.debugElement.injector.get(AutoScrollService);
|
const scrollService: AutoScrollService = fixture.debugElement.injector.get(AutoScrollService);
|
||||||
spyOn(scrollService, 'scroll');
|
spyOn(scrollService, 'scroll');
|
||||||
component.onDocRendered();
|
component.onDocRendered();
|
||||||
expect(scrollService.scroll).toHaveBeenCalledWith(jasmine.any(HTMLElement));
|
expect(scrollService.scroll).toHaveBeenCalledWith();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
// Scroll to the anchor in the hash fragment.
|
// Scroll to the anchor in the hash fragment.
|
||||||
autoScroll() {
|
autoScroll() {
|
||||||
this.autoScrollService.scroll(this.docViewer.nativeElement.offsetParent);
|
this.autoScrollService.scroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDocRendered() {
|
onDocRendered() {
|
||||||
|
@ -3,11 +3,9 @@ import { PlatformLocation } from '@angular/common';
|
|||||||
import { DOCUMENT } from '@angular/platform-browser';
|
import { DOCUMENT } from '@angular/platform-browser';
|
||||||
import { AutoScrollService } from './auto-scroll.service';
|
import { AutoScrollService } from './auto-scroll.service';
|
||||||
|
|
||||||
|
|
||||||
describe('AutoScrollService', () => {
|
describe('AutoScrollService', () => {
|
||||||
let injector: ReflectiveInjector,
|
let injector: ReflectiveInjector,
|
||||||
autoScroll: AutoScrollService,
|
autoScroll: AutoScrollService,
|
||||||
container: HTMLElement,
|
|
||||||
location: MockPlatformLocation,
|
location: MockPlatformLocation,
|
||||||
document: MockDocument;
|
document: MockDocument;
|
||||||
|
|
||||||
@ -16,6 +14,7 @@ describe('AutoScrollService', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MockDocument {
|
class MockDocument {
|
||||||
|
body = new MockElement();
|
||||||
getElementById = jasmine.createSpy('Document getElementById');
|
getElementById = jasmine.createSpy('Document getElementById');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +22,10 @@ describe('AutoScrollService', () => {
|
|||||||
scrollIntoView = jasmine.createSpy('Element scrollIntoView');
|
scrollIntoView = jasmine.createSpy('Element scrollIntoView');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
spyOn(window, 'scrollBy');
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
injector = ReflectiveInjector.resolveAndCreate([
|
injector = ReflectiveInjector.resolveAndCreate([
|
||||||
AutoScrollService,
|
AutoScrollService,
|
||||||
@ -31,34 +34,37 @@ describe('AutoScrollService', () => {
|
|||||||
]);
|
]);
|
||||||
location = injector.get(PlatformLocation);
|
location = injector.get(PlatformLocation);
|
||||||
document = injector.get(DOCUMENT);
|
document = injector.get(DOCUMENT);
|
||||||
container = window.document.createElement('div');
|
|
||||||
container.scrollTop = 100;
|
|
||||||
autoScroll = injector.get(AutoScrollService);
|
autoScroll = injector.get(AutoScrollService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should scroll the container to the top if there is no hash', () => {
|
it('should scroll to the top if there is no hash', () => {
|
||||||
location.hash = '';
|
location.hash = '';
|
||||||
|
|
||||||
autoScroll.scroll(container);
|
const topOfPage = new MockElement();
|
||||||
expect(container.scrollTop).toEqual(0);
|
document.getElementById.and
|
||||||
|
.callFake(id => id === 'top-of-page' ? topOfPage : null);
|
||||||
|
|
||||||
|
autoScroll.scroll();
|
||||||
|
expect(topOfPage.scrollIntoView).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should scroll the container to the top if the hash does not match an element id', () => {
|
it('should not scroll if the hash does not match an element id', () => {
|
||||||
location.hash = 'some-id';
|
location.hash = 'not-found';
|
||||||
document.getElementById.and.returnValue(null);
|
document.getElementById.and.returnValue(null);
|
||||||
|
|
||||||
autoScroll.scroll(container);
|
autoScroll.scroll();
|
||||||
expect(document.getElementById).toHaveBeenCalledWith('some-id');
|
expect(document.getElementById).toHaveBeenCalledWith('not-found');
|
||||||
expect(container.scrollTop).toEqual(0);
|
expect(window.scrollBy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should scroll the container to the element whose id matches the hash', () => {
|
it('should scroll to the element whose id matches the hash', () => {
|
||||||
const element = new MockElement();
|
const element = new MockElement();
|
||||||
location.hash = 'some-id';
|
location.hash = 'some-id';
|
||||||
document.getElementById.and.returnValue(element);
|
document.getElementById.and.returnValue(element);
|
||||||
|
|
||||||
autoScroll.scroll(container);
|
autoScroll.scroll();
|
||||||
expect(document.getElementById).toHaveBeenCalledWith('some-id');
|
expect(document.getElementById).toHaveBeenCalledWith('some-id');
|
||||||
expect(element.scrollIntoView).toHaveBeenCalled();
|
expect(element.scrollIntoView).toHaveBeenCalled();
|
||||||
|
expect(window.scrollBy).toHaveBeenCalledWith(0, -80);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,22 +7,23 @@ import { DOCUMENT } from '@angular/platform-browser';
|
|||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AutoScrollService {
|
export class AutoScrollService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DOCUMENT) private document: any,
|
@Inject(DOCUMENT) private document: any,
|
||||||
private location: PlatformLocation) { }
|
private location: PlatformLocation) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll the contents of the container
|
* Scroll to the element with id extracted from the current location hash fragment
|
||||||
* to the element with id extracted from the current location hash fragment
|
* Scroll to top if no hash
|
||||||
|
* Don't scroll if hash not found
|
||||||
*/
|
*/
|
||||||
scroll(container: HTMLElement) {
|
scroll() {
|
||||||
const hash = this.getCurrentHash();
|
const hash = this.getCurrentHash();
|
||||||
const element: HTMLElement = this.document.getElementById(hash);
|
const element: HTMLElement = hash
|
||||||
|
? this.document.getElementById(hash)
|
||||||
|
: this.document.getElementById('top-of-page') || this.document.body;
|
||||||
if (element) {
|
if (element) {
|
||||||
element.scrollIntoView();
|
element.scrollIntoView();
|
||||||
} else {
|
if (window && window.scrollBy) { window.scrollBy(0, -80); }
|
||||||
container.scrollTop = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user