iOS 5固定位置和虚拟键盘


138

我有一个移动网站,该网站的div通过position:fixed固定在屏幕底部。在iOS 5(我正在iPod Touch上进行测试)中,所有方法都可以正常工作,直到出现带有表单的页面为止。当我点击输入字段并出现虚拟键盘时,突然我的div的固定位置丢失了。现在,只要可见键盘,div就会随页面滚动。单击“完成”以关闭键盘后,div将恢复到其在屏幕底部的位置,并遵守position:fixed规则。

有没有其他人经历过这种行为?这是预期的吗?谢谢。


是的,看来苹果公司对于IOS5并不满意。虚拟键盘出现后,所有固定位置元素都会相对于页面。如果元素恢复到绝对位置可能不会有问题,因为这不会破坏布局。不幸的是,这些元素的实际放置远不能预测。我在[已编辑]上的固定标题存在这个确切的问题。向下滚动页面,然后单击搜索框,然后...布局已损坏。我什至试图通过将焦点事件恢复为绝对定位来解决问题,这种方法可以正常工作,但随后我失去了
John williams

2
我遇到了同样的问题。是否有人向Apple提交了错误报告,以了解如何解决此问题?另外,是否有人在iOS6中看到这种行为?
罗伯特·许

1
我遇到了与iOS6相同的问题。
Redtopia

24
iOS7中似乎仍然存在相同的问题!
Ria Weyprecht

5
似乎未在iOS 8中修复...
contactmatt 2014年

Answers:


49

我的应用程序中有这个问题。这是我的解决方法:

input.on('focus', function(){
    header.css({position:'absolute'});
});
input.on('blur', function(){
    header.css({position:'fixed'});
});

我只是滚动到顶部并将其放置在此处,因此iOS用户不会注意到发生了什么奇怪的事情。将其包装在某些用户代理检测中,以便其他用户不会出现此行为。


从字面上看,今天偶然发现了这个问题,这似乎是最合理的+1。
丹尼尔(Daniel)

3
效果很好。我只为iOS设备称呼它:var isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
Redtopia 2013年

8
我还删除了$(window).scrollTop(0)...我认为这不是必需的,它会导致不必要的滚动。
Redtopia

1
或者,document.body.scrollTop = 0如果您不使用jQuery并定位到最新的浏览器,则很简单。
兰登

在较长的表格上,设置$(window).scrollTop(0);会导致焦点输入移出屏幕,例如页面下方的输入,或者iPhone是纵向的。更好的解决方案是聚焦设置header.css({position:'absolute',top:window.pageYOffset+'px'});和模糊设置header.css({position:'fixed',top:0});
robocat

16

我在ipad上有一个略有不同的问题,其中虚拟键盘将我的视口推到了屏幕外。然后,在用户关闭虚拟键盘后,我的视口仍在屏幕外。就我而言,我做了以下事情:

var el = document.getElementById('someInputElement');
function blurInput() {
    window.scrollTo(0, 0);
}
el.addEventListener('blur', blurInput, false);

3
对我无济于事:(
德米特里

@Altaveron。很遗憾听到这个消息。这是针对我在ios6和更低版本的设备上遇到的特定问题。从那以后我没有再访问过。
ds111 2014年

14

这是我们用来解决ipad问题的代码。它基本上可以检测偏移量和滚动位置之间的差异-这意味着“固定”无法正常工作。

$(window).bind('scroll', function () {
    var $nav = $(".navbar")
    var scrollTop = $(window).scrollTop();
    var offsetTop = $nav.offset().top;

    if (Math.abs(scrollTop - offsetTop) > 1) {
        $nav.css('position', 'absolute');
        setTimeout(function(){
            $nav.css('position', 'fixed');
        }, 1);
    }
});

它来自jquery.timers插件。您可以改用window.setTimeout。
2013年

在输入模糊和聚焦上触发相同的功能也将有所帮助。
Blowsie 2013年

天才-真的很明白这一点。还要对@Blowsie的使用焦距和模糊而不是滚动的建议+1。
Skone

12

当键盘向上时,位置固定元素根本不会更新其位置。我发现通过诱使Safari认为页面已调整大小,元素将重新定位。它并不完美,但是至少您不必担心切换到“位置:绝对”并跟踪自己的变化。

下面的代码仅侦听用户何时可能使用键盘(由于输入已被聚焦),直到听到模糊,它只会侦听任何滚动事件,然后执行调整大小的技巧。到目前为止,看来对我来说还算不错。

    var needsScrollUpdate = false;
    $(document).scroll(function(){
        if(needsScrollUpdate) {
            setTimeout(function() {
                $("body").css("height", "+=1").css("height", "-=1");
            }, 0);
        }
    });
    $("input, textarea").live("focus", function(e) {
        needsScrollUpdate = true;
    });

    $("input, textarea").live("blur", function(e) {
        needsScrollUpdate = false;
    });

在iPad上,这实际上不适用于我。我最终将固定页脚的位置更改为相对位置,并将页脚放在页面底部,直到将位置更改回固定位置时检测到输入模糊事件。
Akrikos 2012年

6
iOS不再可用,iOS可能修复了导致此工作的任何错误
Kevin

6

以防万一有人像我在研究此问题时所做的那样发生在此线程上。我发现此线程有助于激发我对此问题的思考。

这是我在最近的项目中的解决方案。您只需要将“ targetElem”的值更改为表示您的标头的jQuery选择器即可。

if(navigator.userAgent.match(/iPad/i) != null){

var iOSKeyboardFix = {
      targetElem: $('#fooSelector'),
      init: (function(){
        $("input, textarea").on("focus", function() {
          iOSKeyboardFix.bind();
        });
      })(),

      bind: function(){
            $(document).on('scroll', iOSKeyboardFix.react);  
                 iOSKeyboardFix.react();      
      },

      react: function(){

              var offsetX  = iOSKeyboardFix.targetElem.offset().top;
              var scrollX = $(window).scrollTop();
              var changeX = offsetX - scrollX; 

              iOSKeyboardFix.targetElem.css({'position': 'fixed', 'top' : '-'+changeX+'px'});

              $('input, textarea').on('blur', iOSKeyboardFix.undo);

              $(document).on('touchstart', iOSKeyboardFix.undo);
      },

      undo: function(){

          iOSKeyboardFix.targetElem.removeAttr('style');
          document.activeElement.blur();
          $(document).off('scroll',iOSKeyboardFix.react);
          $(document).off('touchstart', iOSKeyboardFix.undo);
          $('input, textarea').off('blur', iOSKeyboardFix.undo);
      }
};

};

由于iOS会在滚动时停止DOM操作,因此修复工作会稍有延迟,但这确实可以解决问题。


在iOS 7之前,它都可以正常工作。其他人在元素放置正确但消失时遇到问题吗?
罗娜·基尔默

@RonaKilmer更改init为常规函数(不是自调用;触发得太早了)。然后iOSKeyboardFix.init();if语句结束之前调用。
cfree

@cfree谢谢,我不久前实现了,但这不是我的问题。还有其他事情发生。重新定位后,我的固定元素之一消失了。另一个没有。我不能将其归咎于键盘修复,因为它并非在所有地方都发生。我也没有看到其他任何人也遇到同样的问题,所以我必须更深入地研究。
罗娜·基默

4

我没有找到针对此错误的其他答案对我有用。我可以通过将页面向上滚动34px(移动浏览器向下滚动它的数量)来解决此问题。使用jQuery:

$('.search-form').on('focusin', function(){
    $(window).scrollTop($(window).scrollTop() + 34);
});

这显然会在所有浏览器中生效,但可以防止它在iOS中损坏。


4

这个问题真烦人。

我结合了上述一些技巧,并提出了以下建议:

$(document).on('focus', 'input, textarea', function() {
    $('.YOUR-FIXED-DIV').css('position', 'static');
});

$(document).on('blur', 'input, textarea', function() {
    setTimeout(function() {
        $('.YOUR-FIXED-DIV').css('position', 'fixed');
        $('body').css('height', '+=1').css('height', '-=1');
    }, 100);
});

我有两个固定的导航栏(使用twitter bootstrap的页眉和页脚)。两者在键盘启动时都表现得很奇怪,而在键盘按下后又表现得很奇怪。

使用此定时/延迟修复程序可以正常工作。我仍然偶尔会发现一个小故障,但似乎足以将其显示给客户。

让我知道这是否适合您。如果没有,我们可能会发现其他东西。谢谢。


2
您似乎不需要该超时(至少对我而言没有超时),并且如果在选择的元素上也发生这种超时,则除了此解决方案效果很好之外。
菲利普·古奇

3

我在iOS7上遇到了同样的问题。底部固定元素会使我的视线混乱,无法正确聚焦。

当我将此meta标签添加到我的html中时,所有内容都开始工作。

<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no,height=device-height" >

与众不同的部分是:

height=device-height

希望能对某人有所帮助。


谢谢@pasevin。这解决了我在iOS 9.3.2中的问题
stillatmylinux '16

3

我已经Jory Cunningham回答并改进了它:

在许多情况下,不仅一个元素发疯了,而且几个固定位置的元素也变得疯狂,因此在这种情况下,targetElem应该是一个jQuery对象,其中包含您要“修复”的所有固定元素。,这似乎会使iOS键盘在您滚动时消失。

不用说,您应该在AFTER document DOM ready事件之后或在结束</body>标记之前使用它。

(function(){
    var targetElem = $('.fixedElement'), // or more than one
        $doc       = $(document),
        offsetY, scrollY, changeY;

    if( !targetElem.length || !navigator.userAgent.match(/iPhone|iPad|iPod/i) )
        return;

    $doc.on('focus.iOSKeyboardFix', 'input, textarea, [contenteditable]', bind);

    function bind(){
        $(window).on('scroll.iOSKeyboardFix', react);
        react();
    }

    function react(){
        offsetY = targetElem.offset().top;
        scrollY = $(window).scrollTop();
        changeY = offsetY - scrollY;

        targetElem.css({'top':'-'+ changeY +'px'});

        // Instead of the above, I personally just do:
        // targetElem.css('opacity', 0);

        $doc.on('blur.iOSKeyboardFix', 'input, textarea, [contenteditable]', unbind)
            .on('touchend.iOSKeyboardFix', unbind);
    }

    function unbind(){
        targetElem.removeAttr('style');
        document.activeElement.blur();

        $(window).off('scroll.iOSKeyboardFix');
        $doc.off('touchend.iOSKeyboardFix blur.iOSKeyboardFix');
    }
})();

2
@vsync道歉,我不知道你是“大厨”
Mike

选择时,该时间标题中的输入焦点正在向上移动(不可见)。我想固定选择和输入字段时的标题div。任何一个请帮我
VK Chikkadamalla

2

我有一个类似于@NealJMD的解决方案,除了我的解决方案仅针对iOS执行,并通过测量本机键盘滚动前后的scollTop以及使用setTimeout允许发生本机滚动来正确确定滚动偏移:

var $window = $(window);
var initialScroll = $window.scrollTop();
if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
  setTimeout(function () {
    $window.scrollTop($window.scrollTop() + (initialScroll - $window.scrollTop()));
  }, 0);
}

1

我已经通过以下方式固定了Ipad主布局内容的固定位置:

var mainHeight;
var main = $('.main');

// hack to detects the virtual keyboard close action and fix the layout bug of fixed elements not being re-flowed
function mainHeightChanged() {
    $('body').scrollTop(0);
}

window.setInterval(function () {
    if (mainHeight !== main.height())mainHeightChanged();
    mainHeight = main.height();
}, 100);

对于我来说,在一个复杂的JQUery UI对话框和屏幕键盘混合中,这确实非常有效,当同时关闭对话框和键盘时,尽管进行了一些编辑,但我的布局仍在屏幕的一半以下:var main = $(' body div')。first(); ...和...函数mainHeightChanged(){$(top).scrollTop(0); }
Awerealis

1
setInterval真的吗 最好只留下错误而不是这样做
vsync 2014年

1

我有一个与@ ds111类似的问题。我的网站被键盘推了上去,但是当键盘关闭时却没有下移。

首先,我尝试了@ ds111解决方案,但我有两个input领域。当然,首先键盘会消失,然后会发生模糊(或类似的情况)。所以第二个input是在键盘下面,当焦点从一个输入直接切换到另一个输入时。

此外,“跳转”对我来说还不够好,因为整个页面只有ipad的大小。所以我使滚动平滑。

最后,我必须将事件侦听器附加到所有输入,即使是当前隐藏的输入也是如此live

总之,我可以将以下javascript代码段解释为:将以下模糊事件侦听器附加到当前和所有将来input以及textarea(= live):等待宽限期(= window.setTimeout(..., 10))并平滑滚动到顶部(= animate({scrollTop: 0}, ...)),但前提是“没有键盘显示”(= if($('input:focus, textarea:focus').length == 0))。

$('input, textarea').live('blur', function(event) {
    window.setTimeout(function() {
        if($('input:focus, textarea:focus').length == 0) {
            $("html, body").animate({ scrollTop: 0 }, 400);
        }
    }, 10)
})

请注意,宽限期(= 10)可能太短,或者尽管没有input或没有textarea聚焦,但仍可能显示键盘。当然,如果您希望滚动得更快或更慢,则可以调整持续时间(= 400


1

找到这种变通办法的过程确实很努力,简而言之就是寻找输入上的焦点和模糊事件,并在事件发生时滚动以选择性地更改固定条的位置。这是防弹的,涵盖了所有情况(使用<>导航,滚动,完成按钮)。注意id =“ nav”是我的固定页脚div。您可以轻松地将此移植到标准js或jquery。对于那些使用电动工具的人来说,这是道场;-)

define([[“ dojo / ready”,“ dojo / query”,],函数(ready,查询){

ready(function(){

    /* This addresses the dreaded "fixed footer floating when focusing inputs and keybard is shown" on iphone 
     * 
     */
    if(navigator.userAgent.match(/iPhone/i)){
        var allInputs = query('input,textarea,select');
        var d = document, navEl = "nav";
        allInputs.on('focus', function(el){
             d.getElementById(navEl).style.position = "static";
        });

        var fixFooter = function(){
            if(d.activeElement.tagName == "BODY"){
                d.getElementById(navEl).style.position = "fixed";
            }
        };
        allInputs.on('blur', fixFooter);
        var b = d.body;
        b.addEventListener("touchend", fixFooter );
    }

});

}); //结束定义


这可能很棒...移植到jQuery / js并会返回报告。顺便说一句,可以预见,在利基脚本语言选择中需要移植或向项目中添加非标准工具的解决方案不会被普遍认为是有用的。
ericpeters0n 2014年

这并非在每种情况下都可行,因为这会将您滚动到元素的“静态”位置,该位置可能与您输入的原始位置相距几英里,尤其是在固定容器中的情况下。如果容器有overflow:hidden规则,那么您将完全看不到您的输入,因为它现在位于框外。
James M. Lay 2014年

1

这是“正确”的难题。您可以尝试在输入元素焦点上隐藏页脚,并在模糊下显示,但这在iOS上并不总是可靠的。每隔一段时间(例如,在我的iPhone 4S中,十分之一),焦点事件似乎无法触发(或可能存在比赛条件),并且页脚没有被隐藏。

经过反复试验,我提出了一个有趣的解决方案:

<head>
    ...various JS and CSS imports...
    <script type="text/javascript">
        document.write( '<style>#footer{visibility:hidden}@media(min-height:' + ($( window ).height() - 10) + 'px){#footer{visibility:visible}}</style>' );
    </script>
</head>

本质上:使用JavaScript确定设备的窗口高度,然后动态创建CSS媒体查询以在窗口高度缩小10个像素时隐藏页脚。因为打开键盘会调整浏览器显示的大小,所以在iOS上这绝不会失败。因为它使用的是CSS引擎而不是JavaScript,所以它也更快,更流畅!

注意:我发现使用“ visibility:hidden”比“ display:none”或“ position:static”的毛刺要少,但是您的里程可能会有所不同。


1
您可能需要在高度和宽度之间切换,以在纵向和横向模式下都解决此问题。
尼尔·梦露

1

为我工作

if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
    $(document).on('focus', 'input, textarea', function() {
        $('header').css({'position':'static'});
    });
    $(document).on('blur', 'input, textarea', function() {
        $('header').css({'position':'fixed'});
    });
}

1

在我们的案例中,这将在用户滚动时自动修复。因此,这就是我们一直在模拟blur任何input或上的滚动的修复程序textarea

$(document).on('blur', 'input, textarea', function () {
    setTimeout(function () {
        window.scrollTo(document.body.scrollLeft, document.body.scrollTop);
    }, 0);
});

1

我的回答是无法完成。

我看到25个答案,但在我的情况下却无用。这就是为什么打开键盘时Yahoo和其他页面会隐藏固定标题的原因。并且Bing将整个页面设为不可滚动(overflow-y:隐藏)。

上面讨论的情况是不同的,有些在滚动时有问题,有些在聚焦或模糊上。有些具有固定的页脚或页眉。我现在无法测试每种组合,但是您可能最终意识到无法根据您的情况进行。


这不能回答问题。
新手

4
确实如此,因为答案是没有解决方案。
Szalai Laci '02


0

万一有人想尝试这个。在固定的页脚中有一个输入字段的情况下,我得到了以下工作。

<script>
    $('document').ready(
        function() {
            if (navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/webOS/i) || navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i)
                  || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/BlackBerry/i) || navigator.userAgent.match(/Windows Phone/i)) {
                var windowHeight = $(window).height();
                var documentHeight = $(document).height();

                $('#notes').live('focus', function() {
                    if (documentHeight > windowHeight) {
                        $('#controlsContainer').css({
                            position : 'absolute'
                        });
                        $("html, body").animate({
                            scrollTop : $(document).height()
                        }, 1);
                    }
                });
                $('#notes').live('blur', function() {
                    $('#controlsContainer').css({
                        position : 'fixed'
                    });
                    $("html, body").animate({
                        scrollTop : 0
                    }, 1);
                });
            }
        });
</script>

0

我有同样的问题。但是我意识到固定的职位只是被延迟而不是被打破(至少对我而言)。等待5到10秒钟,然后查看div是否会调整回屏幕底部。我相信这不是错误,而是键盘打开时的延迟响应。


0

我尝试了该线程中的所有方法,但如果它们没有帮助,它们甚至会更糟。最后,我决定让装置失去重点:

$(<selector to your input field>).focus(function(){
    var $this = $(this);
    if (<user agent target check>) {
        function removeFocus () {
            $(<selector to some different interactive element>).focus();
            $(window).off('resize', removeFocus);
        }
        $(window).on('resize', removeFocus);
    }
});

它就像一种魅力一样工作,并修复了我的粘性登录表单。

注意:

  1. 上面的JS代码只是为了表达我的想法,要执行此代码段,请使用适合您情况的适当值替换尖括号(<>)中的值。
  2. 此代码旨在与 jQuery v1.10.2

0

对于iOS 8.3中具有更高Bootstrap Modals的任何HTML页面而言,这仍然是一个很大的错误。上面提出的所有解决方案都无法正常工作,并且在放大高模态折叠下方的任何字段后,Mobile Safari和/或WkWebView都无法将固定元素移动到HTML正文滚动条所在的位置,从而使它们与实际位置未对齐布置。

要解决该错误,请将事件侦听器添加到您的任何模式输入中,例如:

$(select.modal).blur(function(){
  $('body').scrollTop(0);
});

我猜这是可行的,因为强制HTML正文的滚动高度将实际视图与iOS 8 WebView期望固定模式div的内容重新对齐。


0

如果有人在寻找完全不同的路线(例如,您滚动滚动时甚至不想固定此“页脚” div,而您只希望div停留在页面底部),则只需将页脚位置设置为相对的。

这意味着即使虚拟键盘出现在您的移动浏览器中,您的页脚也只会停留在页面底部,而不会对虚拟键盘的显示或关闭做出反应。

显然,如果固定位置并且在向上或向下滚动时页脚跟随页面,则在Safari上看起来会更好,但是由于Chrome上的这个奇怪的错误,我们最终切换到仅使页脚相对。


0

滚动解决方案似乎都不适合我。相反,有效的方法是在用户编辑文本时将主体的位置设置为固定,然后在用户完成操作后将其恢复为静态。这样可以避免野生动物园在您上滚动内容。您可以对元素进行聚焦/模糊处理(如下所示,用于单个元素,但可以用于所有输入,文本区域),或者如果用户正在做一些事情来开始编辑,例如打开模式,则可以它在该动作上(例如模式打开/关闭)。

$("#myInput").on("focus", function () {
    $("body").css("position", "fixed");
});

$("#myInput").on("blur", function () {
    $("body").css("position", "static");
});

0

iOS9-同样的问题。

TLDR-问题的根源。要解决问题,请滚动到底部

我在position:fixediframe中有一个ID ='subscribe-popup-frame' 的表单

按照最初的问题,在输入焦点上,iframe会到达文档的顶部,而不是屏幕的顶部。

在将用户代理设置为idevice的Safari开发模式下,没有发生相同的问题。因此看来问题出在iOS虚拟键盘弹出时。

通过控制台记录iframe的位置(例如$('#subscribe-popup-frame', window.parent.document).position()),我对发生的事情有了一些了解,从那里我可以看到iOS似乎将元素的位置设置{top: -x, left: 0}为虚拟键盘弹出时(即,专注于输入元素)。

因此,我的解决方案是使该讨厌的事情发生-x,反转符号,然后使用jQuery将该top位置添加回iframe。如果有更好的解决方案,我很想听听,但是尝试了十二种不同的方法后,它是唯一对我有用的方法。

缺点:我需要将超时时间设置为500毫秒(也许可以减少工作时间,但是我想保持安全),以确保x在iOS对元素的位置作了恶作剧后,我捕获了最终值。结果,体验非常生涩。。。但至少它有效

        var mobileInputReposition = function(){
             //if statement is optional, I wanted to restrict this script to mobile devices where the problem arose
            if(screen.width < 769){
                setTimeout(function(){
                    var parentFrame = $('#subscribe-popup-frame',window.parent.document);
                    var parentFramePosFull = parentFrame.position();
                    var parentFramePosFlip = parentFramePosFull['top'] * -1;
                    parentFrame.css({'position' : 'fixed', 'top' : parentFramePosFlip + 'px'});
                },500);
            }    
        }   

然后,只需调用mobileInputReposition在像$('your-input-field).focus(function(){})$('your-input-field).blur(function(){})

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.