189

I've update my iPhone 6 plus to iOS 10 beta version and just found that in mobile safari, you can zoom any webpages by double tapping or pinching IGNORE the user-scalable=no code in the meta tag. I don't know whether it's a bug or feature. If it's considered as a feature, how do we disable viewport zooming iOS 10 safari ?


updated on iOS 11/12 release, iOS 11 and iOS 12 safari still DO NOT respect the user-scalable=no meta tag.

mobile github site on Safari

9
  • 5
    An accessibility feature: Of note in Safari on iOS 10 twitter.com/thomasfuchs/status/742531231007559680/photo/1
    – ErikE
    Jun 14, 2016 at 10:37
  • 110
    No, it isn't. It is bad practice for normal web content. For web apps, the default zoom behavior can completely ruin usability. For example, nobody wants to zoom in on a channel up button because they tapped it twice, nor zoom in on part of a video game because they tapped the jump button twice. There's a reason that this feature was added in the first place, and it makes no sense to break usability for everybody just because a few "web designers" don't know what they're doing. Go scream at the site designers and quit breaking the browser.
    – dgatwood
    Jan 30, 2017 at 8:10
  • 65
    Saying it's "bad practice" is an opinion and doesn't change the fact that Apple is insistent on taking web standards that the community spends months/years/decades getting implemented cross platform and taking a giant crap on them. Why should Apple dictate that web designers don't know what they're doing? Terrible argument.
    – samrap
    Feb 9, 2017 at 23:36
  • 2
    Personally, I think this stems from boilerplate code online where devs just copy and paste blindly without knowing what the purpose of the code is. Mar 1, 2017 at 10:15
  • 12
    Answer is simple, Apple: make disabling the meta tag a default-off accessibility setting. Those who need it, will have it, without punishing those who don't. Apr 19, 2017 at 23:56

19 Answers 19

107

It's possible to prevent webpage scaling in safari on iOS 10, but it's going to involve more work on your part. I guess the argument is that a degree of difficulty should stop cargo-cult devs from dropping "user-scalable=no" into every viewport tag and making things needlessly difficult for vision-impaired users.

Still, I would like to see Apple change their implementation so that there is a simple (meta-tag) way to disable double-tap-to-zoom. Most of the difficulties relate to that interaction.

You can stop pinch-to-zoom with something like this:

document.addEventListener('touchmove', function (event) {
  if (event.scale !== 1) { event.preventDefault(); }
}, false);

Note that if any deeper targets call stopPropagation on the event, the event will not reach the document and the scaling behavior will not be prevented by this listener.

Disabling double-tap-to-zoom is similar. You disable any tap on the document occurring within 300 milliseconds of the prior tap:

var lastTouchEnd = 0;
document.addEventListener('touchend', function (event) {
  var now = (new Date()).getTime();
  if (now - lastTouchEnd <= 300) {
    event.preventDefault();
  }
  lastTouchEnd = now;
}, false);

If you don't set up your form elements right, focusing on an input will auto-zoom, and since you have mostly disabled manual zoom, it will now be almost impossible to unzoom. Make sure the input font size is >= 16px.

If you're trying to solve this in a WKWebView in a native app, the solution given above is viable, but this is a better solution: https://stackoverflow.com/a/31943976/661418. And as mentioned in other answers, in iOS 10 beta 6, Apple has now provided a flag to honor the meta tag.

Update May 2017: I replaced the old 'check touches length on touchstart' method of disabling pinch-zoom with a simpler 'check event.scale on touchmove' approach. Should be more reliable for everyone.

18
  • 3
    Note that touchstart hack is inconsistent... and if you manage to sidestep it you are stuck in a very uncomfortable state Sep 14, 2016 at 21:32
  • 7
    Eg. full screen mapping apps, there is a zoom feature hard coded in the mapping application (Google Maps JS, Leaflet etc.). Google advices to add a meta tag <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> and if browser doesn't respect the meta tag, it is very bad browser design. The other this like very bad design is back/forward action by sliding, which cannot be prevented in iOS 9/10. It breaks severely dragging actions inside web application. Oct 13, 2016 at 18:36
  • 4
    If user start dragging with one finger then put a second finger on the screen will be able to pinch-zoom. You can disable both zoom and scroll with a preventDefault on touchmove. You can't (completely) disable zoom without disabling scroll.
    – Paolo
    Dec 12, 2016 at 22:52
  • 14
    Wow, this part (I'll paste here) was SUPER helpful to me. I haven't seen this mentioned by anyone else ANYWHERE on the internet. It took me hours to fix this problem. I'm really disappointed in Apple's UX design choices about this, where forms auto-zoom-in but then don't zoom back out. If you don't set up your form elements right, focusing on an input will auto-zoom, and since you have mostly disabled manual zoom, it will now be almost impossible to unzoom. Make sure the input font size is >= 16px.
    – Ryan
    Jan 30, 2017 at 18:13
  • 7
    It seems that this isnt working in iOS 12 anymore. Any idea how to make this work for iOS 12?
    – TJR
    Jan 7, 2019 at 13:33
85

This is a new feature in iOS 10.

From the iOS 10 beta 1 release notes:

  • To improve accessibility on websites in Safari, users can now pinch-to-zoom even when a website sets user-scalable=no in the viewport.

I expect we're going to see a JS add-on soon to disable this in some way.

21
  • 4
    This is bad... How can we make them change their mind?... why would you get UI designers then when your webapp breaks because of this.
    – Onza
    Jul 15, 2016 at 7:42
  • 7
    Bad.. I change viewport using js and block zoom only when some elements are selected on the website now it is broken due to this "decision". If someone decide to block it - there is a reason.
    – Zhenya
    Sep 14, 2016 at 17:33
  • 12
    @Karlth it's very bed for a game developer
    – Sandeep
    Sep 21, 2016 at 9:32
  • 17
    I have IOS 10.0.2, user-scalable=no does not disable zooming anymore on our website... Our main issue with zooming is with our fixed side menu.. It just breaks the layout.. Any ideas or solutions on this? I understand zooming is good for accessibility, we made zooming available on specific parts of our site using js events (hammer) & css.. I don't see why a rule has to be imposed on everyone, seems like the PC Police is starting to take over our dev world as well?!
    – webkit
    Sep 29, 2016 at 10:31
  • 58
    "The only acceptable user case would be a real web app that looks and feels like an app." - and this is, like you said, a real, acceptable use case, that isn't that rare..
    – Nebula
    Nov 1, 2016 at 3:45
27

I've been able to fix this using the touch-action css property on individual elements. Try setting touch-action: manipulation; on elements that are commonly clicked on, like links or buttons.

5
  • 1
    "manipulation" doesn't prevent pinch zoom, only double-tap zoom.
    – Moos
    Feb 14, 2017 at 0:41
  • 5
    This should be the accepted answer. you can use touch-action: none; to control all the gestures yourself.
    – Guy Sopher
    Aug 6, 2017 at 11:13
  • 5
    this is great - disabling double-tap zooming while leaving pinch zooming, which SHOULD be considered a gesture - double tapping should not. 1 dog is a dog. 1 dog + 1 dog does not a space shuttle make. It makes 2 dogs, and they do things that you'd expect 2 dogs to do. I have never expected 2 dogs to be a space shuttle. Never.
    – Larry
    Oct 13, 2017 at 14:29
  • 5
    @GuySopher iOS does not provide touch-action: none only manipulatoin, which leaves the pinch-zoom problem as it is. Nov 1, 2018 at 8:12
  • 3
    use touch-action: pan-x pan-y, fixes it.
    – Husky931
    Dec 28, 2020 at 5:42
27

The workaround that works in Mobile Safari at this time of writing, is to have the the third argument in addEventListener be { passive: false }, so the full workaround looks like this:

document.addEventListener('touchmove', function (event) {
  if (event.scale !== 1) { event.preventDefault(); }
}, { passive: false });

You may want to check if options are supported to remain backwards compatible.

9
  • 3
    Anyone tried this on iOS 12? I added the above code and it is doing nothing for my webapp. Still able to zoom in this stupid Safari. My meta viewport tag looks like this btw: <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">. Oct 24, 2018 at 8:04
  • 2
    @PatrickDaVader Yep, can't find any working solution for iOS 12 Safari. Getting seasick from all the incessant zooming. Nov 17, 2018 at 22:58
  • 4
    This one works in iOS 13. My <meta> tags include: <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover, user-scalable=no, shrink-to-fit=no" /> and <meta name="HandheldFriendly" content="true"> Aug 26, 2019 at 17:57
  • 1
    @SterlingBourne Copied your setup. Does work, but only like 90% of the time. Not sure why not all the time.
    – Hillcow
    Nov 12, 2019 at 8:40
  • 2
    @SterlingBourne response worked for me! Thanks. He should post it as an answer rather than a comment so it can be voted <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover, user-scalable=no, shrink-to-fit=no" /> and <meta name="HandheldFriendly" content="true">
    – Gene Black
    Feb 7, 2020 at 19:17
23

Use "touch-action: pan-x pan-y" to disable "pinch zoom" as well as "double tap zoom". It works on all touch screens / phones I have tested including iOS Safari 14.5.1.

body {
  touch-action: pan-x pan-y;
}

4
  • 2
    Best answer nowadays. It works perfectly.
    – kosmosan
    Sep 14, 2021 at 9:19
  • Its' not working on scrollable element but working on non-scrollable element, what is scaleable solution to avoid this issue for scrollable element Oct 1, 2021 at 12:49
  • 2
    Flawless on iOS 15
    – Michael
    Oct 23, 2021 at 6:27
  • 1
    In addition to body, you also need to set this on any element that has overflow: scroll or overflow: auto. Or just set it on *.
    – Jo Liss
    Dec 5, 2022 at 22:04
15

It appears that this behavior is supposedly changed in the latest beta, which at the time of writing is beta 6.

From the release notes for iOS 10 Beta 6:

WKWebView now defaults to respecting user-scalable=no from a viewport. Clients of WKWebView can improve accessibility and allow users to pinch-to-zoom on all pages by setting the WKWebViewConfiguration property ignoresViewportScaleLimits to YES.

However, in my (very limited) testing, I can't yet confirm this to be the case.

Edit: verified, iOS 10 Beta 6 respects user-scalable=no by default for me.

4
  • 13
    10.0.1 here. Does not respect it. What is with Apple getting rid of features that everyone needs..
    – lifwanian
    Sep 28, 2016 at 2:34
  • 1
    This refers to WKWebView not to Safari. Source: One of our map apps broke and we have no idea how to fix it. Sep 28, 2016 at 12:18
  • Aha! Apologies, I came here when searching for solution for the same bug/feature in WKWebView and kind of assumed that the original question asked about WKWebView when writing my answer. So I suppose that during one of the first beta versions, Apple changed the behavior of both WKWebView and mobile Safari, then in beta 6, they reverted the behavior of WKWebView but kept it for the mobile Safari.
    – Cellane
    Sep 29, 2016 at 13:00
  • 1
    10.0.2 does not respect user-scalable=no. I'm not sure why they would ever undo this, only to bring it back, only to remove it again. Oct 15, 2016 at 13:35
8

I spent about an hour looking for a more robust javascript option, and did not find one. It just so happens that in the past few days I've been fiddling with hammer.js (Hammer.js is a library that lets you manipulate all sorts of touch events easily) and mostly failing at what I was trying to do.

With that caveat, and understanding I am by no means a javascript expert, this is a solution I came up with that basically leverages hammer.js to capture the pinch-zoom and double-tap events and then log and discard them.

Make sure you include hammer.js in your page and then try sticking this javascript in the head somewhere:

< script type = "text/javascript" src="http://hammerjs.github.io/dist/hammer.min.js"> < /script >
< script type = "text/javascript" >

  // SPORK - block pinch-zoom to force use of tooltip zoom
  $(document).ready(function() {

    // the element you want to attach to, probably a wrapper for the page
    var myElement = document.getElementById('yourwrapperelement');
    // create a new hammer object, setting "touchAction" ensures the user can still scroll/pan
    var hammertime = new Hammer(myElement, {
      prevent_default: false,
      touchAction: "pan"
    });

    // pinch is not enabled by default in hammer
    hammertime.get('pinch').set({
      enable: true
    });

    // name the events you want to capture, then call some function if you want and most importantly, add the preventDefault to block the normal pinch action
    hammertime.on('pinch pinchend pinchstart doubletap', function(e) {
      console.log('captured event:', e.type);
      e.preventDefault();
    })
  });
</script>

1
  • I've also been trying to solve this problem when working with hammer.js, and can confirm that I could prevent the viewport zoom by adding a .preventDefault to all the hammer gesture handlers. I'm using swipe/pinch/pan/tap together, i've added it to all the handlers, i don't know whether there's a specific one that's doing the job.
    – Conan
    Oct 19, 2016 at 14:16
8

In my particular case, I am using Babylon.js to create a 3D scene and my whole page consists of one full screen canvas. The 3D engine has its own zooming functionality but on iOS the pinch-to-zoom interferes with that. I updated the the @Joseph answer to overcome my problem. To disable it, I figured out that I need to pass the {passive: false} as an option to the event listener. The following code works for me:

window.addEventListener(
    "touchmove",
    function(event) {
        if (event.scale !== 1) {
            event.preventDefault();
        }
    },
    { passive: false }
);
2
  • My use case is also a full page 3d scene with custom pinch controls. I would have been sunk if there wasn't a workaround to Apple explicitly ignoring the user-scale: no meta.
    – Nick Bilyk
    Oct 7, 2018 at 13:54
  • 1
    Life-saver - This is the only solution that works for me as of iOS 14.4.2 that still allows for multi-touch being registered - My use-case being a web game that uses joystick controls for mobile. Apr 27, 2021 at 20:05
7

We can get everything we want by injecting one style rule and by intercepting zoom events:

$(function () {
  if (!(/iPad|iPhone|iPod/.test(navigator.userAgent))) return
  $(document.head).append(
    '<style>*{cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}</style>'
  )
  $(window).on('gesturestart touchmove', function (evt) {
    if (evt.originalEvent.scale !== 1) {
      evt.originalEvent.preventDefault()
      document.body.style.transform = 'scale(1)'
    }
  })
})

✔ Disables pinch zoom.

✔ Disables double-tap zoom.

✔ Scroll is not affected.

✔ Disables tap highlight (which is triggered, on iOS, by the style rule).

NOTICE: Tweak the iOS-detection to your liking. More on that here.


Apologies to lukejackson and Piotr Kowalski, whose answers appear in modified form in the code above.

4
  • This works on my iPad emulator which is running iOS 11.2. On my real iPad running iOS 11.3 it doesn't work. I added a console.log to make sure the events are fired and so they appear in the console. It is something to do with iOS 11.3 ? Or with real devices ?
    – Mathieu R.
    Apr 18, 2018 at 3:33
  • 1
    @MathieuR. It is an iOS 11.3 issue. It can be rectified by using one of the addEventListener based answers and passing { passive: false } as the options parameter instead of false. However, for backward compatibility you need to pass false unless the passive option field is supported. See developer.mozilla.org/en-US/docs/Web/API/EventTarget/… May 17, 2018 at 17:31
  • @JoshGallagher Could you provide a working example? On iOS11 none of the answers is working for me.
    – Mick
    Jul 17, 2018 at 15:53
  • The 'gesturestart' -> preventDefault works for me at time of writing on iOS 12,2 Jul 18, 2019 at 13:43
6

I tried the previous answer about pinch-to-zoom

document.documentElement.addEventListener('touchstart', function (event) {
    if (event.touches.length > 1) {
        event.preventDefault();
    }
}, false);

however sometime the screen still zoom when the event.touches.length > 1 I found out the best way is using touchmove event, to avoid any finger moving on the screen. The code will be something like this:

document.documentElement.addEventListener('touchmove', function (event) {
    event.preventDefault();      
}, false);

Hope it will help.

4
  • 7
    This works only if your app is a perfect fit... if you do have scrollable content it doesn't works... still nice hack for some scenarios.
    – eljamz
    Sep 22, 2016 at 17:29
  • 8
    This even disables scroll on website.. BAD
    – Gags
    Sep 27, 2016 at 4:23
  • @eljamz thanks for let me know and yes..my app is perfect fit on screen. Oct 19, 2016 at 1:38
  • @Gags I didn't test the scroll function yet, thanks for let me know. Oct 19, 2016 at 1:39
6

Check for scale factor in touchove event then prevent touch event.

document.addEventListener('touchmove', function(event) {
    event = event.originalEvent || event;
    if(event.scale > 1) {
        event.preventDefault();
    }
}, false);
1
  • 3
    iOS 13 change false to {passive: false} Dec 23, 2019 at 16:23
6

I came up with a pretty naive solution, but it seems to work. My goal was to prevent accidental double-taps to be interpreted as zoom in, while keeping pinch to zoom working for accessibility.

The idea is in measuring time between the first touchstart and second touchend in a double tap and then interpreting the last touchend as click if the delay is too small. While preventing accidental zooming, this method seems to keep list scrolling unaffected, which is nice. Not sure if I haven't missed anything though.

let preLastTouchStartAt = 0;
let lastTouchStartAt = 0;
const delay = 500;

document.addEventListener('touchstart', () => {
  preLastTouchStartAt = lastTouchStartAt;
  lastTouchStartAt = +new Date();
});
document.addEventListener('touchend', (event) => {
  const touchEndAt = +new Date();
  if (touchEndAt - preLastTouchStartAt < delay) {
    event.preventDefault();
    event.target.click();
  }
});

Inspired by a gist from mutewinter and Joseph's answer.

0
5

As requested, I have transfered my comment to an answer so people can upvote it:

This works 90% of the time for iOS 13:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover, user-scalable=no, shrink-to-fit=no" />

and

<meta name="HandheldFriendly" content="true">

2
  • 4
    Why does this work 90% of the time? I tried it on mine and didn't work :( Sep 15, 2020 at 8:12
  • @JeanPaulA. You're part of the 10% then
    – Marc Dix
    Sep 14, 2023 at 7:05
1

As odd as it sounds, at least for Safari in iOS 10.2, double tap to zoom is magically disabled if your element or any of its ancestors have one of the following:

  1. An onClick listener - it can be a simple noop.
  2. A cursor: pointer set in CSS
4
  • how about pinching?
    – Sam Su
    Jan 14, 2017 at 2:41
  • Unfortunately, pinch to zoom is not covered by this solution. For that, we used the solution proposed in: stackoverflow.com/a/39594334/374196
    – mariomc
    Jan 17, 2017 at 11:42
  • Doesn't work for me. I am using 10.2.1 Beta 4 on an iPod Touch using this test page and double tapping any of the grey squares zooms: jsbin.com/kamuta/quiet
    – robocat
    Jan 22, 2017 at 23:46
  • 1
    I have this on a span in a react app and it does not work.
    – Fasani
    Feb 12, 2017 at 13:16
1

Unintentional zooming tends to happen when:

  • A user double taps on a component of the interface
  • A user interacts with the viewport using two or more digits (pinch)

To prevent the double tap behaviour I have found two very simple workarounds:

<button onclick='event.preventDefault()'>Prevent Default</button>
<button style='touch-action: manipulation'>Touch Action Manipulation</button>

Both of these prevent Safari (iOS 10.3.2) from zooming in on the button. As you can see one is JavaScript only, the other is CSS only. Use appropriately.

Here is a demo: https://codepen.io/lukejacksonn/pen/QMELXQ

I have not attempted to prevent the pinch behaviour (yet), primarily because I tend not to create multi touch interfaces for the web and secondly I have come round to the idea that perhaps all interfaces including native app UI should be "pinch to zoom"-able in places. I'd still design to avoid the user having to do this to make your UI accessible to them, at all costs.

1

Found this simple work around which appears to prevent double click to zoom:

    // Convert touchend events to click events to work around an IOS 10 feature which prevents
    // developers from using disabling double click touch zoom (which we don't want).
    document.addEventListener('touchend', function (event) {
        event.preventDefault();
        $(event.target).trigger('click');
    }, false);
1

In the current version of safari this is not working anymore. You have to define the second parameter as non-passive by passing {passiv:false}

 document.addEventListener('touchmove', function(e) {
 e.preventDefault();
}, { passive: false });
1
  • Tested on iOS 14.4 : it disables touch scrolling of the viewport so you cannot scroll the page up and down anymore. Mar 12, 2021 at 9:35
0

I checked all above answers in practice with my page on iOS (iPhone 6, iOS 10.0.2), but with no success. This is my working solution:

$(window).bind('gesturestart touchmove', function(event) {
    event = event.originalEvent || event;
    if (event.scale !== 1) {
         event.preventDefault();
         document.body.style.transform = 'scale(1)'
    }
});
2
  • Unfortunately this only works when your page is a perfect fit, not when you have scrollable content Jul 28, 2017 at 19:01
  • Hmm. This works fine with scrollable content, in my experience (iOS 10.3). Aug 3, 2017 at 20:33
0

this worked for me:

document.documentElement.addEventListener('touchmove', function (event) {
    event.preventDefault();
}, false);
1
  • I tried this, it did prevent pinch zooming, however it disables touch scrolling of the viewport so you cannot scroll the page up and down anymore.
    – TGR
    Jun 14, 2020 at 5:48

Not the answer you're looking for? Browse other questions tagged or ask your own question.