diff --git a/src/sticky/index.js b/src/sticky/index.js index 6379b58ac..f12d2f311 100644 --- a/src/sticky/index.js +++ b/src/sticky/index.js @@ -7,11 +7,16 @@ const [createComponent, bem] = createNamespace('sticky'); export default createComponent({ mixins: [ - BindEventMixin(function(bind) { + BindEventMixin(function(bind, isBind) { if (!this.scroller) { this.scroller = getScroller(this.$el); } + if (this.observer) { + const method = isBind ? 'observe' : 'unobserve'; + this.observer[method](this.$el); + } + bind(this.scroller, 'scroll', this.onScroll, true); this.onScroll(); }), @@ -58,6 +63,21 @@ export default createComponent({ }, }, + created() { + // compatibility: https://caniuse.com/#feat=intersectionobserver + if (window.IntersectionObserver) { + this.observer = new IntersectionObserver( + entries => { + // trigger scroll when visibility changed + if (entries[0].intersectionRatio > 0) { + this.onScroll(); + } + }, + { root: document.body } + ); + } + }, + methods: { onScroll() { if (isHidden(this.$el)) { diff --git a/src/sticky/test/index.spec.js b/src/sticky/test/index.spec.js index 2212fff2f..0e23111db 100644 --- a/src/sticky/test/index.spec.js +++ b/src/sticky/test/index.spec.js @@ -43,6 +43,22 @@ test('offset-top prop', () => { mockScrollTop(0); }); +test('should not trigger scroll event when hidden', () => { + const scroll = jest.fn(); + mount({ + template: ` + + Content + + `, + methods: { + scroll, + }, + }); + + expect(scroll).toHaveBeenCalledTimes(0); +}); + test('container prop', () => { const wrapper = mount({ template: ` @@ -68,3 +84,52 @@ test('container prop', () => { expect(wrapper).toMatchSnapshot(); mockScrollTop(0); }); + +test('trigger scroll when visibility changed', () => { + const originIntersectionObserver = window.IntersectionObserver; + + const observe = jest.fn(); + const unobserve = jest.fn(); + const scroll = jest.fn(); + + let observerCallback; + + window.IntersectionObserver = class IntersectionObserver { + constructor(callback) { + observerCallback = callback; + } + + observe() { + observe(); + } + + unobserve() { + unobserve(); + } + }; + + const wrapper = mount({ + template: ` + + Content + + `, + methods: { + scroll, + }, + }); + + expect(observe).toHaveBeenCalledTimes(1); + expect(scroll).toHaveBeenCalledTimes(1); + + observerCallback([{ intersectionRatio: 1 }]); + expect(scroll).toHaveBeenCalledTimes(2); + + observerCallback([{ intersectionRatio: 0 }]); + expect(scroll).toHaveBeenCalledTimes(2); + + wrapper.destroy(); + expect(unobserve).toHaveBeenCalledTimes(1); + + window.IntersectionObserver = originIntersectionObserver; +});