CSS Zoom property not working with BoundingClientRectangle

Viewed 2463

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?

3 Answers

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

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