ios8中的Safari是固定的元素获得焦点时的滚动屏幕

在IOS8 Safari中有一个固定位置的新bug。

如果您将焦点放在固定面板中的textarea,safari会将您滚动到页面的底部。

这使得各种用户界面无法使用,因为你没有办法input文本到textareas没有滚动页面一路下降,失去你的地方。

有没有办法彻底解决这个bug?

#a { height: 10000px; background: linear-gradient(red, blue); } #b { position: fixed; bottom: 20px; left: 10%; width: 100%; height: 300px; } textarea { width: 80%; height: 300px; } 
 <html> <body> <div id="a"></div> <div id="b"><textarea></textarea></div> </body> </html> 

基于这个问题的良好分析 ,我已经在CSS和html元素中使用了这个:

 html,body{ -webkit-overflow-scrolling : touch !important; overflow: auto !important; height: 100% !important; } 

我认为这对我很好。

我能想出的最好的解决scheme是切换到使用position: absolute; 重点和计算它在使用position: fixed;时的position: fixed; 。 关键是focus事件太晚了,所以必须使用touchstart

这个答案的解决scheme非常接近地模仿我们在iOS 7中的正确行为。

要求:

当元件切换到绝对位置时, body元件必须具有定位以确保适当的定位。

 body { position: relative; } 

代码 ( 实例 ):

以下代码是提供的testing用例的基本示例,可以根据您的具体使用情况进行调整。

 //Get the fixed element, and the input element it contains. var fixed_el = document.getElementById('b'); var input_el = document.querySelector('textarea'); //Listen for touchstart, focus will fire too late. input_el.addEventListener('touchstart', function() { //If using a non-px value, you will have to get clever, or just use 0 and live with the temporary jump. var bottom = parseFloat(window.getComputedStyle(fixed_el).bottom); //Switch to position absolute. fixed_el.style.position = 'absolute'; fixed_el.style.bottom = (document.height - (window.scrollY + window.innerHeight) + bottom) + 'px'; //Switch back when focus is lost. function blured() { fixed_el.style.position = ''; fixed_el.style.bottom = ''; input_el.removeEventListener('blur', blured); } input_el.addEventListener('blur', blured); }); 

这里是相同的代码没有黑客比较 。

警告:

如果position: fixed; 元素除了body之外还有其他任何具有定位的父元素,切换到position: absolute; 可能有意想不到的行为 由于position: fixed;的性质position: fixed; 这可能不是一个大问题,因为嵌套这样的元素并不常见。

build议:

尽pipetouchstart事件的使用会过滤掉大部分的桌面环境,但是您可能需要使用用户代理嗅探,以便此代码只能运行在破损的iOS 8上,而不能运行在其他设备(如Android和较旧的iOS版本)上。 不幸的是,我们还不知道苹果什么时候会在iOS上解决这个问题,但是如果在下一个主要版本中没有解决,我会感到惊讶。

我发现了一种不需要改变绝对位置的方法!

完整的未注释的代码

 var scrollPos = $(document).scrollTop(); $(window).scroll(function(){ scrollPos = $(document).scrollTop(); }); var savedScrollPos = scrollPos; function is_iOS() { var iDevices = [ 'iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod' ]; while (iDevices.length) { if (navigator.platform === iDevices.pop()){ return true; } } return false; } $('input[type=text]').on('touchstart', function(){ if (is_iOS()){ savedScrollPos = scrollPos; $('body').css({ position: 'relative', top: -scrollPos }); $('html').css('overflow','hidden'); } }) .blur(function(){ if (is_iOS()){ $('body, html').removeAttr('style'); $(document).scrollTop(savedScrollPos); } }); 

打破了

首先,你需要在HTML页面的顶部有一个固定的input字段(这是一个固定的元素,所以它应该在语义上有意义,让它靠近顶部)

 <!DOCTYPE HTML> <html> <head> <title>Untitled</title> </head> <body> <form class="fixed-element"> <input class="thing-causing-the-issue" type="text" /> </form> <div class="everything-else">(content)</div> </body> </html> 

那么你需要将当前的滚动位置保存到全局variables中:

 //Always know the current scroll position var scrollPos = $(document).scrollTop(); $(window).scroll(function(){ scrollPos = $(document).scrollTop(); }); //need to be able to save current scroll pos while keeping actual scroll pos up to date var savedScrollPos = scrollPos; 

那么你需要一种方法来检测iOS设备,所以它不会影响不需要修复的东西(函数取自https://stackoverflow.com/a/9039885/1611058

 //function for testing if it is an iOS device function is_iOS() { var iDevices = [ 'iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod' ]; while (iDevices.length) { if (navigator.platform === iDevices.pop()){ return true; } } return false; } 

现在,我们有我们需要的一切,这里是修复:)

 //when user touches the input $('input[type=text]').on('touchstart', function(){ //only fire code if it's an iOS device if (is_iOS()){ //set savedScrollPos to the current scroll position savedScrollPos = scrollPos; //shift the body up a number of pixels equal to the current scroll position $('body').css({ position: 'relative', top: -scrollPos }); //Hide all content outside of the top of the visible area //this essentially chops off the body at the position you are scrolled to so the browser can't scroll up any higher $('html').css('overflow','hidden'); } }) //when the user is done and removes focus from the input field .blur(function(){ //checks if it is an iOS device if (is_iOS()){ //Removes the custom styling from the body and html attribute $('body, html').removeAttr('style'); //instantly scrolls the page back down to where you were when you clicked on input field $(document).scrollTop(savedScrollPos); } }); 

我能够通过添加一个事件监听器到必要的select元素,然后滚动一个像素的偏移量,当有问题的select获得焦点时,可以解决这个问题。

这不一定是一个好的解决scheme,但它比我在这里看到的其他答案更简单,更可靠。 浏览器似乎重新渲染/重新计算位置:固定; 属性基于window.scrollBy()函数中提供的偏移量。

 document.querySelector(".someSelect select").on("focus", function() {window.scrollBy(0, 1)}); 

就像Mark Ryan Salleebuild议的那样,我发现dynamic改变我的背景元素的高度和溢出是关键 – 这使得Safari无法滚动到。

所以在模态开始animation完成后,改变背景的样式:

 $('body > #your-background-element').css({ 'overflow': 'hidden', 'height': 0 }); 

当你closures模式更改回来:

 $('body > #your-background-element').css({ 'overflow': 'auto', 'height': 'auto' }); 

虽然其他答案在简单的上下文中是有用的,但我的DOM太复杂了(谢谢SharePoint)使用绝对/固定位置交换。

这现在已经在iOS 10.3中修复了!

应该不再需要黑客。

干净? 没有。

我最近有一个固定的search字段在一个粘滞的标题自己的问题,你现在可以做的最好的方式是始终保持在一个variables的滚动位置和select使固定元素的位置绝对而不是固定的顶部位置基于文档的滚动位置。

然而,这是非常丑陋的,仍然会导致一些奇怪的来回滚动,然后着陆在正确的地方,但这是我能得到的最接近的。

任何其他解决scheme将涉及覆盖浏览器的默认滚动机制。

没有处理这个特定的错误,但也许把溢出:隐藏; 当文本区域可见时(或者只是活动,取决于您的devise)。 这可能会导致不向浏览器的任何地方“向下”滚动浏览。

一个可能的解决scheme是replaceinput字段。

  • 监视一个div上的点击事件
  • 集中隐藏的input字段来渲染键盘
  • 将隐藏的input字段的内容复制到假input字段中
 function focus() { $('#hiddeninput').focus(); } $(document.body).load(focus); $('.fakeinput').bind("click",function() { focus(); }); $("#hiddeninput").bind("keyup blur", function (){ $('.fakeinput .placeholder').html(this.value); }); 
 #hiddeninput { position:fixed; top:0;left:-100vw; opacity:0; height:0px; width:0; } #hiddeninput:focus{ outline:none; } .fakeinput { width:80vw; margin:15px auto; height:38px; border:1px solid #000; color:#000; font-size:18px; padding:12px 15px 10px; display:block; overflow:hidden; } .placeholder { opacity:0.6; vertical-align:middle; } 
 <input type="text" id="hiddeninput"></input> <div class="fakeinput"> <span class="placeholder">First Name</span> </div> 

这些解决scheme都没有为我工作,因为我的DOM是复杂的,我有dynamic的无限滚动页面,所以我必须创build自己的。

背景:我使用一个固定的标题和一个下面的元素,一旦用户向下滚动,就会粘在它下面。 这个元素有一个searchinput字段。 另外,在向前和向后滚动时添加了dynamic页面。

问题:在iOS中,只要用户点击了固定元素中的input,浏览器就会一直滚动到页面的顶部。 这不仅造成了不良的行为,还触发了页面顶部的dynamic页面添加。

预期的解决scheme:当用户点击sticky元素中的input时,在iOS中不滚动(根本没有)。

解:

  /*Returns a function, that, as long as it continues to be invoked, will not be triggered. The function will be called after it stops being called for N milliseconds. If `immediate` is passed, trigger the function on the leading edge, instead of the trailing.*/ function debounce(func, wait, immediate) { var timeout; return function () { var context = this, args = arguments; var later = function () { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; function is_iOS() { var iDevices = [ 'iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod' ]; while (iDevices.length) { if (navigator.platform === iDevices.pop()) { return true; } } return false; } $(document).on("scrollstop", debounce(function () { //console.log("Stopped scrolling!"); if (is_iOS()) { var yScrollPos = $(document).scrollTop(); if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px) $('#searchBarDiv').css('position', 'absolute'); $('#searchBarDiv').css('top', yScrollPos + 50 + 'px'); //50 for fixed header } else { $('#searchBarDiv').css('position', 'inherit'); } } },250,true)); $(document).on("scrollstart", debounce(function () { //console.log("Started scrolling!"); if (is_iOS()) { var yScrollPos = $(document).scrollTop(); if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px) $('#searchBarDiv').css('position', 'fixed'); $('#searchBarDiv').css('width', '100%'); $('#searchBarDiv').css('top', '50px'); //50 for fixed header } } },250,true)); 

要求: jQuery mobile是startsroll和stopscroll函数工作所必需的。

反弹是为了消除由粘滞元素产生的任何滞后。

在iOS10中testing

昨天我刚刚跳过这样的事情,当#b可见时,将#a的高度设置为最大可见高度(身高是在我的情况下)

例如:

  <script> document.querySelector('#b').addEventListener('focus', function () { document.querySelector('#a').style.height = document.body.clientHeight; }) </script> 

ps:对于后来的例子抱歉,只是注意到它是需要的。

我有这个问题,下面的代码行解决了我 –

 html{ overflow: scroll; -webkit-overflow-scrolling: touch; }