Skip to content

Instantly share code, notes, and snippets.

@boringtu
Created February 23, 2024 13:33
Show Gist options
  • Save boringtu/2f1b9b98217a4b4ebd226382c8e1bdbd to your computer and use it in GitHub Desktop.
Save boringtu/2f1b9b98217a4b4ebd226382c8e1bdbd to your computer and use it in GitHub Desktop.
解决 `body` 设置 `zoom` 导致各种 `popover` 错位的问题(`Vue 2` + `ElementUI 2`)
import Popper from 'element-ui/lib/utils/popper';
const root = window;
// 重写此方法,以解决 body zoom 导致 ElementUI 所有 popper 相关元素错位的问题
// bug 产生原因:因为此方法依赖 `document.documentElement.clientWidth` 和 `document.documentElement.clientHeight`
// 而这两个值是 body 缩放之前的宽度和高度,换句话说,这两个值与 `zoom` 无关
// 所以解决方案的思路就是:重写此方法,将这两个值换成能有效获取实际宽高的方式即可
// 我的方案是换成了 `document.body.getBoundingClientRect()` 的 `width` 和 `height`
// 但这个逻辑依赖于 `body` 本身的宽高,所以请根据自己的情况换成更合适你的方案食用~
Popper.prototype._getBoundaries = function (data, padding, boundariesElement) {
// NOTE: 1 DOM access here
var boundaries = {};
var width, height;
if (boundariesElement === 'window') {
var body = root.document.body,
html = root.document.documentElement;
height = Math.max(
body.scrollHeight,
body.offsetHeight,
html.clientHeight,
html.scrollHeight,
html.offsetHeight,
);
width = Math.max(
body.scrollWidth,
body.offsetWidth,
html.clientWidth,
html.scrollWidth,
html.offsetWidth,
);
boundaries = {
top: 0,
right: width,
bottom: height,
left: 0,
};
} else if (boundariesElement === 'viewport') {
var offsetParent = getOffsetParent(this._popper);
var scrollParent = getScrollParent(this._popper);
var offsetParentRect = getOffsetRect(offsetParent);
// Thanks the fucking native API, `document.body.scrollTop` & `document.documentElement.scrollTop`
var getScrollTopValue = function getScrollTopValue(element) {
return element == document.body
? Math.max(document.documentElement.scrollTop, document.body.scrollTop)
: element.scrollTop;
};
var getScrollLeftValue = function getScrollLeftValue(element) {
return element == document.body
? Math.max(document.documentElement.scrollLeft, document.body.scrollLeft)
: element.scrollLeft;
};
// if the popper is fixed we don't have to substract scrolling from the boundaries
var scrollTop =
data.offsets.popper.position === 'fixed' ? 0 : getScrollTopValue(scrollParent);
var scrollLeft =
data.offsets.popper.position === 'fixed' ? 0 : getScrollLeftValue(scrollParent);
boundaries = {
top: 0 - (offsetParentRect.top - scrollTop),
// right: root.document.documentElement.clientWidth - (offsetParentRect.left - scrollLeft),
right:
root.document.body.getBoundingClientRect().width -
(offsetParentRect.left - scrollLeft),
// bottom: root.document.documentElement.clientHeight - (offsetParentRect.top - scrollTop),
bottom:
root.document.body.getBoundingClientRect().height -
(offsetParentRect.top - scrollTop),
left: 0 - (offsetParentRect.left - scrollLeft),
};
} else {
if (getOffsetParent(this._popper) === boundariesElement) {
boundaries = {
top: 0,
left: 0,
right: boundariesElement.clientWidth,
bottom: boundariesElement.clientHeight,
};
} else {
boundaries = getOffsetRect(boundariesElement);
}
}
boundaries.left += padding;
boundaries.right -= padding;
boundaries.top = boundaries.top + padding;
boundaries.bottom = boundaries.bottom - padding;
return boundaries;
};
// 注:以下 function 为上面重写 function 的依赖,均完整复制自 Popper.js
/**
* Returns the offset parent of the given element
* @function
* @ignore
* @argument {Element} element
* @returns {Element} offset parent
*/
function getOffsetParent(element) {
// NOTE: 1 DOM access here
var offsetParent = element.offsetParent;
return offsetParent === root.document.body || !offsetParent
? root.document.documentElement
: offsetParent;
}
/**
* Returns the scrolling parent of the given element
* @function
* @ignore
* @argument {Element} element
* @returns {Element} offset parent
*/
function getScrollParent(element) {
var parent = element.parentNode;
if (!parent) {
return element;
}
if (parent === root.document) {
// Firefox puts the scrollTOp value on `documentElement` instead of `body`, we then check which of them is
// greater than 0 and return the proper element
if (root.document.body.scrollTop || root.document.body.scrollLeft) {
return root.document.body;
} else {
return root.document.documentElement;
}
}
// Firefox want us to check `-x` and `-y` variations as well
if (
['scroll', 'auto'].indexOf(getStyleComputedProperty(parent, 'overflow')) !== -1 ||
['scroll', 'auto'].indexOf(getStyleComputedProperty(parent, 'overflow-x')) !== -1 ||
['scroll', 'auto'].indexOf(getStyleComputedProperty(parent, 'overflow-y')) !== -1
) {
// If the detected scrollParent is body, we perform an additional check on its parentNode
// in this way we'll get body if the browser is Chrome-ish, or documentElement otherwise
// fixes issue #65
return parent;
}
return getScrollParent(element.parentNode);
}
/**
* Get CSS computed property of the given element
* @function
* @ignore
* @argument {Eement} element
* @argument {String} property
*/
function getStyleComputedProperty(element, property) {
// NOTE: 1 DOM access here
var css = root.getComputedStyle(element, null);
return css[property];
}
/**
* Get the position of the given element, relative to its offset parent
* @function
* @ignore
* @param {Element} element
* @return {Object} position - Coordinates of the element and its `scrollTop`
*/
function getOffsetRect(element) {
var elementRect = {
width: element.offsetWidth,
height: element.offsetHeight,
left: element.offsetLeft,
top: element.offsetTop,
};
elementRect.right = elementRect.left + elementRect.width;
elementRect.bottom = elementRect.top + elementRect.height;
// position
return elementRect;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment