diff --git a/packages/react-events/src/Press.js b/packages/react-events/src/Press.js index f059327f3e21..8a2d3cc930e7 100644 --- a/packages/react-events/src/Press.js +++ b/packages/react-events/src/Press.js @@ -436,10 +436,32 @@ function getAbsoluteBoundingClientRect( let offsetY = 0; // Traverse through all offset nodes - while (node != null && node.nodeType !== Node.DOCUMENT_NODE) { - offsetX += (node: any).scrollLeft; - offsetY += (node: any).scrollTop; - node = node.parentNode; + while (node != null) { + const parent = node.parentNode; + const scrollTop = node.scrollTop; + const scrollLeft = node.scrollLeft; + const isParentDocumentNode = + parent !== null && parent.nodeType === Node.DOCUMENT_NODE; + + // Check if the current node is fixed position, by using + // offsetParent node for a fast-path. Then we need to + // check if it's a scrollable container by checking if + // either scrollLeft or scrollTop are not 0. If it is + // a scrollable container and the parent node is not + // the document, then we can stop traversing the tree. + if ( + !isParentDocumentNode && + node.offsetParent === null && + (scrollLeft !== 0 || scrollTop !== 0) + ) { + break; + } + offsetX += scrollLeft; + offsetY += scrollTop; + if (isParentDocumentNode) { + break; + } + node = parent; } return { left: left + offsetX, diff --git a/packages/react-events/src/__tests__/Press-test.internal.js b/packages/react-events/src/__tests__/Press-test.internal.js index c3d73bf78ff6..32bae5e13af6 100644 --- a/packages/react-events/src/__tests__/Press-test.internal.js +++ b/packages/react-events/src/__tests__/Press-test.internal.js @@ -1109,8 +1109,8 @@ describe('Event responder: Press', () => { ReactDOM.render(element, container); ref.current.getBoundingClientRect = getBoundingClientRectMock; - // Emulate the element being offset - document.body.scrollTop = 1000; + // Emulate the element being offset with scroll + document.firstElementChild.scrollTop = 1000; const updatedCoordinatesInside = { pageX: coordinatesInside.pageX, pageY: coordinatesInside.pageY + 1000, @@ -1125,7 +1125,125 @@ describe('Event responder: Press', () => { createEvent('pointerup', updatedCoordinatesInside), ); jest.runAllTimers(); - document.body.scrollTop = 0; + document.firstElementChild.scrollTop = 0; + + expect(events).toEqual([ + 'onPressStart', + 'onPressChange', + 'onPressMove', + 'onPressEnd', + 'onPressChange', + 'onPress', + ]); + }); + + it('"onPress" is called on release inside a fixed container', () => { + let events = []; + const ref = React.createRef(); + const fixedContainerRef = React.createRef(); + const createEventHandler = msg => () => { + events.push(msg); + }; + + const element = ( +