No, not really.
My first guess was that this was intentionally not exposed in browsers because browsers intentionally don’t want us fighting it — or making well-intentioned but bad-outcome decisions based on that info. But I don’t see any evidence of that.
StackOverflow answers paint how weird cross-browser it can be. This script from 2013 works for me in Chrome, but not at all in Safari and reports incorrectly in Firefox. Even if that script worked, it relies on user agent detection (which is not long for this world) and some incredibly weird hacks.
So please, feel free to correct me if I’m wrong, but I think the answer is that we can’t really do this right now.
There is a thing called the Visual Viewport API
I’m kinda confused by it.
- The spec is a draft
- The support chart lists a lot of support
window.visualViewport
is defined in Firefox, Safari, and Chrome (desktop)- But…
window.visualViewport.scale
is always1
in Safari and Chrome, andundefined
in Firefox. In other words: useless.
I don’t entirely know if that is supposed to accurately represent the browser zoom level, because the spec talks about specifically about pinch-zoom. So, maybe it’s just not intended for desktop browser zoom levels.
What’s a use case here?
I had a fella spell out a situation like this:
He wanted to use CSS grid to layout cemetery plots (interesting already), like a top-down blueprint of a graveyard. There was lots of information in the layout. If you were “zoomed out” so you could see the whole graveyard on one page, the text in each area would be too small to read (sincethe type would be sized to fit within the boxes/graves). Ideally, the page would hide that text while the browser is zoomed out (perhaps a .hide-text
class). When zoomed in far enough, the text is shown again.
Like…
// Dunno if "resize" is best. I don't know what the "change zoom" event would be
window.visualViewport.addEventListener("resize", viewportHandler);
function viewportHandler(event) {
// NOTE: This doesn't actually work at time of writing
if (event.target.scale > 3) {
document.body.classList.remove("hide-text");
} else {
document.body.classList.add("hide-text");
}
}
There is Pixel Density…
Ben Nadel recently blogged: Looking At How Browser Zoom Affects CSS Media Queries And Pixel-Density.
If you look at window.devicePixelRatio
and zoom in, the pixel density in Chrome and Firefox will increase as you zoom in and decrease as you zoom out. Theoretically, you could test the original value (it might start at different places for users with different screens) and use changes in that value to guess zoom. But… in Safari it does nothing, as in, it stays the same regardless of zoom. Plus, the operating system zoom level can affect things here, making it extra tricky; not to mention that a page might start at a zoomed in level which could throw off the whole calculation from the start.
Well I was just about to go to sleep…then I read this…was sure there was an answer. Had a go, then realised..”No it can’t be done”. I was trying to test for specific ratios screen width to window width… which works for full screen on chrome but as IE & firefox both scale the screen size along with with window size there’s no way to detect the difference. Excuse bad spelling and verbos code… it’s 2am.
At this time I would just prevent browser zooming by listening to mouse and key events within the target container and implement custom zooming (e.g. transform scale) within these events. Only then you’ll have full control across browsers.
In my opinion, this would be a case where you would want to use a JavaScript library that implements a z axis scroll/zoom mechanism instead of the browser native zoom anyways.
Yeah for this use case I totally agree
I’ve got a similar use case trying to figure out: many newer laptops have high-dpi screens and decent resolutions, but they scale things at the OS level out of the box for the equivalent of something less than 1024×768. Trying to figure out a way to reverse that scaling for some components in our web app so things that would normally fit still fit.
Did you ever find a solution to this. We have the same issue never had a client with this but they view everything at 150% OS side and it ruining our layouts .! Any way to limit it or prevent it from happening?
window.visualViewport.scale changes when you use pinch-zoom on tablets and laptops, it’s a different behavior.
Not sure if I’m understanding the problem correctly, but couldn’t you use a media query in JS to see if the document’s width matches the media query to tell if the user is zoomed in or not?
For example:
Resize your browser window to 800px wide.
Run this JS in the developer tools:
window.matchMedia('(min-width: 800px)')
(It should report “true” for “matches”.)Zoom in.
Run the same JS. (It should report “false” for “matches”.)
To detect the amount of zoom, you would just look at the difference between the document width and when the media query finally matches again.
First way to work-around not knowing the browser zoom level is to use CSS to assign
zoom
based onradio
input state, here’s a quick example that hides caption text when25%
zoom level is selected…… checking CanIUse — Zoom states that
zoom
is not supported by Firefox, Opera Mini, and KaiOS.However, all is not lost because as suggested by an answer on StackOverflow — imitate browser zoom with JavaScript, the second way is to instead use
transform: scale(/*...*/)
along with a few other properties, eg…While these examples don’t detect zoom level they’re operational workarounds, all without any JavaScript, plus if ya wanted JavaScript event listeners that’s not overly difficult to add to radio inputs.
After spending several frustrating hours on this problem I have reluctantly come to the conclusion that it is not possible to reliably read out the zoom factor on desktop because W3C and the browser vendors worked together harmoniously to completely fuck up the viewport system. On mobile it IS possible.
First, window.visualViewport.scale gives you the zoom level in supporting browsers (Chromium-based ones + Firefox Android + Safari iOS), BUT ONLY if you use pinch-zoom. With page zoom it always stays 1, as you saw. Obviously.
On non-scale-supporting MOBiLE browsers, use this calculation, which works:
screen.width / ((window.visualViewport && window.visualViewport.width) || window.innerWidth)
On desktop browsers, forget it. Scale doesn’t work with page zoom. window.devicePixelRatio DOES work with page zoom, but not with pinch zoom. Besides, you’d have to divide the value by the actual physical DPR of the system, which cannot be found anywhere in JavaScript, because that would give the clue away.
For your friend’s use case I recommend the following:
1) Pick one graveyard plot as your reference element and read out its offsetWidth once.
2) Compare this value to window.innerWidth and decide whether the user has zoomed in or out enough to warrant showing or hiding the text.
3) Ideally, repeat this with every resize event. Unfortunately resize events are untrustworthy on both mobile and desktop browsers because why would you make them trustworthy? So maybe add a scroll event as well, and maybe a polling script that checks once every two seconds or so.
Addendum to my previous comment: the formula I give for mobile browsers only works when you use meta width=device-width.
I have been able to detect text-zoom via JS for quite a while now: when a text-zoom happens, I add a class to the tag so that I can style the page so zoomed text is still legible. You can see a demo here:
https://www.useragentman.com/enable/hero-image-text-resize.php
Of course, this could be abused (e.g. when a text zoom happens, a dev could code CSS make the font smaller, which should never be done) – but if used correctly, this can fix a lot of css issues that come up with text-zoom (and in my career, I have seen this a lot).
I would love feedback about this library, which is available on github:
https://github.com/zoltan-dulac/text-zoom-resize
(at the time of this writing, the Enable page I linked to at the top of this page doesn’t give proper npm info yet. It will be updated soon, so in the meantime just use the github.com link for info about how to install the package).
I recently had to figure out a way to do this and landed on a CSS only method that takes advantage of the fact that the value of a
rem
will change during text zoom, and the value of apx
will not.So, where
1rem
is16px
something like……would not apply at standard zoom, but would apply in 200% zoom between 1170px and 2336px
Can JavaScript Detect the Browser’s Zoom Level?
In Chromium-based browsers, the answer is yes, so long as the version supports the Window API
getScreenDetails
(available in Chrome since v100, March 2022 – for further details see caniuse).Significant caveats:
Can only be retrieved asynchronously.
Requires user to accept permission to “Manage windows on all your displays” (obviously this is a fairly big one)