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

固定位置div在页面上冻结(iPad),ipad,ios5,css-position,Ipad,Ios5,Css Position,我有一个asp.net网站,我正在建立一个支持ipad的网站。当我关注一个输入元素并弹出键盘时,位置固定的标题div(通常随页面滚动)将弹出页面,其距离相当于键盘占用的量,并在输入过程中冻结在那里。一旦将键盘放回原位,div就会弹回原位并再次正常工作。我正在iOS5上测试,所以应该支持位置:fixed 这是一个已知的问题吗?以前有没有人遇到过这个问题并处理过?我似乎找不到这方面的任何信息。在iOS5/iOS6/iOS7上,固定定位被破坏 编辑3:有关iOS8,请参阅此答案末尾附近的工作修复程序链

我有一个asp.net网站,我正在建立一个支持ipad的网站。当我关注一个输入元素并弹出键盘时,位置固定的标题div(通常随页面滚动)将弹出页面,其距离相当于键盘占用的量,并在输入过程中冻结在那里。一旦将键盘放回原位,div就会弹回原位并再次正常工作。我正在iOS5上测试,所以应该支持位置:fixed


这是一个已知的问题吗?以前有没有人遇到过这个问题并处理过?我似乎找不到这方面的任何信息。

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

编辑3:有关iOS8,请参阅此答案末尾附近的工作修复程序链接

位置:当以下任一情况发生时,固定断开:

a) 页面被缩放

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

您可以通过打开链接并缩放,或提供输入焦点,自己查看bug。您可以自己编辑该文件

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

  • 带有
    top的固定div:0px;左:0px将显示在错误的位置(屏幕顶部上方或下方)

  • 问题似乎与屏幕上输入的自动居中有关(Changed window.pageYOffset)

  • 这似乎是一个计算错误,而不是重画错误:如果您强制onScroll事件上的
    top:
    更改(例如,在0px和1px之间切换),您可以看到固定div移动了一个像素,但它仍然位于错误的位置

  • 我以前使用的一个解决方案是——看看我写的另一个答案

  • fixed div似乎卡在了页面上与键盘打开时相同的绝对位置

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

  • 如果页面被缩放,固定位置似乎会出现另一个问题——试试看(这也是Android支持固定评论的好信息)

  • 编辑1:使用jsbin(而不是JSFIDLE)和jsbin的全屏视图(而不是编辑页面)进行复制。避免使用JSFIDLE(和jsbin的编辑视图),因为它们将代码放在iframe中,这会干扰固定定位和页面偏移

    编辑2:iOS 6和iOS 7移动Safari
    位置:固定
    仍然存在相同的问题-可能是出于设计

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

    • 当输入未聚焦时使用固定定位(使用window.onscroll会产生可怕的抖动)
    • 不允许收缩缩放(避免上面的错误(a))
    • 一旦输入获得焦点,使用绝对定位和window.pageYOffset(以便正确定位标题)
    • 如果在输入具有焦点时滚动,则将style.top设置为等于pageYOffset(即使在iOS8上,由于onscroll事件延迟,标题也会有点抖动)
    • 如果在iOS8上的应用程序中使用UIWebView,或者根据我的需要使用,我发现使用绝对定位标题、滚动前隐藏标题和滚动完成时显示标题更容易(我需要相同的代码来支持iOS4和Android)

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

    • 通常,iPad在滚动前无法重新绘制(即,绝对页眉仍然被卡住-即使在滚动开始前更改了
      top

    • 当一个输入元素获得焦点时,iPad会自动将该元素居中,但scrollstart事件不会被触发(因为如果只是
      单击
      输入,则不会触发touchmove)

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

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

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

      • 关闭键盘后,切换回固定位置

      当键盘关闭时要检测的代码(例如使用键盘隐藏键)是在文档元素上注册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
      }
      

      这在iOS13中仍然是一个问题(当“textarea”字段中的长文本被删除时,fixed header会跳到该“textarea”字段的开头,阻碍查看),因此,我想分享一下我的快速修复方法:


      因为我的页脚相当大,所以我没有使用任何JS,只是在页脚上添加了一个比固定页眉更大的z索引。看不见,想不起来。

      这是一个iOS错误,我暂时不讨论这个问题,但是你的建议是有效的,所以我会将这个答案标记为“更正”。JAS,是否可以提供iOS错误问题url的链接
      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 );
              });
          }
      };