Created
February 23, 2024 13:33
-
-
Save boringtu/2f1b9b98217a4b4ebd226382c8e1bdbd to your computer and use it in GitHub Desktop.
解决 `body` 设置 `zoom` 导致各种 `popover` 错位的问题(`Vue 2` + `ElementUI 2`)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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