固定位置div冻结页面(iPad)

时间:2012-05-18 21:18:33

标签: ipad ios5 css-position

我正在构建一个asp.net网站,以便在ipad上得到支持。当我专注于输入元素并弹出键盘时,位置固定标题div(通常与页面一起滚动)将弹出页面相当于键盘占用量的距离并在此处冻结输入过程。键盘向下拉后,div会重新锁定到位并再次正常运行。我在iOS5上测试所以位置:应该支持fixed。

这是一个已知问题吗?有人遇到这个并在之前处理过吗?我似乎无法找到任何相关内容。

3 个答案:

答案 0 :(得分:15)

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

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

位置:固定在以下情况之一被破坏:

a)页面被缩放

b)键盘显示在iPad / iPhone上(由于输入得到关注)。

您可以通过打开链接并缩放或提供输入焦点,在jsbin.com/icibaz/3中自行查看错误。您可以自己编辑edit the html

关于错误(a)和(b)的注释:

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

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

  3. 这似乎是一个计算错误,而不是重绘错误:如果强制{on {1}}更改(例如在0px和1px之间切换)onScroll事件,你可以看到固定的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支持的良好信息here,请在评论中修复)。

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

    编辑2:iOS 6和iOS 7移动Safari top:仍有相同的问题 - 可能是设计出来的!

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

    • 当输入未聚焦时使用固定定位(使用window.onscroll有可怕的抖动)。
    • 不允许双指缩放(避免上面的错误(a))。
    • 使用绝对定位和window.pageYOffset一旦输入获得焦点(因此标题正确定位)。
    • 如果在输入具有焦点时滚动,则将style.top设置为等于pageYOffset(即使在iOS8上,由于onscroll事件延迟,标题也会稍微抖动)。
    • 如果在iOS8上的应用程序中使用UIWebView,或者使用< = iOS7,如果在输入有焦点时滚动,则标题会超级抖动,因为在滚动完成之前不会触发onscroll。
    • 输入失去焦点后返回固定位置标题(示例使用input.onblur,但可能使用tider document.body.onfocus)。
    • 注意可用性失败,如果标题太大,输入可以被遮挡/覆盖。
    • 当键盘显示时,由于iOS页面/视口高度存在错误,我无法为页脚工作。
    • 使用http://jsbin.com/xujofoze/4/edit修改示例并使用http://output.jsbin.com/xujofoze/4/quiet
    • 进行查看

答案 1 :(得分:0)

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

出于我的目的,我将标题隐藏在touchstart事件上,并在touchendscroll事件上再次显示(加上一些计时器以提高响应速度/减少闪烁)。它闪烁,但是我能找到的最好的妥协。可以使用touchmove事件检测滚动的开始(jQuery这样做),但我发现touchmove对我来说不起作用,因为:

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

  2. 当输入元素获得焦点时,iPad会自动居中元素,但不会触发scrollstart事件(因为只有click输入才会触摸移动。

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

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

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

    • 键盘关闭后,返回固定位置。

    检测键盘何时关闭的代码(例如,使用键盘隐藏键)是在DOMFocusOut元素上注册document事件,并执行类似下面的代码。需要超时是因为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 );
            });
        }
    };
    

答案 2 :(得分:0)

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

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