diff --git a/README.md b/README.md index 34963be..6bbc41c 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,21 @@ export class SomeModule { } ``` +#### Scroll in + +Per default Sticky Things expects your body to be the element that scrolls. However, if Sticky Things is used in an `overflow`-container, that container must be made known to the directive. + +This is best done with a query selector. If a string is provided it will be called with `document.querySelector`. Instead an HTML element (nativeElement) can be provided as well. + +```html +
+
+
+ Scroll by! +
+
+``` + #### Boundary Elements diff --git a/e2e/src/posa.e2e-spec.ts b/e2e/src/posa.e2e-spec.ts index 949f5d5..dbe64a0 100644 --- a/e2e/src/posa.e2e-spec.ts +++ b/e2e/src/posa.e2e-spec.ts @@ -11,8 +11,10 @@ describe('with position:absolute container', () => { it('should recognize sticky ', async () => { - page.navigateToDev(); - await browser.executeScript('window.scrollTo(0,501);'); + await page.navigateToDev(); + + // dont use window scroll here. + browser.actions().mouseMove(await page.getOtherScrollable()).perform(); const hasStickyClass = await hasClass(page.getStickyElement(), 'is-sticky'); expect(hasStickyClass).toBe(true); }); diff --git a/e2e/src/posa.po.ts b/e2e/src/posa.po.ts index de3c10c..0f29525 100644 --- a/e2e/src/posa.po.ts +++ b/e2e/src/posa.po.ts @@ -18,6 +18,10 @@ export class PosaPage { return element(by.css('.some-thing-sticky')); } + getOtherScrollable() { + return element(by.css('.another-scrollable-container')); + } + getEnableButton() { return element(by.css('#enable-btn')); } diff --git a/projects/angular-sticky-things/src/lib/sticky-thing.directive.ts b/projects/angular-sticky-things/src/lib/sticky-thing.directive.ts index 42edeea..1034035 100644 --- a/projects/angular-sticky-things/src/lib/sticky-thing.directive.ts +++ b/projects/angular-sticky-things/src/lib/sticky-thing.directive.ts @@ -39,6 +39,8 @@ export class StickyThingDirective implements OnInit, AfterViewInit, OnDestroy { marginBottom$ = new BehaviorSubject(0); enable$ = new BehaviorSubject(true); + @Input() scrollContainer: string | HTMLElement; + @Input() set marginTop(value: number) { this.marginTop$.next(value); } @@ -61,7 +63,7 @@ export class StickyThingDirective implements OnInit, AfterViewInit, OnDestroy { * The field represents some position values in normal (not sticky) mode. * If the browser size or the content of the page changes, this value must be recalculated. * */ - private scroll$ = new Subject(); + private scroll$ = new Subject(); private scrollThrottled$: Observable; @@ -81,7 +83,6 @@ export class StickyThingDirective implements OnInit, AfterViewInit, OnDestroy { this.scrollThrottled$ = this.scroll$ .pipe( throttleTime(0, animationFrame), - map(() => window.pageYOffset), share() ); @@ -169,19 +170,49 @@ export class StickyThingDirective implements OnInit, AfterViewInit, OnDestroy { } } - @HostListener('window:scroll', []) - adapter(): void { + setupListener(): void { if (isPlatformBrowser(this.platformId)) { - this.scroll$.next(); + const target = this.getScrollTarget(); + target.addEventListener('scroll', this.listener); } } - ngOnDestroy(): void { - this.componentDestroyed.next(); + removeListener() { + if (isPlatformBrowser(this.platformId)) { + const target = this.getScrollTarget(); + target.removeEventListener('scroll', this.listener); + } } + listener = (e: Event) => { + const upperScreenEdgeAt = (e.target as HTMLElement).scrollTop || window.pageYOffset; + this.scroll$.next(upperScreenEdgeAt); + }; + + ngOnInit(): void { this.checkSetup(); + this.setupListener(); + + } + + ngOnDestroy(): void { + this.componentDestroyed.next(); + this.removeListener(); + } + + private getScrollTarget(): Element | Window { + + let target: Element | Window; + + if (this.scrollContainer && typeof this.scrollContainer === 'string') { + target = document.querySelector(this.scrollContainer); + } else if (this.scrollContainer && this.scrollContainer instanceof HTMLElement) { + target = this.scrollContainer; + } else { + target = window; + } + return target; } getComputedStyle(el: HTMLElement): ClientRect | DOMRect { diff --git a/src/app/test-cases/posa-container/posa-container.component.html b/src/app/test-cases/posa-container/posa-container.component.html index 3814168..d49cacb 100644 --- a/src/app/test-cases/posa-container/posa-container.component.html +++ b/src/app/test-cases/posa-container/posa-container.component.html @@ -6,7 +6,7 @@
-
+
Scroll by!
@@ -17,3 +17,22 @@

























































































+ + +
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
Hello
+
diff --git a/src/app/test-cases/posa-container/posa-container.component.scss b/src/app/test-cases/posa-container/posa-container.component.scss index 3980e11..1c1f249 100644 --- a/src/app/test-cases/posa-container/posa-container.component.scss +++ b/src/app/test-cases/posa-container/posa-container.component.scss @@ -16,3 +16,16 @@ padding: 15px; color: #000; } + +.another-scrollable-container { + width: 150px; + height: 100px; + overflow: auto; + + div { + background-color: #fff; + border: 1px solid #333; + padding: 15px; + width: 200px; + } +} diff --git a/src/app/test-cases/posa-container/posa-container.component.ts b/src/app/test-cases/posa-container/posa-container.component.ts index 1cbbb9f..645fc80 100644 --- a/src/app/test-cases/posa-container/posa-container.component.ts +++ b/src/app/test-cases/posa-container/posa-container.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, HostBinding, OnInit} from '@angular/core'; @Component({ selector: 'demo-posa-container', @@ -7,6 +7,8 @@ import {Component, OnInit} from '@angular/core'; }) export class PosaContainerComponent implements OnInit { + @HostBinding('class') class = 'scroll-container'; + constructor() { }