element-zoom-bug.js

解决 body 设置 zoom 导致各种 popover 错位的问题(Vue 2 + ElementUI 2
不适用zoom的话 可以使用ElementUI和autofit.js

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;
}

/**
 * Get offsets to the popper
 * @method
 * @memberof Popper
 * @access private
 * @param {Element} popper - the popper element
 * @param {Element} reference - the reference element (the popper will be relative to this)
 * @returns {Object} An object containing the offsets which will be applied to the popper
 */
Popper.prototype._getOffsets = function (popper, reference, placement) {
  placement = placement.split("-")[0];
  var popperOffsets = {};

  popperOffsets.position = this.state.position;
  var isParentFixed = popperOffsets.position === "fixed";

  //
  // Get reference element position
  //
  var referenceOffsets = getOffsetRectRelativeToCustomParent(
    reference,
    getOffsetParent(popper),
    isParentFixed
  );

  //
  // Get popper sizes
  //
  var popperRect = getOuterSizes(popper);

  //
  // Compute offsets of popper
  //

  // depending by the popper placement we have to compute its offsets slightly differently
  if (["right", "left"].indexOf(placement) !== -1) {
    popperOffsets.top =
      referenceOffsets.top +
      referenceOffsets.height / 2 -
      popperRect.height / 2;
    if (placement === "left") {
      popperOffsets.left = referenceOffsets.left - popperRect.width;
    } else {
      popperOffsets.left = referenceOffsets.right;
    }
  } else {
    popperOffsets.left =
      referenceOffsets.left + referenceOffsets.width / 2 - popperRect.width / 2;
    if (placement === "top") {
      popperOffsets.top = referenceOffsets.top - popperRect.height;
    } else {
      popperOffsets.top = referenceOffsets.bottom;
    }
  }

  // Add width and height to our offsets object
  popperOffsets.width = popperRect.width;
  popperOffsets.height = popperRect.height;

  return {
    popper: popperOffsets,
    reference: referenceOffsets,
  };
};
/**
 * Given an element and one of its parents, return the offset
 * @function
 * @ignore
 * @param {HTMLElement} element
 * @param {HTMLElement} parent
 * @return {Object} rect
 */
function getOffsetRectRelativeToCustomParent(element, parent, fixed) {
  var elementRect = getBoundingClientRect(element);
  var parentRect = getBoundingClientRect(parent,fixed);

  if (fixed) {
    var scrollParent = getScrollParent(parent);
    parentRect.top += scrollParent.scrollTop;
    parentRect.bottom += scrollParent.scrollTop;
    parentRect.left += scrollParent.scrollLeft;
    parentRect.right += scrollParent.scrollLeft;
  }

  var rect = {
    top: elementRect.top - parentRect.top,
    left: elementRect.left - parentRect.left,
    bottom: elementRect.top - parentRect.top + elementRect.height,
    right: elementRect.left - parentRect.left + elementRect.width,
    width: elementRect.width,
    height: elementRect.height,
  };
  return rect;
}
/**
 * Get the outer sizes of the given element (offset size + margins)
 * @function
 * @ignore
 * @argument {Element} element
 * @returns {Object} object containing width and height properties
 */
function getOuterSizes(element) {
  // NOTE: 1 DOM access here
  var _display = element.style.display,
    _visibility = element.style.visibility;
  element.style.display = "block";
  element.style.visibility = "hidden";
  var calcWidthToForceRepaint = element.offsetWidth;

  // original method
  var styles = root.getComputedStyle(element);
  var x = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom);
  var y = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight);
  var result = {
    width: element.offsetWidth + y,
    height: element.offsetHeight + x,
  };

  // reset element styles
  element.style.display = _display;
  element.style.visibility = _visibility;
  return result;
}

function getBoundingClientRect(element,fixed) {
  var rect = element.getBoundingClientRect();

  // whether the IE version is lower than 11
  var isIE = navigator.userAgent.indexOf("MSIE") != -1;

  // fix ie document bounding top always 0 bug
  var rectTop =
    isIE && element.tagName === "HTML" ? -element.scrollTop : rect.top;
  var zoom = window.getComputedStyle(document.body).zoom;
  const visualtop = fixed?rectTop * zoom:rectTop; // 视觉左偏移(物理像素)
  return {
    left: rect.left,
    top: visualtop,
    right: rect.right,
    bottom: rect.bottom,
    width: rect.right - rect.left,
    height: rect.bottom - rectTop,
  };
}

剩余的细节问题,需要同志们自己去查看微调!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容