diff --git a/src/utilities.js b/src/utilities.js index a693c35..4edb9e0 100644 --- a/src/utilities.js +++ b/src/utilities.js @@ -21,14 +21,17 @@ function getContainerRect(container) { left: 0, bottom: window.innerHeight, right: window.innerWidth, + height: window.innerHeight, + width: window.innerWidth, } : container.getBoundingClientRect(); } -export function getTargetScrollTop(container, element) { +export function getTargetScrollTop(container, element, toBottom = false) { const containerRect = getContainerRect(container); const elementRect = element.getBoundingClientRect(); const scrollOffset = elementRect.top - containerRect.top; - return scrollOffset + getScrollingElement(container).scrollTop; + const offsetCorrection = toBottom ? elementRect.height - containerRect.height : 0; + return scrollOffset + offsetCorrection + getScrollingElement(container).scrollTop; } export function getElementsInContainerViewport(container, elementList) { diff --git a/src/utilities.test.js b/src/utilities.test.js index 3364e47..6d2b823 100644 --- a/src/utilities.test.js +++ b/src/utilities.test.js @@ -42,48 +42,128 @@ describe('getScrolllingElement', () => { }); describe('getTargetScrollTop', () => { - function getElements(scrollTop, containerTop, targetTop) { + function getElements(scrollTop, containerDimensions, targetDimensions) { const container = document.createElement('div'); container.scrollTop = scrollTop; container.getBoundingClientRect = () => ({ - top: containerTop, + ...containerDimensions, + height: containerDimensions.bottom - containerDimensions.top, }); const target = document.createElement('div'); target.getBoundingClientRect = () => ({ - top: targetTop, + ...targetDimensions, + height: targetDimensions.bottom - targetDimensions.top, }); return [container, target]; } test('calculates scrollTop for target element', () => { - expect(getTargetScrollTop(...getElements(0, 0, 0))).toEqual(0); - expect(getTargetScrollTop(...getElements(100, 0, -100))).toEqual(0); - expect(getTargetScrollTop(...getElements(100, 0, 0))).toEqual(100); - expect(getTargetScrollTop(...getElements(0, 0, 100))).toEqual(100); - expect(getTargetScrollTop(...getElements(100, 0, 100))).toEqual(200); - expect(getTargetScrollTop(...getElements(100, 100, 100))).toEqual(100); + expect(getTargetScrollTop(...getElements(0, { top: 0 }, { top: 0 }))).toEqual(0); + expect(getTargetScrollTop(...getElements(100, { top: 0 }, { top: -100 }))).toEqual(0); + expect(getTargetScrollTop(...getElements(100, { top: 0 }, { top: 0 }))).toEqual(100); + expect(getTargetScrollTop(...getElements(0, { top: 0 }, { top: 100 }))).toEqual(100); + expect(getTargetScrollTop(...getElements(100, { top: 0 }, { top: 100 }))).toEqual(200); + expect(getTargetScrollTop(...getElements(100, { top: 100 }, { top: 100 }))).toEqual(100); }); - function getBodyElements(scrollTop, targetTop) { + test('calculates scrollTop for target element bottom', () => { + expect(getTargetScrollTop(...getElements(0, { + top: 0, + bottom: 300, + }, { + top: 0, + bottom: 300, + }), true)).toEqual(0); + + expect(getTargetScrollTop(...getElements(100, { + top: 0, + bottom: 300, + }, { + top: -100, + bottom: 200, + }), true)).toEqual(0); + + expect(getTargetScrollTop(...getElements(100, { + top: 0, + bottom: 300, + }, { + top: 0, + bottom: 300, + }), true)).toEqual(100); + + expect(getTargetScrollTop(...getElements(0, { + top: 0, + bottom: 300, + }, { + top: 100, + bottom: 400, + }), true)).toEqual(100); + + expect(getTargetScrollTop(...getElements(100, { + top: 0, + bottom: 300, + }, { + top: 100, + bottom: 400, + }), true)).toEqual(200); + + expect(getTargetScrollTop(...getElements(100, { + top: 100, + bottom: 400, + }, { + top: 100, + bottom: 400, + }), true)).toEqual(100); + }); + + function getBodyElements(scrollTop, targetDimensions) { const container = document.body; getScrollingElement(document.body).scrollTop = scrollTop; const target = document.createElement('div'); target.getBoundingClientRect = () => ({ - top: targetTop, + ...targetDimensions, + height: targetDimensions.bottom - targetDimensions.top, }); return [container, target]; } test('calculates scrollTop for target element in body', () => { - expect(getTargetScrollTop(...getBodyElements(0, 0))).toEqual(0); - expect(getTargetScrollTop(...getBodyElements(100, -100))).toEqual(0); - expect(getTargetScrollTop(...getBodyElements(100, 0))).toEqual(100); - expect(getTargetScrollTop(...getBodyElements(0, 100))).toEqual(100); - expect(getTargetScrollTop(...getBodyElements(100, 100))).toEqual(200); + expect(getTargetScrollTop(...getBodyElements(0, { top: 0 }))).toEqual(0); + expect(getTargetScrollTop(...getBodyElements(100, { top: -100 }))).toEqual(0); + expect(getTargetScrollTop(...getBodyElements(100, { top: 0 }))).toEqual(100); + expect(getTargetScrollTop(...getBodyElements(0, { top: 100 }))).toEqual(100); + expect(getTargetScrollTop(...getBodyElements(100, { top: 100 }))).toEqual(200); + }); + + test('calculates scrollTop for target element bottom in body', () => { + expect(getTargetScrollTop(...getBodyElements(0, { + top: 0, + bottom: SCREEN_HEIGHT, + }), true)).toEqual(0); + + expect(getTargetScrollTop(...getBodyElements(100, { + top: -100, + bottom: SCREEN_HEIGHT - 100, + }), true)).toEqual(0); + + expect(getTargetScrollTop(...getBodyElements(100, { + top: 0, + bottom: SCREEN_HEIGHT, + }), true)).toEqual(100); + + expect(getTargetScrollTop(...getBodyElements(0, { + top: 100, + bottom: SCREEN_HEIGHT + 100, + }), true)).toEqual(100); + + expect(getTargetScrollTop(...getBodyElements(100, { + top: 100, + bottom: SCREEN_HEIGHT + 100, + }), true)).toEqual(200); }); });