web-dev-qa-db-ja.com

iOS 5の固定位置と仮想キーボード

Position:fixedを介して画面の下部に固定されたdivを持つモバイルWebサイトがあります。フォームのあるページを表示するまで、iOS 5(iPod Touchでテスト中)ですべて正常に動作します。入力フィールドをタップして仮想キーボードが表示されると、突然divの固定位置が失われます。キーボードが表示されている限り、divはページとともにスクロールします。 [完了]をクリックしてキーボードを閉じると、divは画面の下部の位置に戻り、position:fixedルールに従います。

他の誰かがこのような行動を経験しましたか?これは予想されますか?ありがとう。

135
jeffc

アプリケーションでこの問題が発生しました。これを回避する方法は次のとおりです。

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

一番上までスクロールしてそこに配置するだけなので、iOSユーザーは奇妙なことに気づかない。これを一部のユーザーエージェント検出でラップして、他のユーザーがこの動作を取得しないようにします。

48
Nick Retallack

仮想キーボードがビューポートを画面外に押し上げるというipadの問題がわずかにありました。その後、ユーザーが仮想キーボードを閉じた後、私のビューポートはまだオフスクリーンでした。私の場合、次のようなことをしました。

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

これは、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);
    }
});
14
Hatch

位置が固定された要素は、キーボードが起動しているときに単に位置を更新しません。しかし、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;
    });
12
Riley Dutton

この問題の調査中に私がしたように、誰かがこのスレッドに遭遇した場合に備えて。このスレッドは、この問題に関する私の考えを刺激するのに役立ちました。

これは最近のプロジェクトでこれに対する私の解決策でした。 「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操作を停止するため、修正が保留になるのに少し遅れがありますが、トリックは...

6
Jory Cunningham

この問題は本当に迷惑です。

上記の手法のいくつかを組み合わせて、これを思い付きました。

$(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);
});

2つの固定navbar(ヘッダーとフッター、Twitterブートストラップを使用)があります。両方とも、キーボードがアップ状態のときは奇妙な動作をし、キーボードがダウンした後は再び奇妙な動作をしました。

この時間指定/遅延修正により、動作します。私はまだ時々不具合を見つけますが、クライアントにそれを示すのに十分なようです。

これがあなたのために働くかどうか私に知らせてください。そうでない場合、他の何かを見つけることができます。ありがとう。

4
escapedcat

このバグについて私が見つけた他の答えはどれも私にとってはうまくいきませんでした。ページを34pxだけ上にスクロールすることで修正できました。モバイルサファリの量が下にスクロールします。 jqueryを使用:

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

これは明らかにすべてのブラウザで有効になりますが、iOSでの破損を防ぎます。

4
NealJMD

私はJory Cunningham答えを取り、それを改善しました:

多くの場合、頭がおかしいのは1つの要素だけではなく、いくつかの固定配置要素です。この場合、targetElemは「修正」したいすべての固定要素を持つjQueryオブジェクトである必要があります。ほら、これはスクロールするとiOSキーボードが消えてしまうようです...

言うまでもなく、これは_ _の後 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');
    }
})();
3
vsync

IOS7でも同じ問題が発生していました。下部の固定要素は、適切に焦点を合わせていない私のビューを台無しにします。

このメタタグを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

それが誰かを助けることを願っています。

3
pasevin

私は@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);
}
2
ChrisWren

はい、Appleは、IOS5でこれをあまりよく考えていなかったようです。仮想キーボードが表示されるとすぐに、固定位置の要素はページに対して相対的になります。要素が絶対位置に戻っても、レイアウトを壊さないため、おそらく大丈夫でしょう。残念ながら、これらの要素の実際の配置は予測がはるかに困難です。

[編集済み]の固定ヘッダーにこの問題があります。ページを下にスクロールし、検索ボックスをクリックして、レイアウトが壊れています。フォーカスイベントの絶対位置に戻すことで修正しようとしましたが、動作しますが、フォーカスは失われます(キーボードは開いたままですが、カーソルは検索ボックスにありません)。

とにかく作業中なので、並べ替えたらお知らせします

乾杯

2
John williams

私のために働く

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
ShibinRagh

私の答えは、それができないということです。

25個の回答が表示されますが、私の場合はうまくいきません。キーボードがオンのとき、Yahooや他のページが固定ヘッダーを隠すのはそのためです。また、Bingはページ全体をスクロール不可にします(オーバーフローy:非表示)。

上記のケースは異なり、スクロール時の問題、フォーカスまたはブラーに関する問題があります。フッターまたはヘッダーが固定されているものもあります。私は今、各組み合わせをテストすることはできませんが、あなたの場合はそれを行うことができないことに気付くかもしれません。

1
Szalai Laci

@ ds111と同様の問題がありました。私のウェブサイトはキーボードによって押し上げられましたが、キーボードを閉じても下に移動しませんでした。

最初に@ ds111ソリューションを試しましたが、2つのinputフィールドがありました。もちろん、最初にキーボードが消え、次にぼかしが発生します(またはそのようなもの)。そのため、2番目のinputは、フォーカスが1つの入力から別の入力に直接切り替わったときにキーボードの下にありました。

さらに、ページ全体のサイズはiPadのサイズしかないため、「ジャンプアップ」だけでは十分ではありませんでした。そこで、スクロールをスムーズにしました。

最後に、現在非表示になっているすべての入力、つまりliveにイベントリスナーをアタッチする必要がありました。

まとめて、次のJavaScriptスニペットを次のように説明できます。現在および将来のinputおよびtextarea(= live)に次のblurイベントリスナーをアタッチします。猶予期間(= 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
Raul Pinto

これは「正しく」行うのが難しい問題です。入力要素のフォーカスでフッターを非表示にしたり、ぼかしで表示したりできますが、iOSで常に信頼できるとは限りません。頻繁に(たとえば、iPhone 4Sで10回に1回)フォーカスイベントが発生しない(または競合状態にある)ようで、フッターが非表示になりません。

多くの試行錯誤の後、この興味深い解決策を思いつきました。

<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を使用してデバイスのウィンドウの高さを決定し、ウィンドウの高さが10ピクセル縮小したときにフッターを非表示にするCSSメディアクエリを動的に作成します。キーボードを開くとブラウザのディスプレイのサイズが変更されるため、iOSでこれが失敗することはありません。 JavaScriptではなくCSSエンジンを使用しているため、はるかに高速でスムーズです。

注:「display:none」または「position:static」よりも「visibility:hidden」の方がグリッチが少ないことがわかりましたが、走行距離は異なる場合があります。

1
Richard Kennard

このようにして、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);
1
Brett

私たちの場合、これはユーザーがスクロールするとすぐに修正されます。これが、blurまたはinputtextareaのスクロールをシミュレートするために使用してきた修正です。

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

入力のフォーカスイベントとブラーイベントを簡単に検索し、イベントが発生したときに固定バーの位置を選択的に変更するためにスクロールするこの回避策を見つけるために、本当に一生懸命働きました。これは防弾であり、すべてのケースをカバーします(<>、スクロール、完了ボタンでナビゲート)。注id = "nav"は固定フッターdivです。これを標準のjsまたはjqueryに簡単に移植できます。これは電動工具を使用する人のための道場です;-)

define(["dojo/ready"、 "dojo/query"、]、function(ready、query){

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 );
    }

});

}); //定義を終了

1
httpete

iOS9-同じ問題。

TLDR-問題の原因。解決策については、下にスクロールしてください

Id = 'subscribe-popup-frame'のposition:fixed iframeにフォームがありました

元の質問によると、入力フォーカスでは、iframeは画面の上部ではなくドキュメントの上部に移動します。

ユーザーエージェントがideviceに設定されているサファリdevモードでは、同じ問題は発生しませんでした。そのため、問題はポップアップしたときにiOS仮想キーボードによって引き起こされているようです。

Iframeの位置を記録するコンソール(たとえば$('#subscribe-popup-frame', window.parent.document).position())で何が起こっているのかを可視化し、そこから仮想キーボードがポップアップしたときにiOSが要素の位置を{top: -x, left: 0}に設定しているように見える(つまり、入力要素に焦点を当てています)。

したがって、私の解決策は、その厄介な-xを取得し、符号を逆にしてから、jQueryを使用してそのtop位置をiframeに追加することでした。より良い解決策があれば、それを聞きたいと思いますが、数十の異なるアプローチを試した後、それが私のために働いた唯一のものでした。

欠点:タイムアウトを500ミリ秒に設定する必要がありました(おそらく機能しなかったかもしれませんが、安全にしたかったため)。iOSがx値を取得した後、要素の位置。その結果、経験は非常にぎくしゃくしています。 。 。しかし、少なくともそれは動作します

ソリューション

        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);
            }    
        }   

次に、$('your-input-field).focus(function(){})$('your-input-field).blur(function(){})などのmobileInputRepositionを呼び出します。

0
martellalex

これは、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
Terry Bu

誰かがこれを試してみたいと思った場合に備えて。入力フィールドを含む固定フッターで次の機能を使用できます。

<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
Bernard

このソリューションはGithubで見つけました。

https://github.com/Simbul/baker/issues/504#issuecomment-12821392

スクロール可能なコンテンツがあることを確認してください。

// put in your .js file
$(window).load(function(){
    window.scrollTo(0, 1);
});

// min-height set for scrollable content
<div id="wrap" style="min-height: 480px">
  // website goes here
</div>

追加のボーナスとして、アドレスバーが折りたたまれます。

0
Leigh Mackay

スクロールの解決策はどれも私にとってはうまくいかないようでした。代わりに、ユーザーがテキストを編集している間に本文の位置を固定に設定し、ユーザーが完了したらテキストを静的に復元するのが効果的です。これにより、サファリがコンテンツをスクロールするのを防ぎます。これは、要素のフォーカス/ぼかし(単一の要素については下に表示されますが、すべての入力、テキストエリアに対して可能です)、またはユーザーがモーダルを開くなどの編集を開始する場合に行うことができますそのアクション(モーダルオープン/クローズなど)でそれ。

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

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

同じ問題があります。しかし、私は(少なくとも私にとっては)固定位置が遅れているだけで壊れていないことに気付きました。 5〜10秒待って、divが画面の下部に戻るかどうかを確認します。エラーではなく、キーボードが開いているときの応答が遅れていると思います。

0
Philip

私はこのスレッドからすべてのアプローチを試みましたが、もし助けなければ、さらに悪化しました。最終的に、強制的にデバイスのフォーカスを緩めることにしました。

$(<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コードは私の考えを示すためのものです。このスニペットを実行するには、angular中括弧(<>)の値を状況に応じた適切な値に置き換えてください。
  2. このコードはjQuery v1.10.2で動作するように設計されています
0
Anton Boritskiy