diff --git a/src/position-observer.js b/src/position-observer.js index 9b52181..3f2f06b 100644 --- a/src/position-observer.js +++ b/src/position-observer.js @@ -49,18 +49,20 @@ PositionObserver.prototype.check = function(viewportState) { let untriggered = false - if (onBottom && !_wasBottom && atBottom) onBottom.call(this, container, viewportState) - else if (onTop && !_wasTop && atTop) onTop.call(this, container, viewportState) - else if (onRight && !_wasRight && atRight) onRight.call(this, container, viewportState) - else if (onLeft && !_wasLeft && atLeft) onLeft.call(this, container, viewportState) - else if (onFit && !_wasFit && fits) onFit.call(this, container, viewportState) - else untriggered = true + try { + if (onBottom && !_wasBottom && atBottom) onBottom.call(this, container, viewportState) + else if (onTop && !_wasTop && atTop) onTop.call(this, container, viewportState) + else if (onRight && !_wasRight && atRight) onRight.call(this, container, viewportState) + else if (onLeft && !_wasLeft && atLeft) onLeft.call(this, container, viewportState) + else if (onFit && !_wasFit && fits) onFit.call(this, container, viewportState) + else untriggered = true - if (once && !untriggered) this.destroy() - - this._wasTop = atTop - this._wasBottom = atBottom - this._wasLeft = atLeft - this._wasRight = atRight - this._wasFit = fits + if (once && !untriggered) this.destroy() + } finally { + this._wasTop = atTop + this._wasBottom = atBottom + this._wasLeft = atLeft + this._wasRight = atRight + this._wasFit = fits + } } diff --git a/src/viewport.js b/src/viewport.js index ae4be41..e37e261 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -16,10 +16,14 @@ export function Viewport(container, observerCollection) { requestAnimationFrame(() => { const { observers } = this const state = this.getState() - for (let i = observers.length; i--; ) observers[i].check(state) - this.lastX = state.positionX - this.lastY = state.positionY - scheduled = false + + try { + for (let i = observers.length; i--; ) observers[i].check(state) + } finally { + this.lastX = state.positionX + this.lastY = state.positionY + scheduled = false + } }) } } diff --git a/test/index.js b/test/index.js index fb13d96..ccf7264 100644 --- a/test/index.js +++ b/test/index.js @@ -196,7 +196,60 @@ describe('viewprt', () => { await testObserverType('ElementObserver') }) - it('destroys after trigging with once option', async () => { + it('recovers from observer exceptions', async () => { + assert.ok( + await page.evaluate(pageHeight => { + const content = document.createElement('div') + const contentHeight = pageHeight * 2 + content.style.height = contentHeight + 'px' + document.body.appendChild(content) + + return new Promise(resolve => { + PositionObserver({ + onTop() { + setTimeout(() => resolve(true)) + }, + onBottom() { + throw new Error() + } + }) + window.scrollTo(0, contentHeight) + setTimeout(() => window.scrollTo(0, 0), 65) + }) + }, pageHeight) + ) + + await closePage() + await createPage() + + assert.ok( + await page.evaluate(pageHeight => { + const content = document.createElement('div') + const contentHeight = pageHeight * 2 + content.style.height = contentHeight + 'px' + document.body.appendChild(content) + + const element = document.createElement('div') + element.style.height = '10px' + document.body.appendChild(element) + + return new Promise(resolve => { + ElementObserver(element, { + onEnter() { + throw new Error() + }, + onExit() { + setTimeout(() => resolve(true)) + } + }) + window.scrollTo(0, contentHeight) + setTimeout(() => window.scrollTo(0, 0), 65) + }) + }, pageHeight) + ) + }) + + it('destroys after triggering with once option', async () => { assert.ok( await page.evaluate(pageHeight => { const content = document.createElement('div')