在iPhone上的Safari浏览器中禁用“文本”标签的自动缩放

863

我制作了一个包含 <input> 标签以及 type="text" 属性的 HTML 页面。当我在 iPhone 上使用 Safari 点击它时,页面会变大(自动缩放)。有人知道如何禁用此功能吗?


11
所有使用 Twitter Bootstrap 的用户注意:还可以参考这个 Github 问题 - Jeroen
78
我发誓,苹果公司是有意制造这些反功能来搞乱我们的思维。 - Andrew Koster
12
@AndrewKoster,即使到了2020年,我仍然同意你的观点。 - Ashok
13
2020年8月,我再次来到这里,希望能在回答中得到奇迹。明年再见。我要吃一个苹果。 - Marc
14
iOS 是下一个 IE。 - step
显示剩余4条评论
40个回答

7

经过一段时间的尝试,我想出了这个解决方案。

// set font-size to 16px to prevent zoom 
input.addEventListener("mousedown", function (e) {
  e.target.style.fontSize = "16px";
});

// change font-size back to its initial value so the design will not break
input.addEventListener("focus", function (e) {
  e.target.style.fontSize = "";
});

在“mousedown”事件中,它将输入框的字体大小设置为16像素。这将防止缩放。在焦点事件中,它会将字体大小更改回初始值。
与之前发布的解决方案不同,这将允许您将输入框的字体大小设置为任何您想要的大小。

这个对我来说实际上很有效,特别是在较新的iOS版本中,您无法使用视口元标记禁用缩放。 - mparizeau

7

我也用jQuery实现了这个功能:

$('input[type=search]').on('focus', function(){
  // replace CSS font-size with 16px to disable auto zoom on iOS
  $(this).data('fontSize', $(this).css('font-size')).css('font-size', '16px');
}).on('blur', function(){
  // put back the CSS font-size
  $(this).css('font-size', $(this).data('fontSize'));
});

当然,如果这个16px的字体大小破坏了设计,界面中的其他元素可能也需要进行调整。

4
这很有品位。这很时髦。我没有更多的双关语了。聪明的方法。 - crowjonah
@Wolfr 你有在实际设备上尝试过吗? - Nicolas Hoizey
1
这在我们的iOS 12上运行良好。我喜欢这种方法,而不是在css转换和负边距上胡乱尝试。 - anonymous-one

6

使用 (hover:none) 和 (pointer:coarse) 来针对所有触摸屏设备:

这里有一些答案通过部署JavaScriptjQuery来解决问题。

但是,完全可以(也确实可以)用CSS来控制条件性的字体呈现等问题。


Safari Mobile 要求(理由充足)任何表单元素在被交互时必须具有最小字体大小 16px(或视觉等效大小)。

我们承诺将这种深思熟虑的用户体验应用于所有触摸屏浏览器上。

因此我们可以采用以下方法:

@media only screen and (hover: none) and (pointer: coarse) {

  input,
  select,
  textarea {
    font-size: 11px;
  }

  input:focus,
  select:focus,
  textarea:focus {
    font-size: 16px;
  }
}

结果

当使用触摸屏设备时,如果有任何交互式表单元素被聚焦,该表单元素的font-size将暂时设置为16px

这会禁用iOS Safari Mobile的auto-zoom功能。

所有设备上的用户启动的缩放操作都不受影响,也永远不会被禁用。


6

阅读了这里几乎每一行代码并测试了各种解决方案,感谢所有提供解决方案的人,以下是我得出的、经过测试并在我的 iPhone 7 iOS 10.x 上有效的解决方案:

@media screen and (-webkit-min-device-pixel-ratio:0) {
    input[type="email"]:hover,
    input[type="number"]:hover,
    input[type="search"]:hover,
    input[type="text"]:hover,
    input[type="tel"]:hover,
    input[type="url"]:hover,
    input[type="password"]:hover,
    textarea:hover,
    select:hover{font-size: initial;}
}
@media (min-width: 768px) {
    input[type="email"]:hover,
    input[type="number"]:hover,
    input[type="search"]:hover,
    input[type="text"]:hover,
    input[type="tel"]:hover,
    input[type="url"]:hover,
    input[type="password"]:hover,
    textarea:hover,
    select:hover{font-size: inherit;}
}

然而,它也有一些缺点,明显的是“跳动”,即由于“悬停”和“聚焦”状态之间快速字体大小变化所导致的效果 - 以及对性能的重新绘制影响。


感谢您的反馈,@MikeBoutin。您能分享一下您的环境(设备/ iOS 版本)吗? - l3bel

4
即使有了这些答案,我还是用了三天的时间才弄清楚发生了什么,未来可能仍需要解决这个问题。
我的情况与描述的情况略有不同。
在我的情况下,页面上有一个 div 中的一些 contenteditable 文本。当用户点击不同的 div(某种按钮)时,我会自动选择内容可编辑的 div 中的一些文本(先前保存并清除的选择范围),对该选择运行富文本 execCommand 并再次清除它。
这使我能够根据页面上其他位置颜色 div 的用户交互而无形地更改文本颜色,同时保持选择通常隐藏以让用户在正确的上下文中看到颜色。
但是,在 iPad 的 Safari 上,单击颜色 div 会导致出现屏幕键盘,并且我所做的任何操作都无法阻止它。
最终我弄清了 iPad 是如何做到这一点的。
它监听触摸开始和结束的序列,这会触发对可编辑文本的选择。
当发生这种组合时,它会显示屏幕键盘。
实际上,它进行了一次 dolly zoom,同时放大底层页面并缩放可编辑文本。我花了一天时间才理解我所看到的东西。
因此,我使用的解决方案是拦截那些特定的颜色 div 上的触摸开始和触摸结束事件。在这两个处理程序中,我停止传播和冒泡并返回 false。但是在触摸结束事件中,我触发了与单击相同的行为。
因此,在 Safari 之前,它会依次触发“touchstart”、“mousedown”、“touchend”、“mouseup”、“click”,并且由于我的代码,文本选择也被触发。
由于拦截,新序列仅为文本选择。在 Safari 处理它并进行其键盘处理之前,所有其他内容都被拦截。触摸开始和触摸结束拦截器也会防止鼠标事件触发,在上下文中完全没有问题。
我不知道是否有更简单的方法来描述这一点,但我认为将其放在这里很重要,因为我在第一次遇到这个问题的一小时内就找到了这个线程。
我98% 确定相同的解决方法也适用于输入框和其他任何内容。拦截触摸事件并单独处理它们,而不让它们传播或冒泡,并考虑在微小的超时后执行任何选择,以确保 Safari 不将该序列识别为键盘触发器。

这是一个很好的解释,说明了Safari正在做什么。谢谢! - Jeremy

4

我曾经需要为一所荷兰大学的网站修复自动缩放到表单控件的问题(使用了15px的表单控件)。我想出了以下要求:

  • 用户仍然可以进行缩放
  • 字体大小必须保持不变
  • 没有临时不同样式的闪烁
  • 无需jQuery
  • 必须在最新的iOS上工作,并不影响其他操作系统或设备组合
  • 如果可能,不要使用超时技巧,如果需要,请正确清除计时器

这是我目前想出的方案:

/*
NOTE: This code overrides the viewport settings, an improvement would be
      to take the original value and only add or change the user-scalable value
*/

// optionally only activate for iOS (done because I havn't tested the effect under other OS/devices combinations such as Android)
var iOS = navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)
if (iOS)
  preventZoomOnFocus();


function preventZoomOnFocus()
{
  document.documentElement.addEventListener("touchstart", onTouchStart);
  document.documentElement.addEventListener("focusin", onFocusIn);
}


let dont_disable_for = ["checkbox", "radio", "file", "button", "image", "submit", "reset", "hidden"];
//let disable_for = ["text", "search", "password", "email", "tel", "url", "number", "date", "datetime-local", "month", "year", "color"];


function onTouchStart(evt)
{
  let tn = evt.target.tagName;

  // No need to do anything if the initial target isn't a known element
  // which will cause a zoom upon receiving focus
  if (    tn != "SELECT"
      &&  tn != "TEXTAREA"
      && (tn != "INPUT" || dont_disable_for.indexOf(evt.target.getAttribute("type")) > -1)
     )
    return;

  // disable zoom
  setViewport("width=device-width, initial-scale=1.0, user-scalable=0");
}

// NOTE: for now assuming this focusIn is caused by user interaction
function onFocusIn(evt)
{
  // reenable zoom
  setViewport("width=device-width, initial-scale=1.0, user-scalable=1");
}

// add or update the <meta name="viewport"> element
function setViewport(newvalue)
{
  let vpnode = document.documentElement.querySelector('head meta[name="viewport"]');
  if (vpnode)
    vpnode.setAttribute("content",newvalue);
  else
  {
    vpnode = document.createElement("meta");
    vpnode.setAttribute("name", "viewport");
    vpnode.setAttribute("content", newvalue);
  }
}

一些注意事项:

  • 注意目前我只在iOS 11.3.1上进行了测试,但很快将在其他版本上进行测试。
  • 使用focusIn事件意味着至少需要iOS 5.1(但我看到我们构建的站点在iOS版本为9或更旧时工作也是一个很棒的奖励)。
  • 使用事件委托,因为我所工作的许多网站可能会动态创建表单控件。
  • 将eventListeners设置为html元素(documentElement),以便不必等待body可用(不想麻烦检查文档是否有ready/loaded状态或需要等待DOMContentLoaded事件)。

这对我来说并不完全完美(当用户按下按钮动态添加<input>时,输入缩放被触发),但它在85%的情况下工作正常,目前已经足够好了。只是想感谢您分享您的解决方案! - Sven

3
在Angular中,您可以使用指令来防止IOS设备在聚焦时缩放。没有保留无障碍性的meta标签。
import { Directive, ElementRef, HostListener } from '@angular/core';

const MINIMAL_FONT_SIZE_BEFORE_ZOOMING_IN_PX = 16;

@Directive({ selector: '[noZoomiOS]' })

export class NoZoomiOSDirective {
  constructor(private el: ElementRef) {}

@HostListener('focus')
  onFocus() {
    this.setFontSize('');
  }

@HostListener('mousedown')
  onMouseDown() {
    this.setFontSize(`${MINIMAL_FONT_SIZE_BEFORE_ZOOMING_IN_PX}px`);
  }

private setFontSize(size: string) {
  const { fontSize: currentInputFontSize } = window.getComputedStyle(this.el.nativeElement, null);

  if (MINIMAL_FONT_SIZE_BEFORE_ZOOMING_IN_PX <= +currentInputFontSize.match(/\d+/)) {
      return;
   }

  const iOS = navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
  iOS 
     && (this.el.nativeElement.style.fontSize = size);
 }
}

在声明了*.module.ts之后,您可以像这样使用它:<input noZoomiOS >

3

太棒了,这里有很多与JavaScript和视口有关的答案,只有另外一个提到了 text-size-adjust,我认为这是最好的解决方案。

你只需要将它设置为none

添加以下CSS:

* {
 -webkit-text-size-adjust: none;
  text-size-adjust: none;
}

这似乎在iOS上无法正常工作,是否还需要其他要求才能成功应用解决方案 - 比如适当值的meta viewport? - jimmont
7
很遗憾,在我的Safari iOS 14.4上这个方法无效。 - Elliot Plant
根据developer.mozilla.org的说法,text-size-adjust仍被视为实验性质,但当前移动版Chrome和Safari已支持该属性。请务必查看caniuse.com - rocky
这个 CSS 属性和自动缩放有什么关系吗?它的作用是控制渲染字体大小,而不是屏幕缩放。 - Adam Jagosz
@adam 是的,它会增加输入框的字体大小,从而导致自动缩放。 - mike nelson
显示剩余2条评论

2
我看到这里的人们在JavaScript或视口功能中做一些奇怪的事情,并关闭设备上的所有手动缩放。在我看来,这不应该是一个解决方案。添加这个CSS片段将关闭iOS中的自动缩放,而无需将字体大小更改为固定数字,如16px。
默认情况下,我在输入字段中使用93.8%(15px)的字体大小,并通过添加我的CSS片段使其保持在93.8%。无需更改为16px或使其成为固定数字。
input[type="text"]:focus,
textarea:focus {
    -webkit-text-size-adjust: 100%;
}

5
这对我没有效果,测试了最新的iOS 6和iOS 9.2.1版本。这是一个最小可重现页面:http://pastebin.com/bh5Zhe9h 它仍然会在焦点上缩放。奇怪的是,这篇文章是在2015年发布的并且得到了赞成,但在iOS 6中却不能使用。 - Alexandre Dieulot

2
顺便提一下,如果您使用 Bootstrap,您可以直接使用以下变体:
.form-control {
  font-size: 16px;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接