diff --git a/components/affix/affix.spec.ts b/components/affix/affix.spec.ts
index f173e801fff..6606e04b70f 100644
--- a/components/affix/affix.spec.ts
+++ b/components/affix/affix.spec.ts
@@ -44,6 +44,8 @@ describe('affix', () => {
new Event('pageshow'),
new Event('load')
];
+ const height = 100;
+ const width = 100;
beforeEach(fakeAsync(() => {
TestBed.configureTestingModule({
@@ -109,13 +111,13 @@ describe('affix', () => {
describe('when element gets shifted horizontally', () => {
it('adjusts left position accordingly to maintain natural position', fakeAsync(() => {
setupInitialState();
- componentObject.offsetTo(componentObject.elementRef(), { top: startOffset, left: 10, width: 100, height: 100 });
+ componentObject.offsetTo(componentObject.elementRef(), { top: startOffset, left: 10, width, height });
emitScroll(window, defaultOffsetTop + startOffset + 1);
expect(componentObject.wrap().offsetLeft).toBe(10);
emitScroll(window, defaultOffsetTop + startOffset - 1);
- componentObject.offsetTo(componentObject.elementRef(), { top: startOffset, left: 100, width: 100, height: 100 });
+ componentObject.offsetTo(componentObject.elementRef(), { top: startOffset, left: 100, width, height });
emitScroll(window, defaultOffsetTop + startOffset + 1);
expect(componentObject.wrap().offsetLeft).toBe(100);
@@ -288,6 +290,15 @@ describe('affix', () => {
fixture.detectChanges();
expect(component.updatePosition).toHaveBeenCalled();
});
+
+ it('should be a string value', () => {
+ spyOn(component, 'updatePosition');
+ expect(component.updatePosition).not.toHaveBeenCalled();
+ fixture.detectChanges();
+ context.fakeTarget = '#target';
+ fixture.detectChanges();
+ expect(component.updatePosition).toHaveBeenCalled();
+ });
});
describe('(nzChange)', () => {
@@ -318,11 +329,12 @@ describe('affix', () => {
}));
});
- it('should adjust the width when resize', fakeAsync(() => {
+ it('should adjust placeholder width when resize', fakeAsync(() => {
const offsetTop = 150;
context.newOffset = offsetTop;
setupInitialState({ offsetTop: offsetTop + 1 });
emitScroll(window, 2);
+ expect(componentObject.elementRef().style.width).toBe(`${width}px`);
componentObject.offsetYTo(componentObject.elementRef(), offsetTop + 2);
tick(20);
fixture.detectChanges();
@@ -330,7 +342,7 @@ describe('affix', () => {
tick(20);
fixture.detectChanges();
- expect(componentObject.wrap().offsetTop).toBe(offsetTop);
+ expect(componentObject.elementRef().style.width).toBe(``);
discardPeriodicTasks();
}));
@@ -352,7 +364,7 @@ describe('affix', () => {
}
getOffset(el: Element): Offset {
- return this.offsets[el.id] || { top: 10, left: 0, height: 100, width: 100 };
+ return this.offsets[el.id] || { top: 10, left: 0, height, width };
}
emitEvent(el: Element | Window, event: Event): void {
@@ -368,8 +380,8 @@ describe('affix', () => {
this.offsets[this.getKey(el)] = {
top: offset.top,
left: offset.left,
- height: 100,
- width: 100
+ height,
+ width
};
}
@@ -379,8 +391,8 @@ describe('affix', () => {
{
top: offsetTop,
left: 0,
- height: 100,
- width: 100
+ height,
+ width
}
);
}
@@ -486,14 +498,12 @@ describe('affix-extra', () => {
- `,
- styleUrls: [ './style/index.less' ],
- encapsulation: ViewEncapsulation.None
+ `
})
class TestAffixComponent {
@ViewChild(NzAffixComponent)
nzAffixComponent: NzAffixComponent;
- fakeTarget: Element | Window = null;
+ fakeTarget: string | Element | Window = null;
newOffset: {};
newOffsetBottom: {};
}
diff --git a/components/affix/doc/index.en-US.md b/components/affix/doc/index.en-US.md
index 8125b25ac3f..d0b11d41468 100755
--- a/components/affix/doc/index.en-US.md
+++ b/components/affix/doc/index.en-US.md
@@ -20,7 +20,7 @@ Please note that Affix should not cover other content on the page, especially wh
| -------- | ----------- | ---- | ------- |
| `[nzOffsetBottom]` | Pixels to offset from bottom when calculating position of scroll | number | - |
| `[nzOffsetTop]` | Pixels to offset from top when calculating position of scroll | number | 0 |
-| `[nzTarget]` | specifies the scrollable area dom node | `HTMLElement` | `window` |
+| `[nzTarget]` | specifies the scrollable area dom node | `string, HTMLElement` | `window` |
| `(nzChange)` | Callback for when affix state is changed | `EventEmitter` | - |
**Note:** Children of `nz-affix` can not be `position: absolute`, but you can set `nz-affix` as `position: absolute`:
diff --git a/components/affix/doc/index.zh-CN.md b/components/affix/doc/index.zh-CN.md
index c1989e9ba8f..d01518a3862 100755
--- a/components/affix/doc/index.zh-CN.md
+++ b/components/affix/doc/index.zh-CN.md
@@ -21,7 +21,7 @@ title: Affix
| --- | --- | --- | --- |
| `[nzOffsetBottom]` | 距离窗口底部达到指定偏移量后触发 | number | |
| `[nzOffsetTop]` | 距离窗口顶部达到指定偏移量后触发 | number | |
-| `[nzTarget]` | 设置 `nz-affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | `HTMLElement` | `window` |
+| `[nzTarget]` | 设置 `nz-affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | `string, HTMLElement` | `window` |
| `(nzChange)` | 固定状态改变时触发的回调函数 | `EventEmitter` | 无 |
**注意:**`nz-affix` 内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 `nz-affix` 为绝对定位:
diff --git a/components/affix/nz-affix.component.ts b/components/affix/nz-affix.component.ts
index c17e378f605..4b579cf62cc 100644
--- a/components/affix/nz-affix.component.ts
+++ b/components/affix/nz-affix.component.ts
@@ -1,15 +1,17 @@
-// tslint:disable:no-any
+import { DOCUMENT } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
+ Inject,
Input,
OnDestroy,
OnInit,
Output,
- ViewChild
+ ViewChild,
+ ViewEncapsulation
} from '@angular/core';
import { NzScrollService } from '../core/scroll/nz-scroll.service';
@@ -21,18 +23,19 @@ import { throttleByAnimationFrameDecorator } from '../core/util/throttleByAnimat
selector : 'nz-affix',
templateUrl : './nz-affix.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
- styles : [
- `:host {
+ styles : [ `
+ nz-affix {
display: block;
- }`
- ]
+ }
+ ` ],
+ encapsulation : ViewEncapsulation.None
})
export class NzAffixComponent implements OnInit, OnDestroy {
@Input()
- set nzTarget(value: Element | Window) {
+ set nzTarget(value: string | Element | Window) {
this.clearEventListeners();
- this._target = value || window;
+ this._target = typeof value === 'string' ? this.doc.querySelector(value) : value || window;
this.setTargetEventListeners();
this.updatePosition({});
}
@@ -60,10 +63,11 @@ export class NzAffixComponent implements OnInit, OnDestroy {
@Output()
readonly nzChange: EventEmitter = new EventEmitter();
- constructor(private scrollSrv: NzScrollService, private _el: ElementRef, private cd: ChangeDetectorRef) {
+ // tslint:disable-next-line:no-any
+ constructor(private scrollSrv: NzScrollService, private _el: ElementRef, @Inject(DOCUMENT) private doc: any, private cd: ChangeDetectorRef) {
}
- private timeout: any;
+ private timeout;
private events = [
'resize',
'scroll',
@@ -73,8 +77,8 @@ export class NzAffixComponent implements OnInit, OnDestroy {
'pageshow',
'load'
];
- private affixStyle: any;
- private placeholderStyle: any;
+ private affixStyle;
+ private placeholderStyle;
@ViewChild('wrap') private wrap: ElementRef;
@@ -94,6 +98,7 @@ export class NzAffixComponent implements OnInit, OnDestroy {
ngOnDestroy(): void {
this.clearEventListeners();
clearTimeout(this.timeout);
+ // tslint:disable-next-line:no-any
(this.updatePosition as any).cancel();
}
@@ -109,7 +114,7 @@ export class NzAffixComponent implements OnInit, OnDestroy {
const scrollTop = this.scrollSrv.getScroll(target, true);
const scrollLeft = this.scrollSrv.getScroll(target, false);
- const docElem = window.document.body;
+ const docElem = this.doc.body;
const clientTop = docElem.clientTop || 0;
const clientLeft = docElem.clientLeft || 0;
@@ -121,7 +126,72 @@ export class NzAffixComponent implements OnInit, OnDestroy {
};
}
+ private setTargetEventListeners(): void {
+ this.clearEventListeners();
+ this.events.forEach((eventName: string) => {
+ this._target.addEventListener(eventName, this.updatePosition, false);
+ });
+ }
+
+ private clearEventListeners(): void {
+ this.events.forEach(eventName => {
+ this._target.removeEventListener(eventName, this.updatePosition, false);
+ });
+ }
+
+ private getTargetRect(target: Element | Window | null): ClientRect {
+ return target !== window ?
+ (target as HTMLElement).getBoundingClientRect() :
+ { top: 0, left: 0, bottom: 0 } as ClientRect;
+ }
+
+ private genStyle(affixStyle: {}): string {
+ if (affixStyle == null) {
+ return '';
+ }
+ return Object.keys(affixStyle).map(key => {
+ const val = affixStyle[ key ];
+ return `${key}:${typeof val === 'string' ? val : val + 'px'}`;
+ }).join(';');
+ }
+
+ private setAffixStyle(e: Event, affixStyle: {}): void {
+ const originalAffixStyle = this.affixStyle;
+ const isWindow = this._target === window;
+ if (e.type === 'scroll' && originalAffixStyle && affixStyle && isWindow) {
+ return;
+ }
+ if (shallowEqual(originalAffixStyle, affixStyle)) {
+ return;
+ }
+
+ const fixed = !!affixStyle;
+ const wrapEl = this.wrap.nativeElement as HTMLElement;
+ wrapEl.style.cssText = this.genStyle(affixStyle);
+ this.affixStyle = affixStyle;
+ const cls = 'ant-affix';
+ if (fixed) {
+ wrapEl.classList.add(cls);
+ } else {
+ wrapEl.classList.remove(cls);
+ }
+
+ if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
+ this.nzChange.emit(fixed);
+ }
+ }
+
+ private setPlaceholderStyle(placeholderStyle: {}): void {
+ const originalPlaceholderStyle = this.placeholderStyle;
+ if (shallowEqual(placeholderStyle, originalPlaceholderStyle)) {
+ return;
+ }
+ (this._el.nativeElement as HTMLElement).style.cssText = this.genStyle(placeholderStyle);
+ this.placeholderStyle = placeholderStyle;
+ }
+
@throttleByAnimationFrameDecorator()
+ // tslint:disable-next-line:no-any
updatePosition(e: any): void {
const targetNode = this._target;
// Backwards support
@@ -187,69 +257,4 @@ export class NzAffixComponent implements OnInit, OnDestroy {
this.setPlaceholderStyle(null);
}
}
-
- private setTargetEventListeners(): void {
- this.clearEventListeners();
- this.events.forEach((eventName: string) => {
- this._target.addEventListener(eventName, this.updatePosition, false);
- });
- }
-
- private clearEventListeners(): void {
- this.events.forEach(eventName => {
- this._target.removeEventListener(eventName, this.updatePosition, false);
- });
- }
-
- private getTargetRect(target: Element | Window | null): ClientRect {
- return target !== window ?
- (target as HTMLElement).getBoundingClientRect() :
- { top: 0, left: 0, bottom: 0 } as ClientRect;
- }
-
- private genStyle(affixStyle: {}): string {
- if (affixStyle == null) {
- return '';
- }
- return Object.keys(affixStyle).map(key => {
- const val = affixStyle[ key ];
- return `${key}:${typeof val === 'string' ? val : val + 'px'}`;
- }).join(';');
- }
-
- private setAffixStyle(e: any, affixStyle: {}): void {
- const originalAffixStyle = this.affixStyle;
- const isWindow = this._target === window;
- if (e.type === 'scroll' && originalAffixStyle && affixStyle && isWindow) {
- return;
- }
- if (shallowEqual(originalAffixStyle, affixStyle)) {
- return;
- }
-
- const fixed = !!affixStyle;
- const wrapEl = this.wrap.nativeElement as HTMLElement;
- wrapEl.style.cssText = this.genStyle(affixStyle);
- this.affixStyle = affixStyle;
- const cls = 'ant-affix';
- if (fixed) {
- wrapEl.classList.add(cls);
- } else {
- wrapEl.classList.remove(cls);
- }
-
- if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
- this.nzChange.emit(fixed);
- }
- }
-
- private setPlaceholderStyle(placeholderStyle: {}): void {
- const originalPlaceholderStyle = this.placeholderStyle;
- if (shallowEqual(placeholderStyle, originalPlaceholderStyle)) {
- return;
- }
- (this._el.nativeElement as HTMLElement).style.cssText = this.genStyle(placeholderStyle);
- this.placeholderStyle = placeholderStyle;
- }
-
}