package.lib.dom-utils.getOffsetParent.js.flow Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of core Show documentation
Show all versions of core Show documentation
Tooltip and Popover Positioning Engine
// @flow
import getWindow from './getWindow';
import getNodeName from './getNodeName';
import getComputedStyle from './getComputedStyle';
import { isHTMLElement, isShadowRoot } from './instanceOf';
import isTableElement from './isTableElement';
import getParentNode from './getParentNode';
import getUAString from '../utils/userAgent';
function getTrueOffsetParent(element: Element): ?Element {
if (
!isHTMLElement(element) ||
// https://github.com/popperjs/popper-core/issues/837
getComputedStyle(element).position === 'fixed'
) {
return null;
}
return element.offsetParent;
}
// `.offsetParent` reports `null` for fixed elements, while absolute elements
// return the containing block
function getContainingBlock(element: Element) {
const isFirefox = /firefox/i.test(getUAString());
const isIE = /Trident/i.test(getUAString());
if (isIE && isHTMLElement(element)) {
// In IE 9, 10 and 11 fixed elements containing block is always established by the viewport
const elementCss = getComputedStyle(element);
if (elementCss.position === 'fixed') {
return null;
}
}
let currentNode = getParentNode(element);
if (isShadowRoot(currentNode)) {
currentNode = currentNode.host;
}
while (
isHTMLElement(currentNode) &&
['html', 'body'].indexOf(getNodeName(currentNode)) < 0
) {
const css = getComputedStyle(currentNode);
// This is non-exhaustive but covers the most common CSS properties that
// create a containing block.
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
if (
css.transform !== 'none' ||
css.perspective !== 'none' ||
css.contain === 'paint' ||
['transform', 'perspective'].indexOf(css.willChange) !== -1 ||
(isFirefox && css.willChange === 'filter') ||
(isFirefox && css.filter && css.filter !== 'none')
) {
return currentNode;
} else {
currentNode = currentNode.parentNode;
}
}
return null;
}
// Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
export default function getOffsetParent(element: Element) {
const window = getWindow(element);
let offsetParent = getTrueOffsetParent(element);
while (
offsetParent &&
isTableElement(offsetParent) &&
getComputedStyle(offsetParent).position === 'static'
) {
offsetParent = getTrueOffsetParent(offsetParent);
}
if (
offsetParent &&
(getNodeName(offsetParent) === 'html' ||
(getNodeName(offsetParent) === 'body' &&
getComputedStyle(offsetParent).position === 'static'))
) {
return window;
}
return offsetParent || getContainingBlock(element) || window;
}