【问题标题】:fixed position div freezes on page (iPad)固定位置 div 在页面上冻结 (iPad)
【发布时间】:2012-05-26 10:57:32
【问题描述】:

我有一个要在 ipad 上支持的 asp.net 网站。当我专注于输入元素并且键盘弹出时,位置固定的标题 div(通常与页面一起滚动)将弹出页面的距离等于键盘占用的距离并在持续时间内冻结在那里输入过程。将键盘放回原位后, div 会弹回原位并再次正常运行。我在 iOS5 上测试所以 position: fixed 应该支持。

这是一个已知问题吗?有人遇到过这个并处理过吗?我似乎在这方面找不到任何东西。

【问题讨论】:

    标签: ipad ios5 css-position


    【解决方案1】:

    固定定位在 iOS5/iOS6/iOS7 上被破坏。

    编辑 3:请参阅 iOS8 的此答案接近尾声的工作修复链接。

    Position:fixed 在以下任一情况下被破坏:

    a) 页面被缩放

    b) iPad/iPhone 上显示键盘(由于输入获得焦点)。

    您可以在jsbin.com/icibaz/3 中自己查看错误,方法是打开链接并缩放,或给予输入焦点。您可以自己编辑edit the html

    关于错误 (a) 和 (b) 的说明:

    1. 当输入获得焦点并显示键盘时,带有top: 0px; left: 0px; 的固定 div 将显示在错误的位置(屏幕顶部上方或下方)。

    2. 问题似乎与屏幕上输入的自动居中有关(更改 window.pageYOffset)。

    3. 这似乎是计算错误,而不是重绘错误:如果你在 onScroll 事件中强制 top: 改变(例如在 0px 和 1px 之间切换),你可以看到固定的 div 移动一个像素,但它仍然在错误的位置。

    4. 我之前使用的一个解决方案是 hide the fixed div when an input gets focus - 请参阅我写的另一个答案。

    5. 固定的 div 似乎卡在了与打开键盘时相同的页面绝对位置。

    6. 那么当输入有焦点时,也许将 div 更改为绝对定位?编辑 3:使用此解决方案查看底部的评论。或者也许在打开键盘之前保存 pageXOffset/pageYOffset 值,并在 onScroll 事件中计算这些值与当前 pageXOffset/pageYOffset 值(打开键盘后的当前值)之间的差异,并将固定 div 偏移该差异。

    7. 如果页面被缩放,固定定位似乎会出现不同的问题 - 试试here(还有关于 Android 对 cme​​ts 中的固定支持的良好信息here)。

    编辑 1:重现使用 jsbin(不是 jsfiddle)并使用 jsbin 的全屏视图(不是编辑页面)。避免使用 jsfiddle(和 jsbin 的编辑视图),因为它们将代码放在 iframe 中,这会干扰固定定位和 pageYOffset。

    编辑 2:iOS 6 和 iOS 7 Mobile Safari position:fixed; 仍然存在相同的问题 - 大概是设计使然!

    编辑 3:(b) 的工作解决方案是当输入获得焦点时,将标题更改为绝对定位,然后在页面滚动事件 for example 上设置标题顶部。这个解决方案:

    • 在输入未聚焦时使用固定定位(使用 window.onscroll 时会有严重的抖动)。
    • 不允许捏合缩放(避免上述错误 (a))。
    • 在输入获得焦点后使用绝对定位和 window.pageYOffset(因此标题被正确定位)。
    • 如果在输入具有焦点时滚动,请将 style.top 设置为等于 pageYOffset(即使在 iOS8 上,由于 onscroll 事件延迟,标题也会有些抖动)。
    • 如果在 iOS8 上的 App 中使用 UIWebView,或者使用
    • 一旦输入失去焦点,返回固定位置标题(示例使用 input.onblur,但可能更适合使用 document.body.onfocus)。
    • 注意可用性失败,如果标头太大,输入可能会被遮挡/覆盖。
    • 由于键盘显示时 iOS 页面/视口高度中的错误,我无法为页脚工作。
    • 使用http://jsbin.com/xujofoze/4/edit 编辑示例并使用http://output.jsbin.com/xujofoze/4/quiet 查看

    【讨论】:

    • 原来这是一个 iOS 错误,我将暂时离开这个问题,但您的建议是有效的,所以我会将此答案标记为正确
    • jas,能否提供iOS bug issue url的链接?
    • 我还没有查看 iOS6 中修复了什么,但至少添加了一个额外的错误:igstudio.blogspot.com/2012/09/positionfixed-in-ios-6.html(我还怀疑给固定的 div 一个 CSS 属性,强制它具有硬件加速合成也可能修复它)。
    • IOS 8 错误仍然存​​在,但现在的行为有所不同。将位置更改为绝对位置时,DIV 不会保持相同的位置。
    • @nicolas 上面引用的 iOS8 示例似乎工作正常。在这里试试:output.jsbin.com/xujofoze/4/quiet
    【解决方案2】:

    根据我的需要,我发现使用绝对定位的标题更容易,在滚动之前隐藏它并在完成滚动时显示它(我需要相同的代码来支持 iOS4 和 Android)。

    出于我的目的,我在 touchstart 事件上隐藏了标题,并在 touchendscroll 事件上再次显示它(加上一些计时器以提高响应能力/减少闪烁)。它闪烁,但这是我能找到的最佳折衷方案。可以使用touchmove 事件检测滚动的开始(jQuery 会这样做),但我发现touchmove 对我来说效果不佳,因为:

    1. iPad 通常无法在滚动之前重新绘制(即绝对标题仍然卡住 - 即使在滚动开始之前更改了 top)。

    2. 当输入元素获得焦点时,iPad 会自动将元素居中,但不会触发 scrollstart 事件(因为如果只是 clicking 输入,则不会触发 touchmove)。

      李>

    可以通过使用固定和绝对定位的混合方法来改进在 iOS5 上实现固定标头:

    • 对 iOS5 使用固定定位,直到输入获得焦点。

    • 当输入获得焦点(键盘显示)时,更改为 iOS4 绝对定位代码。

    • 当键盘关闭时,改回固定位置。

    检测键盘何时关闭的代码(例如,使用键盘隐藏键)是在document 元素上注册DOMFocusOut 事件并执行类似于以下代码的操作。需要超时是因为DOMFocusOut 事件可以在一个元素获得焦点和另一个元素失去焦点之间触发。

    function document_DOMFocusOut() {
        clearTimeout(touchBlurTimer);
        touchBlurTimer = setTimeout(function() {
            if (document.activeElement == document.body) {
                handleKeyboardHide();
            }
        }.bind(this), 400);
    }
    

    我的固定标题代码是这样的:

    {
        setup: function() {
            observe(window, 'scroll', this, 'onWinScroll');
            observe(document, 'touchstart', this, 'onTouchStart');
            observe(document, 'touchend', this, 'onTouchEnd');
            if (isMobile) {
                observe(document, 'DOMFocusOut', this, 'docBlurTouch');
            } else if (isIE) {
            // see http://ajaxian.com/archives/fixing-loss-of-focus-on-ie for code to go into this.docBlurIe()
                observe(document, 'focusout', this, 'docBlurIe');
            } else {
                observe(isFirefox ? document : window, 'blur', this, 'docBlur');
            }
        },
    
        onWinScroll: function() {
            clearTimeout(this.scrollTimer);
            this.scrolling = false;
            this.rehomeAll();
        },
    
        rehomeAll: function() {
            if ((isIOS5 && this.scrolling) || isIOS4 || isAndroid) {
                this.useAbsolutePositioning();
            } else {
                this.useFixedPositioning();
            }
        },
    
        // Important side effect that this event registered on document on iOs. Without it event.touches.length is incorrect for any elements in the document using the touchstart event!!!
        onTouchStart: function(event) {
            clearTimeout(this.scrollTimer);
            if (!this.scrolling && event.touches.length == 1) {
                this.scrolling = true;
                this.touchStartTime = inputOrOtherKeyboardShowingElement(event.target) ? 0 : (new Date).getTime();
                // Needs to be in touchStart so happens before iPad automatic scrolling to input, also not reliable using touchMove (although jQuery touch uses touchMove to unreliably detect scrolling).
                this.rehomeAll();
            }
        },
    
        onTouchEnd: function(event) {
            clearTimeout(this.scrollTimer);
            if (this.scrolling && !event.touches.length) {
                var touchedDuration = (new Date).getTime() - this.touchStartTime;
                // Need delay so iPad can scroll to the input before we reshow the header.
                var showQuick = this.touchStartTime && touchedDuration < 400;
                this.scrollTimer = setTimeout(function() {
                    if (this.scrolling) {
                        this.scrolling = false;
                        this.rehomeAll();
                    }
                }.bind(this), showQuick ? 0 : 400);
            }
        },
    
        // ... more code
    }
    

    jQuery mobile 支持 scrollstart 和 scrollstop 事件:

    var supportTouch = $.support.touch,
        scrollEvent = "touchmove scroll",
        touchStartEvent = supportTouch ? "touchstart" : "mousedown",
        touchStopEvent = supportTouch ? "touchend" : "mouseup",
        touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
    
    function triggerCustomEvent( obj, eventType, event ) {
        var originalType = event.type;
        event.type = eventType;
        $.event.handle.call( obj, event );
        event.type = originalType;
    }
    
    // also handles scrollstop
    $.event.special.scrollstart = {
    
        enabled: true,
    
        setup: function() {
    
            var thisObject = this,
                $this = $( thisObject ),
                scrolling,
                timer;
    
            function trigger( event, state ) {
                scrolling = state;
                triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
            }
    
            // iPhone triggers scroll after a small delay; use touchmove instead
            $this.bind( scrollEvent, function( event ) {
    
                if ( !$.event.special.scrollstart.enabled ) {
                    return;
                }
    
                if ( !scrolling ) {
                    trigger( event, true );
                }
    
                clearTimeout( timer );
                timer = setTimeout(function() {
                    trigger( event, false );
                }, 50 );
            });
        }
    };
    

    【讨论】:

      【解决方案3】:

      这在iOS13中仍然是一个问题(当在'textarea'字段中删除长文本时,固定标题会跳转到'textarea'字段的开头,阻碍视图),因此,我想我分享我的快速修复:

      由于我的页脚相当大,我没有任何 JS,只是在页脚中添加了一个比固定页眉更大的 z-index。眼不见心不烦。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多