4

There are two types of zoom. The 'pinch zoom' you get on mobile browsers where content disappears off the edges of the screen. And the 'page zoom' you get on desktop browsers, like when you do Ctrl +. After 'page zoom' the page is re-flowed, so with, a responsive layout you still see the whole page width.

How can I allow users to 'page zoom' on mobile devices?

I am thinking there could be a Zoom + and Zoom - button on the header bar of my site. I want this because I have a web app that the majority of users like, both on desktop and mobile browsers. But some less capable users find the site small and fiddly on some of their mobile devices. The ability to pinch zoom (which I have not disabled) is a help, but it means constant zooming in and out to navigate.

I have tried solutions involving CSS transform: scale(...) and HTML <meta name="viewport" ...> and altering these from JavaScript. But these all seem to have a 'pinch zoom' effect, not the page zoom I am after. Also transform: scale(...) causes problems with js based/pixelbased interaction types such as draggable which I use.

I have also looked at altering CSS font sizes from JavaScript. But this only works for text, not the images, <div>s etc..

James
  • 5,635
  • 2
  • 33
  • 44
  • Possibly duplicate of http://stackoverflow.com/questions/1156278/how-can-i-scale-an-entire-web-page-with-css –  Oct 19 '16 at 12:09
  • @SuperCoolHandsomeGelBoy I don't think it is a duplicate of 1156278, because `transform: scale(...)` is a 'pinch zoom'. Also `transform: scale(...)` causes problems with js based/pixelbased interaction types such as draggable which I use. – James Oct 19 '16 at 12:21
  • If you want to control zooms with javascript, this requires a lot of calculations and cannot be answered in a short time –  Oct 19 '16 at 12:22

4 Answers4

7

Apologies for answering my own question, but after a lot of tinkering, I found a way that works for me and seems to work on most web sites, so I thought it was worth sharing:

function zoom(scale) {
    document.body.style.transform = "scale(" + scale + ")";
    document.body.style.transformOrigin = "top left";
    document.body.style.width = (100 / scale) + "%";
    document.body.style.height = (100 / scale) + "%";
};
zoom(1.25);

The trick is to scale up the body with a scale transform, but then reduce the height and width. Reducing the height and width causes it to re-flow and keep the transformed content on the screen.

I tested the above code by pasting it into the console of Chrome Firefox and IE on several popular websites. It seems to perfectly re-scale amazon.com and stackoverflow.com, but not gmail. My own web app needed the patches described below.

Fuller solution with patches for jQuery:

With the above solution (and after pinch zoom), issues occur when JavaScript tries to measure pixel positions and use them to position other elements. This is because functions like getBoundingClientRect() returns coordinates multiplied by scale. If you use jQuery .height(), .width(), offset() etc. you get the same issue; all jQuery docs says, "dimensions may be incorrect when the page is zoomed by the user".

You can fix jQuery methods like .width() so deliver values as they would be if were viewing it with scale = 1.

Edit since jQuery 3.2.0: height(), width(), etc. have been fixed and do not require the patch shown below. But offset() still needs the patch and if you use $(window).height() or width() to find the size of the view-port you will need to divide by scale.

var zoom = (function () {
    var scale = 1, recurLev = 0;
    function alter(fn, adj) {
        var original = $.fn[fn];
        $.fn[fn] = function () {
            var result;
            recurLev += 1;
            try {
                result = original.apply(this, arguments);
            } finally {
                recurLev -= 1;
            }
            if (arguments.length === 0 && recurLev === 0) {
                result = adj(result);
            }
            return result;
        };
    }
    function scalePos(n) { return n / scale; }
    /* Not needed since jQuery 3.2.0
    alter("width", scalePos);
    alter("height", scalePos);
    alter("outerWidth", scalePos);
    alter("outerHeight", scalePos);
    alter("innerWidth", scalePos);
    alter("innerHeight", scalePos);
    */
    alter("offset", function (o) { o.top /= scale; o.left /= scale; return o; });
    return function (s) {
        scale = s;
        document.body.style.transform = "scale(" + scale + ")";
        document.body.style.transformOrigin = "top left";
        document.body.style.width = (100 / scale) + "%";
        document.body.style.height = (100 / scale) + "%";
    };
}());
zoom(1.25);

The only other issue I found was in code (like dragging and drawing etc) that uses positions from events like mousedown, touchstart, mousemove, touchmove etc. I found you had to scale pageX and pageY by dividing them by scale.

James
  • 5,635
  • 2
  • 33
  • 44
  • Thanks for this. Your first fix (just pure javascript) worked great for me on my desktop but for some reason has no effect on mobile. Any idea why? – Karl Penzhorn Jun 30 '21 at 09:04
2

If you are okay with replicating a "pinch zoom", then you can use the widely supported document.body.style.zoom property.

Try running this in your console:

document.body.style.zoom = 2;
theonlygusti
  • 11,032
  • 11
  • 64
  • 119
  • This does pretty much what I was looking for on Chrome desktop and Chrome, though causes problems with js based/pixelbased interaction types such as draggable. But on Firefox it has no effect and on IE it messes up flex layouts. – James Oct 19 '16 at 13:41
  • @James but you are designing for a mobile site right? So you only really need to check against chrome/safari Android/iOS. But I see the problem in that it is not fully cross-browser yet. – theonlygusti Oct 19 '16 at 14:21
  • No I am not designing specifically for mobile. It is meant to run on any browser. – James Oct 19 '16 at 14:34
  • @James Your exact question: "How can I allow users to 'page zoom' on mobile devices?" but nevermind – theonlygusti Oct 19 '16 at 14:44
1
  Hi you can try this 
  css:
   div {
    margin: 150px;
    width: 200px;
    height: 100px;
    background-color: yellow;
    border: 1px solid black;
    border: 1px solid black;
    -ms-transform: scale(2,3); /* IE 9 */
    -webkit-transform: scale(2,3); /* Safari */
    transform: scale(2,3); /* Standard syntax */
  }

  Html:
   <div>
This div element is two times of its original width, and three times of its original height.
</div>
Ayyappan K
  • 1,111
  • 8
  • 9
  • As stated in my question, the `transform: scale(...)` solution does not work. If I applied your style to a `
    ` that wrapped my entire ``, the edge of the body would bleed off the screen.
    – James Oct 19 '16 at 12:34
1

Change font size on everything? So basically, every font size you define would need to use em as its units (intstead of px for example) so that it become a fraction of the default font.

Then you can set the body's font size (in px) to change the size of all the fonts.

function zoomPage(amount) {
  var currentSize = Number(window.getComputedStyle(document.body, null).getPropertyValue('font-size').match(/\d+/));
  console.log(Number(currentSize), amount, (currentSize + amount));
  document.body.style.fontSize = (currentSize + amount) + 'px';
}
body {
  font-size: 20px;
  font-family: sans-serif;
  max-width: 300px;
}

#smaller {
  font-size: 0.5em;
}

.noresize {
  font-size: 20px;
}
<button class="noresize" onclick="zoomPage(1)">+</button><button class="noresize" onclick="zoomPage(-1)">-</button>
<h1>I am big text</h1>
<p>I am <i>smaller</i> text</p>
<p id="smaller">I am even smaller text, even though I am also a paragraph</p>
theonlygusti
  • 11,032
  • 11
  • 64
  • 119
  • Your code changes font sizes nicesly, but my CSS has a lot of images `
    `s etc. sized and positioned in `px` size. I really want everything on the page to resize together, just like Ctrl +/- on a desktop browser.
    – James Oct 19 '16 at 13:15
  • @James then specify everything in `em` apart from the widths of containers you want to act as "bounding" boxes. – theonlygusti Oct 19 '16 at 13:16
  • @James there is a much easier way to do this but you said you wanted reflow, so I assumed you wanted bounding boxes to stay the same size. If you simply want to replicate a phone's pinch zoom w/o gestures I can provide another answer. – theonlygusti Oct 19 '16 at 13:18
  • @James then I don't understand, surely the view port becomes the boudnign box in that case? * bounding – theonlygusti Oct 19 '16 at 13:27