admin管理员组

文章数量:1208155

I'm having trouble getting the coordinates of an element after it has been transformed using the "zoom" property. I need to know the coordinates of all 4 corners. I would typically accomplish this with the getBoundingClientRect property however, that does not seem to work correctly when the element is zoomed. I've attached a short jsfiddle link to show what doesn't seem to work. I'm using Chrome but the behavior is present on Firefox as well.

/

HTML:

<div id="box" style="zoom:100%">Hello</div><div></div><div></div>

JS:

var div = document.getElementsByTagName("div");
div[1].innerHTML = "PreZoom Width: " + div[0].getBoundingClientRect().width;
div[0].style.zoom = "150%";
div[2].innerHTML = "PostZoom Width: " + div[0].getBoundingClientRect().width;

When I run that code, "100" is displayed for both the before and after zoom widths.

I have found a lot of information but everything I find seems to say that it was a known bug on Chrome and has since been fixed. Does anyone know how this can be corrected or what I might be doing wrong?

I'm having trouble getting the coordinates of an element after it has been transformed using the "zoom" property. I need to know the coordinates of all 4 corners. I would typically accomplish this with the getBoundingClientRect property however, that does not seem to work correctly when the element is zoomed. I've attached a short jsfiddle link to show what doesn't seem to work. I'm using Chrome but the behavior is present on Firefox as well.

http://jsfiddle.net/GCam11489/0hu7kvqt/

HTML:

<div id="box" style="zoom:100%">Hello</div><div></div><div></div>

JS:

var div = document.getElementsByTagName("div");
div[1].innerHTML = "PreZoom Width: " + div[0].getBoundingClientRect().width;
div[0].style.zoom = "150%";
div[2].innerHTML = "PostZoom Width: " + div[0].getBoundingClientRect().width;

When I run that code, "100" is displayed for both the before and after zoom widths.

I have found a lot of information but everything I find seems to say that it was a known bug on Chrome and has since been fixed. Does anyone know how this can be corrected or what I might be doing wrong?

Share Improve this question asked May 31, 2017 at 6:40 G-CamG-Cam 2913 silver badges14 bronze badges 1
  • Thank you Jesus! I was going nuts trying to figure out why my bounding rectangles were larger than the screen itself. Chromium should be ashamed of themselves for this undocumented behavior. – Ed_ Commented Jun 11, 2024 at 19:58
Add a comment  | 

6 Answers 6

Reset to default 6

This comment on the bug report goes into some detail reg. the issue, but its status appears to be 'WontFix', so you're probably out of luck there.

Some alternatives:

  • If you were to use transform: scale(1.5) instead of zoom, you'd get the correct value in getBoundingClientRect(), but it would mess with the page layout.
  • You could use window.getComputedStyle(div[0]).zoom to get the zoom value of the element (in decimals) and multiply it with the width from getBoundingClientRect()

Had the issue with a tooltip component.

Created this function to fetch all zooms applied to an element. Then I used that zoom to apply it to the global tooltip as well. Once done it aligned correctly.

Notice that it checks for parents parentel.parentElement?.parentElement, this is so it doesn't take the global browser zoom into account.

export const getZoomLevel = (el: HTMLElement) : number[] => {
    const zooms = []
    const getZoom = (el: HTMLElement) => {
        const zoom = window.getComputedStyle(el).getPropertyValue('zoom')
        const rzoom = zoom ? parseFloat(zoom) : 1
        if (rzoom !== 1) zooms.push(rzoom)
        if (el.parentElement?.parentElement) getZoom(el.parentElement)
    }
    getZoom(el)
    zooms.reverse()
    return zooms
}

If you don't want to to zoom the 'tooltip' component would be to get rect with zoom adjusted for, like this:

export const getRect = (el: HTMLElement) : Partial<DOMRect> => {
    if (!el) return { x: 0, y: 0, width: 0, height: 0, top: 0, right: 0, bottom: 0, left: 0 }
    let rect = el?.getBoundingClientRect();
    const zooms = getZoomLevel(el)
    const rectWithZoom = {
        bottom: zooms.reduce((a, b) => a * b, rect.bottom),
        height: zooms.reduce((a, b) => a * b, rect.height),
        left: zooms.reduce((a, b) => a * b, rect.left),
        right: zooms.reduce((a, b) => a * b, rect.right),
        top: zooms.reduce((a, b) => a * b, rect.top),
        width: zooms.reduce((a, b) => a * b, rect.width),
        x: zooms.reduce((a, b) => a * b, rect.x),
        y: zooms.reduce((a, b) => a * b, rect.y),
    }
    return rectWithZoom
}

This is quite an old post but I've encountered the same problem, I have an angular material project on a small panel which has a pixel ratio of under 1 which makes everything very small. To fix that I've added a zoom on the body to counter this.

The angular material (7) slider uses getBoundingClientRect() to determine it's position, which made the slider go way further then I'd wish.

I've used a solution like eruditespirit mentioned above.

if (Element.prototype.getBoundingClientRect) {
  const DefaultGetBoundingClientRect = Element.prototype.getBoundingClientRect;
  
  Element.prototype.getBoundingClientRect = function () {
    let zoom = +window.getComputedStyle(document.body).zoom;
    let data = DefaultGetBoundingClientRect.apply(this, arguments);

    if (zoom !== 1) {
      data.x = data.x * zoom;
      data.y = data.y * zoom;
      data.top = data.top * zoom;
      data.left = data.left * zoom;
      data.right = data.right * zoom;
      data.bottom = data.bottom * zoom;
      data.width = data.width * zoom;
      data.height = data.height * zoom;
    }

    return data;
  };
}

There has been some activity regarding this in late 2023: https://issues.chromium.org/issues/41461010#comment29

The new behavior adjusts the bounding box to the page's zoom, but not to the CSS zoom. This means that two elements with the same size before zoom, but with different zoom properties, will now have different sized bounding boxes.

But the fix seemed to not work correctly (https://issues.chromium.org/issues/41461010#comment30).

I came across this in my search for a solution. I have a position:absolute element that I update the position on when the window is resized, and iOS triggers this on zooming in/out.

I discovered window.visualViewport which has the key information needed to offset this, including zoom information.

My solution for setting the position/size of an element with the zoom offset:

positionNavHighlight(link: HTMLElement) {
  const rect = link.getBoundingClientRect();

  const offsetTop = window.visualViewport.offsetTop;
  const offsetLeft = window.visualViewport.offsetLeft;

  navActiveBg.style.top = `${rect.top + offsetTop}px`;
  navActiveBg.style.left = `${rect.left + offsetLeft}px`;
  navActiveBg.style.height = `${rect.height}px`;
  navActiveBg.style.width = `${rect.width}px`;
}

The behaviour seems to be inconsistent, depending on which browser engine you are using. In some cases for very modern browsers (Chromium 128+), CSS zoom seems to be taken into effect for the bounding client rect - however you can apply a "correction" after retrieving the element.currentCSSZoom, for example to apply a dynamic transform correctly

MDN page for currentCSSZoom

本文标签: javascriptCSS Zoom property not working with BoundingClientRectangleStack Overflow