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

Sam*_*ron 91 javascript css safari ios ios8

在IOS8 Safari中,有一个新的位置修复错误.

如果您聚焦固定面板中的文本区域,Safari会将您滚动到页面底部.

这使得所有类型的UI都无法使用,因为您无法将文本输入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;
}
Run Code Online (Sandbox Code Playgroud)
<html>
   <body>
   <div id="a"></div>
   <div id="b"><textarea></textarea></div>
   </body>
</html>
Run Code Online (Sandbox Code Playgroud)

Moh*_*nna 55

基于对这个问题的这个好分析,我在css中使用了这个htmlbody元素:

html,body{
    -webkit-overflow-scrolling : touch !important;
    overflow: auto !important;
    height: 100% !important;
}
Run Code Online (Sandbox Code Playgroud)

我觉得它对我很有用.

  • 也为我工作.这搞砸了很多其他的事情,因为我在加载时操纵我的DOM,所以我把它变成了一个类,并在DOM稳定后将它添加到html,body中.像scrollTop这样的东西不能很好地工作(我正在进行自动滚动),但同样,你可以在进行滚动操作时添加/删除类.但Safari团队的部分工作不佳. (2认同)

Ale*_*ara 34

我能想到的最好的解决方案是切换到使用position: absolute;焦点并计算它在使用时的位置position: fixed;.诀窍是focus事件发生得太晚,所以touchstart必须使用.

这个答案中的解决方案非常接近地模仿了iOS 7中的正确行为.

要求:

body元件必须具有定位以确保正确的定位时,元件切换到绝对定位.

body {
    position: relative;
}
Run Code Online (Sandbox Code Playgroud)

代码(实例):

以下代码是所提供测试用例的基本示例,可以根据您的具体用例进行调整.

//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);
});
Run Code Online (Sandbox Code Playgroud)

这是相同的代码,没有用于比较的黑客.

警告:

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

建议:

虽然使用该touchstart事件将过滤掉大多数桌面环境,但您可能希望使用用户代理嗅探,以便此代码仅针对损坏的iOS 8运行,而不是针对其他设备(如Android和较旧的iOS版本)运行.不幸的是,我们还不知道Apple何时会在iOS中修复此问题,但如果在下一个主要版本中没有修复它,我会感到惊讶.

  • 这对我不起作用,输入字段仍在移动. (4认同)

Dan*_*non 8

我找到了一种无需改变绝对位置即可工作的方法!

完整的未注释代码

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);
    }
});
Run Code Online (Sandbox Code Playgroud)

打破它

首先,您需要在HTML中将固定输入字段放在页面顶部(这是一个固定元素,因此无论如何它在语义上应该是有意义的):

<!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>
Run Code Online (Sandbox Code Playgroud)

然后,您需要将当前滚动位置保存到全局变量中:

//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;
Run Code Online (Sandbox Code Playgroud)

然后你需要一种方法来检测iOS设备,这样它就不会影响不需要修复的东西(函数来自/sf/answers/632791981/)

//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;
}
Run Code Online (Sandbox Code Playgroud)

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

//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);
    }
});
Run Code Online (Sandbox Code Playgroud)


Sam*_*ron 0

该问题现已在 iOS 10.3 中修复!

不再需要黑客了。

  • 我在 iOS 11 上仍然遇到这个问题 (3认同)
  • 不,即使在 iOS 13 上这仍然是一个问题。 (3认同)