9

iPadでサポートするために構築しているasp.net Webサイトがあります。入力要素に焦点を合わせてキーボードがポップアップすると、位置固定ヘッダー div (通常はページとともにスクロールします) が、キーボードが占める量に相当する距離だけページをポップアップし、その間そこでフリーズします。入力プロセス。キーボードを元に戻すと、div が所定の位置に戻り、再び正常に動作します。私は iOS5 でテストしているので、position: fixed をサポートする必要があります。

これは既知の問題ですか? 誰かがこれに遭遇し、以前に対処したことがありますか? 私はこれについて何も見つけられないようです。

4

3 に答える 3

18

iOS5/iOS6/iOS7で固定位置が壊れます。

編集 3: iOS8 のこの回答の終わり近くにある作業修正へのリンクを参照してください。

Position:fixed は、次のいずれかの場合に壊れます。

a) ページがズームされている

また

b) iPad/iPhone にキーボードが表示されます (入力がフォーカスされるため)。

リンクを開いてズームするか、入力フォーカスを与えることで、jsbin.com/icibaz/3でバグを自分で表示できます。自分でhtmlを編集できます。

バグ (a) および (b) に関する注意事項:

  1. 入力がフォーカスされてキーボードが表示されると、固定された div がtop: 0px; left: 0px;間違った位置 (画面の上部または下部) に表示されます。

  2. この問題は、画面上の入力の自動センタリング (window.pageYOffset の変更) と関係があるようです。

  3. これは計算エラーであり、再描画エラーではないようです: top:onScroll イベントで強制的に変更 (0px と 1px の切り替えなど) を行うと、固定された div が 1 ピクセルずつ移動するのがわかりますが、間違ったままです。場所。

  4. 以前に使用した1つの解決策は、入力がフォーカスされたときに固定divを非表示にすることです-私が書いた他の回答を参照してください。

  5. 固定 div は、キーボードが開いたときと同じページ上の絶対位置で動かなくなったようです。

  6. 入力にフォーカスがある場合は、div を絶対位置に変更しますか? 編集 3: このソリューションを使用して、下部のコメントを参照してください。あるいは、キーボードが開かれる前に pageXOffset/pageYOffset 値を保存し、onScroll イベントで、それらの値と現在の pageXOffset/pageYOffset 値 (キーボードが開かれた後の現在値) との差を計算し、その差によって固定 div をオフセットします。

  7. ページがズームされている場合、固定位置には別の問題があるようです -ここで試してみてください (また、コメントでの固定に対する Android サポートに関する良い情報もここにあります)。

編集1:再現するには、jsbin(jsfiddleではなく)を使用し、jsbinのフルスクリーンビュー(編集ページではありません)を使用します。jsfiddle (および jsbin の編集ビュー) を避けてください。コードが iframe 内に配置され、固定位置と pageYOffset に干渉するためです。

編集 2: iOS 6 および iOS 7 Mobile Safariposition:fixed;にはまだ同じ問題があります。おそらく、それらは設計によるものです!.

編集 3: (b) の実用的な解決策は、入力がフォーカスを取得し、ヘッダーを絶対位置に変更してから、たとえばページ スクロール イベントでヘッダーの上部を設定することです。このソリューション:

  • 入力がフォーカスされていない場合は固定位置を使用します (window.onscroll を使用するとひどいジッターが発生します)。
  • ピンチズームを許可しないでください (上記のバグ (a) を回避します)。
  • 入力がフォーカスされると、絶対配置と window.pageYOffset を使用します (ヘッダーが正しく配置されます)。
  • 入力にフォーカスがあるときにスクロールする場合は、style.top を pageYOffset と等しくなるように設定します (iOS8 でも、onscroll イベントの遅延により、ヘッダーが多少揺れます)。
  • iOS8 のアプリ内で UIWebView を使用する場合、または iOS7 以下を使用する場合、入力にフォーカスがあるときにスクロールすると、スクロールが完了するまで onscroll が起動されないため、ヘッダーが非常に不安定になります。
  • 入力がフォーカスを失うと、固定位置のヘッダーに戻ります (例では input.onblur を使用していますが、おそらく document.body.onfocus を使用するように調整されています)。
  • ヘッダーが大きすぎると、入力が遮られたり覆われたりする可能性があることに注意してください。
  • キーボードが表示されているときの iOS ページ/ビューポートの高さのバグにより、フッターの作業を行うことができませんでした。
  • http://jsbin.com/xujofoze/4/editを使用して例を編集し、 http: //output.jsbin.com/xujofoze/4/quietを使用して表示します
于 2012-05-25T02:12:24.573 に答える
0

私のニーズでは、絶対位置ヘッダーを使用し、スクロールする前に非表示にし、スクロールが終了したときに表示する方が簡単であることがわかりました (iOS4 と Android をサポートするには同じコードが必要です)。

私の目的のために、イベントでヘッダーを非表示にし、またはイベントtouchstartで再び表示します (さらに、応答性を向上させたり、ちらつきを減らしたりするためのいくつかのタイマー)。点滅しますが、私が見つけることができる最良の妥協点です. イベントを使用してスクロールの開始を検出できます(jQuery がこれを行います) が、次の理由でうまく機能しないことがわかりました。touchendscrolltouchmovetouchmove

  1. 定期的に、iPad はスクロール前の再描画に失敗します (つまり、スクロールが開始される前にヘッダーが変更されたにもかかわらず、絶対ヘッダーがスタックしたままになりますtop)。

  2. 入力要素がフォーカスされると、iPad はその要素を自動的に中央に配置しますが、scrollstart イベントは発生しません (click入力だけでは touchmove が発生しないため)。

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 モバイルでは、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 );
        });
    }
};
于 2012-05-28T23:57:04.347 に答える