iOS Safari/Chrome - 在模式内聚焦输入时不需要的滚动

Posted

技术标签:

【中文标题】iOS Safari/Chrome - 在模式内聚焦输入时不需要的滚动【英文标题】:iOS Safari/Chrome - Unwanted scrolling when focusing an input inside the modal 【发布时间】:2016-09-14 04:54:23 【问题描述】:

在 Safari 和 Chrome 中测试 - 结果相同,所以我认为这是 ios 问题。

仅当模态框内有输入并且我点击该输入时才会发生这种情况。在输入获得焦点的同一时刻,原生 iOS 键盘变得可见。

模态下方的页面在同一时刻自动滚动到其高度的 50%。这种行为是完全不受欢迎的,我不知道如何阻止这个默认的 iOS“功能”。

演示:

【问题讨论】:

使用 DOMListener for Chrome 获取 DOM 更改日志。我在打开模式和添加 1 种媒体查询样式时进行了 59 处更改。还要检查调整大小处理程序。位置固定或溢出隐藏也可能是问题。 感谢@Pinal 的回复!我尝试将overflow: hidden 动态添加到正文中,但在这种情况下没有帮助。可能position: fixed 是问题所在。您能否详细解释一下为什么会出现问题以及如何解决。 刚刚注意到position:fixed 在iOS CoreGraphics 渲染中存在一些问题。在您的情况下,我建议您将所有 position: fixed 更改为绝对值并再次测试。您也可以在 github 上寻找解决方案(例如 - github.com/scottjehl/Device-Bugs/…) 我注意到这个函数 close(i, expectedNumberOfNonCommentArgs) 会影响这个东西,当你尝试评论它时,Safari 中没有 bg 滚动。 【参考方案1】:

只需在此处添加答案,以防人们偶然发现此问题 (rather than your other question, which has a great demo to illustrate this issue)

我们在工作中也面临着类似的问题。正如您所提到的,偏移量始终约为页面高度的 50%,无论您的初始偏移量在哪里,都会发生这种情况。

在过去,当我观察到与早期 iOS 版本类似的“跳跃”(尽管没有那么剧烈的跳跃)时,我通常通过将position: fixed(或relative)应用于body 来解决这个问题(which allows overflow: hidden to properly work)。

但是,如果用户向下滚动,这会导致用户自动返回页面顶部。

所以,如果您愿意通过 javascript 来解决这个问题,这里是我整理的修复/hack:

// Tapping into swal events
onOpen: function () 
  var offset = document.body.scrollTop;
  document.body.style.top = (offset * -1) + 'px';
  document.body.classList.add('modal--opened');
,
onClose: function () 
  var offset = parseInt(document.body.style.top, 10);
  document.body.classList.remove('modal--opened');
  document.body.scrollTop = (offset * -1);

CSS 是什么样子的:

.modal--opened 
  position: fixed;
  left: 0;
  right: 0;

这是您演示页面的一个分支(来自您的其他问题),用于说明:https://jpattishall.github.io/sweetalert2/ios-bug.html

对于那些正在寻找更通用修复的人,您可以在打开/关闭模式时执行以下操作:

function toggleModal() 
    var offset;
    if (document.body.classList.contains('modal--opened')) 
        offset = parseInt(document.body.style.top, 10);
        document.body.classList.remove('modal--opened');
        document.body.scrollTop = (offset * -1);
     else 
        offset = document.body.scrollTop;
        document.body.style.top = (offset * -1) + 'px';
        document.body.classList.add('modal--opened');
    

编辑:为了避免桌面上的“移动/取消移动”,我建议功能检测/ua 嗅探将其仅应用于移动 safari。

【讨论】:

在 IE 中仅供参考,document.body.scrollTop 始终为 0,并且设置它不会滚动。改用window.scroll(x,y),在IE中通过window.pageYOffset获取scrollTop【参考方案2】:

我也遇到过这个问题。一个简短的解释是,iOS Safari 将尝试自动滚动到任何聚焦的输入,在这种特殊情况下,聚焦元素位于固定定位的元素内。当 Safari 想要使元素在屏幕上居中因此背景滚动时,它似乎很难找到固定位置的元素。

一种可能的解决方法是将touchstart 事件侦听器添加到输入字段并计算叠加层的当前位置,将定位更改为绝对位置并更新顶部/左侧位置以将叠加层放回原来的位置固定位置时的屏幕,最后添加一个新的blur 侦听器,以在输入焦点/模糊时将覆盖重置回固定位置。这应该可以防止页面滚动。我建议使用touchstart,因为它应该在focus 事件之前触发,在这种情况下页面已经开始滚动。触发focus 事件时,Safari 应该能够找到绝对定位的叠加层并将其居中。

【讨论】:

【参考方案3】:

所以我已经解决了这个问题并在我的 Iphone 5 上进行了测试,没有 Ipad 可以检查。 我在我的解决方案中禁用了overflow:hidden,您可以添加,如果您想对所有模式禁用全部滚动。 解决方案是向 html 和 body 元素添加基本的 height 和 position 属性。

html, body 
   position:relative;
   /*overflow:hidden;*/
   height: 100%;

所以,只有当您专注于输入时,才会定义高度和位置,我已经从您的存储库中编写了此解决方案,我将向您发送拉取请求。 我还在您的 gulp 设置中添加了 browserSync。因此,现在可以很容易地在任何设备上进行测试。

干杯!

编辑:另一种方式,如果上述解决方案由于某种原因不起作用。那么,

 /*
  * @summary touch handler; will remove touch ability
  * @author yeomann
  */
  function Touchyhandler(e) 
    e.preventDefault();
  

然后像这样务实地添加和删除触摸侦听器

//to add
document.addEventListener('touchmove', Touchyhandler, false); 
//to remove   
document.removeEventListener('touchmove', Touchyhandler);

在 IOS 9.3.2 上测试的上述 js 解决方案对我来说就像魅力一样]

【讨论】:

【参考方案4】:

为了阻止页面在 x 和 y 轴上滚动,我们在 css 中使用overflow: hidden; 属性。

所以如果我们把它应用到身体上,

body 
    overflow: hidden !important;

它应该可以正常工作吗?

事实上,这实际上是行不通的,因为您一直禁用整个页面的 x 和 y 滚动。

要绕过这一点,我们可以在模态框处于活动状态时使用一点 javascript 向 body 添加一个类。

首先我们必须给body添加一个id,<body id="body">这允许javascript识别body。

其次,我们必须为我们的模态添加一个 id,<div id="modal">,同时允许 javascript 识别模态。

<script type="text/javascript">
    function modalActive() 
        if (document.getElementById("modal").classList.contains("active")) 
            document.getElementById("body").classList.add("modal-active");
         else 
            getElementById("modal").classList.remove("active"));
            getElementById("body").classList.remove("modal-active"));           
        
    
</script>

对于启动和关闭模式的按钮,我们必须添加一个onclick事件,

&lt;button onclick="modalActive()"&gt;Click Me!&lt;/button&gt;

除此之外,我们必须将其添加到 css 文件中。

body 
    overflow: initial !important;


body.modal-active 
    overflow: hidden !important;

我们去吧。

【讨论】:

【参考方案5】:

我不是 100% 确定,但我可以想象以下情况:

当键盘出现时,窗口高度减小,但scrollTop值还是一样,所以网站跳转到那个值。当模式打开时,您可以将overflow:hidden 添加到body。这将阻止模式“背后”的滚动,并可能解决您的问题。

【讨论】:

感谢@Kilian 的回复!不幸的是,这个技巧并不能解决我的问题。除此之外,当我将overflow: hidden 添加到正文时,在桌面浏览器中会出现此负面影响i.imgur.com/HDOxzmm.gif(正文在滚动条显示/隐藏时左右移动)。 嗯,您可以将 overflow 添加到 #wrapper 而不是 body 以避免该问题。然后,将position:absolute 添加到模态而不是fixed。你已经试过了吗?但是,如果您添加 position:absolute 而不添加 overflow:hidden,则打开模式时页面将跳转到顶部。【参考方案6】:

我尝试了多种方法,这些方法适用于 html 页面上的弹跳模式输入。对我有用的最简单的解决方案是在页面上打开模式时向 body 标签添加以下样式

position: fixed;
width: 100%;

【讨论】:

【参考方案7】:

添加此元 (ma​​ximum-scale=1, minimum-scale=1 ) 以重置滚动,这是在聚焦输入字段时发生的。

【讨论】:

【参考方案8】:

我在升级到 iPadOS 13 后遇到了这个问题。我必须删除以下旧 JavaScript 代码,这是修复旧版本中不同的滚动问题所必需的:

function fixIpadKeyboardScrolling() 
   if ((Device.is("ipad") || Device.is("iphone"))) 
    console.log("Fixing iPad/iPhone floating bar bug");
    var inputs = document.getElementsByTagName("input");
    if (inputs.length) 
      for (var i = inputs.length - 1; i >= 0; i--) 
        addListener(inputs[i], "blur", function () 
          setTimeout(function () 
              window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
          , 0);
        );
      
    
    console.log(inputs.length + " input controls where edited to fix keyboard scrolling");
  

如果您在升级后突然遇到此问题,您可能将类似的代码应用于您的应用程序。

在我的例子中,我添加了对 safari mobile 13 版本的检测,并且只为低于该版本的版本运行脚本。

【讨论】:

以上是关于iOS Safari/Chrome - 在模式内聚焦输入时不需要的滚动的主要内容,如果未能解决你的问题,请参考以下文章

移动 iOS 和 Android Safari/Chrome 之间的 JavaScript 差异

在 iOS 13 safari/chrome 浏览器中播放音频 (mp3) 的延迟

iOS Safari/Chrome 不会向上滚动以在无线电输入上显示验证错误消息

Safari/Chrome调试WebView

Safari iOS 5 中不显示自定义 webfont 字符

Angular 2,iOS Safari 错误:eval@[native code]