diff --git a/src/dom/__test__/get-positioned-parent-offset.web.test.ts b/src/dom/__test__/get-positioned-parent-offset.web.test.ts index 7573845..aa09a50 100644 --- a/src/dom/__test__/get-positioned-parent-offset.web.test.ts +++ b/src/dom/__test__/get-positioned-parent-offset.web.test.ts @@ -19,4 +19,24 @@ describe('getPositionedParentOffset', () => { expect(getPositionedParentOffset(target)).toEqual({ x: 100, y: 100 }); }); + + test('should handle out parameter', () => { + const parent = document.createElement('div'); + const target = document.createElement('div'); + + window.scrollTo(10, 20); + + parent.style.position = 'relative'; + target.style.position = 'absolute'; + mock.method(parent, 'getBoundingClientRect', () => new DOMRectReadOnlyMock(10, 20, 30, 40)); + + document.body.append(parent); + parent.append(target); + + const point = { x: 12, y: 34 }; + const result = getPositionedParentOffset(target, undefined, point); + expect(result).toBe(point); + expect(result.x).toEqual(10); + expect(result.y).toEqual(20); + }); }); diff --git a/src/dom/get-positioned-parent-offset.ts b/src/dom/get-positioned-parent-offset.ts index 443c3e8..a50f863 100644 --- a/src/dom/get-positioned-parent-offset.ts +++ b/src/dom/get-positioned-parent-offset.ts @@ -11,14 +11,19 @@ export interface PositioningOptions { * Returns position of top left corner of the parent positioned element relative to viewport origin. * @param element Target element. * @param options Options. + * @param out Out parameter. * @returns Offset. */ export function getPositionedParentOffset( element: HTMLElement, { strategy = 'absolute' }: PositioningOptions = {}, + out: Point2d = { x: 0, y: 0 }, ): Point2d { + out.x = 0; + out.y = 0; + if (!element.isConnected) { - return { x: 0, y: 0 }; + return out; } const offsetParent = element.parentElement @@ -28,26 +33,21 @@ export function getPositionedParentOffset( ) : null; - const offset: Point2d = { - x: 0, - y: 0, - }; - if (strategy === 'absolute') { - offset.x = -window.scrollX; - offset.y = -window.scrollY; + out.x = -window.scrollX; + out.y = -window.scrollY; } if (offsetParent) { const parentRect = offsetParent.getBoundingClientRect(); const parentStyle = getComputedStyle(offsetParent); - offset.x = parentRect.left; - offset.y = parentRect.top; + out.x = parentRect.left; + out.y = parentRect.top; // IMPORTANT: border-top/border-left affects parent positioning origin - offset.x += cssValueToNumber(parentStyle.borderLeftWidth); - offset.y += cssValueToNumber(parentStyle.borderTopWidth); + out.x += cssValueToNumber(parentStyle.borderLeftWidth); + out.y += cssValueToNumber(parentStyle.borderTopWidth); } const scrollParent = @@ -56,11 +56,11 @@ export function getPositionedParentOffset( // IMPORTANT: check offsetParent's scrollTop/scrollLeft if (offsetParent && offsetParent === scrollParent) { - offset.x += scrollParent.scrollLeft; - offset.y += scrollParent.scrollTop; + out.x += scrollParent.scrollLeft; + out.y += scrollParent.scrollTop; } - return offset; + return out; } /**