UNPKG

composed-offset-position

Version:

This provides a set of ponyfills to achieve the same behavior of `offsetParent`, `offsetLeft` and `offsetTop` before the `offsetParent` spec was changed.

80 lines (76 loc) 3.07 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@floating-ui/utils/dom')) : typeof define === 'function' && define.amd ? define(['exports', '@floating-ui/utils/dom'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ComposedOffsetPosition = {}, global.FloatingUIUtilsDOM)); })(this, (function (exports, dom) { 'use strict'; function offsetParent(element) { return offsetParentPolyfill(element); } function offsetTop(element) { return offsetTopLeftPolyfill(element, 'offsetTop'); } function offsetLeft(element) { return offsetTopLeftPolyfill(element, 'offsetLeft'); } function flatTreeParent(element) { if (element.assignedSlot) { return element.assignedSlot; } if (element.parentNode instanceof ShadowRoot) { return element.parentNode.host; } return element.parentNode; } function ancestorTreeScopes(element) { const scopes = new Set(); let currentScope = element.getRootNode(); while (currentScope) { scopes.add(currentScope); currentScope = currentScope.parentNode ? currentScope.parentNode.getRootNode() : null; } return scopes; } function offsetParentPolyfill(element) { // Do an initial walk to check for display:none ancestors. for (let ancestor = element; ancestor; ancestor = flatTreeParent(ancestor)) { if (!(ancestor instanceof Element)) { continue; } if (getComputedStyle(ancestor).display === 'none') { return null; } } for (let ancestor = flatTreeParent(element); ancestor; ancestor = flatTreeParent(ancestor)) { if (!(ancestor instanceof Element)) { continue; } const style = getComputedStyle(ancestor); // Display:contents nodes aren't in the layout tree, so they should be skipped. if (style.display === 'contents') { continue; } if (style.position !== 'static' || dom.isContainingBlock(style)) { return ancestor; } if (ancestor.tagName === 'BODY') { return ancestor; } } return null; } function offsetTopLeftPolyfill(element, offsetTopOrLeft) { let value = element[offsetTopOrLeft]; let nextOffsetParent = offsetParentPolyfill(element); const scopes = ancestorTreeScopes(element); while (nextOffsetParent && !scopes.has(nextOffsetParent.getRootNode())) { value -= nextOffsetParent[offsetTopOrLeft]; nextOffsetParent = offsetParentPolyfill(nextOffsetParent); } return value; } exports.offsetLeft = offsetLeft; exports.offsetParent = offsetParent; exports.offsetTop = offsetTop; }));