Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: refactor adjust implementation #22

Merged
merged 11 commits into from
Nov 23, 2023
58 changes: 49 additions & 9 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,52 @@ export const getOverflowNodes = (targetNode: HTMLElement, container: HTMLElement
return overflowNodes;
};

/**
* 获取 containing block
* https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
*/
function getContainingBlock(element: HTMLElement): HTMLElement | null {
function isWebKit(): boolean {
if (typeof CSS === 'undefined' || !CSS.supports) {
return false;
}
return CSS.supports('-webkit-backdrop-filter', 'none');
}

function isContainingBlock(ele: Element) {
const webkit = isWebKit();
YSMJ1994 marked this conversation as resolved.
Show resolved Hide resolved
const css = getComputedStyle(ele);

return (
css.transform !== 'none' ||
css.perspective !== 'none' ||
(css.containerType ? css.containerType !== 'normal' : false) ||
(!webkit && (css.backdropFilter ? css.backdropFilter !== 'none' : false)) ||
(!webkit && (css.filter ? css.filter !== 'none' : false)) ||
['transform', 'perspective', 'filter'].some((value) =>
(css.willChange || '').includes(value)
) ||
['paint', 'layout', 'strict', 'content'].some((value) => (css.contain || '').includes(value))
);
}

function isLastTraversableElement(ele: Element): boolean {
return ['html', 'body'].includes(ele.tagName.toLowerCase());
}

let currentElement: HTMLElement | null = element.parentElement;

while (currentElement && !isLastTraversableElement(currentElement)) {
if (isContainingBlock(currentElement)) {
return currentElement;
} else {
currentElement = currentElement.parentElement;
}
}

return null;
}

/**
* 获取可视区域,用来计算弹窗应该相对哪个节点做上下左右的位置变化。
* @param container
Expand All @@ -160,15 +206,9 @@ export function getViewPort(container: HTMLElement) {
return container;
}

// https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
// 若container为 fixed 或者相对于body定位,则代表其跳出父级滚动容器(若有),此时使用浏览器视口作为可视区域调整位置
const { offsetParent } = container;
if (!offsetParent || offsetParent === document.body) {
// 若container定位节点为body,且body有滚动,则使用body作为可视区域
if (offsetParent === document.body && isContentClippedElement(document.body)) {
return document.body;
}
return document.documentElement;
// 若container为 fixed 则代表其跳出父级滚动容器(若有),使用 containing block 或 浏览器根节点 作为可视区域
eternalsky marked this conversation as resolved.
Show resolved Hide resolved
if (getStyle(container, 'position') === 'fixed') {
return getContainingBlock(container) || document.documentElement;
}

// 循环寻找父级滚动容器
Expand Down
Loading