ios8のSafariは、固定要素がフォーカスを取得すると画面をスクロールします


96

IOS8 Safariでは、位置が修正された新しいバグがあります。

固定パネル内のテキストエリアにフォーカスすると、Safariがページの一番下までスクロールします。

これは、ページを一番下までスクロールして場所を失うことなくテキストエリアにテキストを入力する方法がないため、あらゆる種類のUIを操作することを不可能にします。

このバグをきれいに回避する方法はありますか?

#a {
  height: 10000px;
  background: linear-gradient(red, blue);
}
#b {
  position: fixed;
  bottom: 20px;
  left: 10%;
  width: 100%;
  height: 300px;
}

textarea {
   width: 80%;
   height: 300px;
}
<html>
   <body>
   <div id="a"></div>
   <div id="b"><textarea></textarea></div>
   </body>
</html>

1
#bでz-indexを設定すると役立ちますか?
Ben

1
zインデックスは役に立たない、おそらくいくつかの派手なop css変換がスタックコンテキストで多くなるとは思わないでしょう。
Sam Saffron

1
コンテキストのためにここに談話上の議論は次のとおりです。meta.discourse.org/t/dealing-with-ios-8-ipad-mobile-safari-bugs/...
サムサフラン

79
iOSサファリは新しいIEです
geedubb 2015年

4
@geedubbは同意した。デフォルトのブラウザバージョンをOSに結び付けるmoronic OSは、過去7年間IEを悩ませてきた問題に陥るでしょう。
dewd 2015年

回答:


58

この問題のこの良い分析に基づいて、私はこれをCSSの要素htmlbody要素で使用しました:

html,body{
    -webkit-overflow-scrolling : touch !important;
    overflow: auto !important;
    height: 100% !important;
}

私はそれが私にとって素晴らしい働きをしていると思います。


2
私も働いた。これは、ロード時にDOMを操作しているため、他の多くのことを台無しにしたので、これをクラスにして、DOMが安定した後、それをhtml、bodyに追加しました。scrollTopのようなものはうまく機能しません(私は自動スクロールを行っています)が、ここでも、スクロール操作中にクラスを追加/削除できます。ただし、Safariチームの一部の作業は不十分です。
Amarsh

1
このオプションを見ている人はtransform: translateZ(0);stackoverflow.com
questions / 7808110 /…

1
これで問題は解決しますが、アニメーションがあると、アニメーションが途切れ途切れに見えます。メディアクエリでラップする方がよい場合があります。
mmla

iOS 10.3で私のために働いた。
引用Bro

それは問題を解決しません。仮想キーボードが表示されたときにスクロールをインターセプトし、高さを特定の値に変更する必要があります。stackoverflow.com
Brian Haak

36

私が思いつくことができる最良の解決策はposition: absolute;、フォーカスでの使用に切り替えて、それが使用されていたときの位置を計算することposition: fixed;です。トリックは、focusイベントの起動が遅すぎるため、touchstart使用する必要があるということです。

この回答のソリューションは、iOS 7での正しい動作を非常によく模倣しています。

要件:

body要素は、要素が絶対位置に切り替わるときに適切な位置決めを確実にするために配置しておく必要があります。

body {
    position: relative;
}

コード実際の例):

次のコードは、提供されているテストケースの基本的な例であり、特定のユースケースに適合させることができます。

//Get the fixed element, and the input element it contains.
var fixed_el = document.getElementById('b');
var input_el = document.querySelector('textarea');
//Listen for touchstart, focus will fire too late.
input_el.addEventListener('touchstart', function() {
    //If using a non-px value, you will have to get clever, or just use 0 and live with the temporary jump.
    var bottom = parseFloat(window.getComputedStyle(fixed_el).bottom);
    //Switch to position absolute.
    fixed_el.style.position = 'absolute';
    fixed_el.style.bottom = (document.height - (window.scrollY + window.innerHeight) + bottom) + 'px';
    //Switch back when focus is lost.
    function blured() {
        fixed_el.style.position = '';
        fixed_el.style.bottom = '';
        input_el.removeEventListener('blur', blured);
    }
    input_el.addEventListener('blur', blured);
});

これは、比較のためのハックなしの同じコードです。

警告:

position: fixed;要素に以外の位置にある他の親要素がある場合、bodyに切り替えるとposition: absolute;予期しない動作が発生する可能性があります。このposition: fixed;ような要素をネストすることは一般的ではないので、この性質上、おそらく大きな問題にはなりません。

推奨事項:

使用しながらtouchstartイベントは、ほとんどのデスクトップ環境を除外します、あなたはおそらく、このコードは唯一のAndroidや古いiOSのバージョンなどのiOS 8壊れた、とされていない他のデバイスに対して実行されるようにスニッフィングユーザーエージェントを使用したいと思うでしょう。残念ながら、AppleがいつiOSでこの問題を修正するかはまだわかりませんが、次のメジャーバージョンで修正されない場合は驚きます。


透明ラッピングdivの上〜100%のdivと高さ設定とのダブルラッピングが...これを避けるにそれをだますことができれば、私は疑問に思う
サムサフラン

@SamSaffronそのようなテクニックがどのように機能するかを明確にしていただけませんか?私はこのようないくつかのことを試みたが成功しなかった。ドキュメントの高さが不明確であるため、どのように機能するかわかりません。
Alexander O'Mara

私は多分、単に「固定」の100%の高さのラッパーがこの問題を回避する必要はありませ考えていた
サムサフラン

@downvoter:何か問題が発生しましたか?これはひどい解決策であることに私は同意しますが、これ以上の解決策はないと思います。
Alexander O'Mara 2015年

4
これは私にとってはうまくいきませんでした、入力フィールドはまだ動きます。
Rodrigo Ruiz

8

絶対位置に変更する必要なしに機能する方法を見つけました

完全なコメント化されていないコード

var scrollPos = $(document).scrollTop();
$(window).scroll(function(){
    scrollPos = $(document).scrollTop();
});
var savedScrollPos = scrollPos;

function is_iOS() {
  var iDevices = [
    'iPad Simulator',
    'iPhone Simulator',
    'iPod Simulator',
    'iPad',
    'iPhone',
    'iPod'
  ];
  while (iDevices.length) {
    if (navigator.platform === iDevices.pop()){ return true; }
  }
  return false;
}

$('input[type=text]').on('touchstart', function(){
    if (is_iOS()){
        savedScrollPos = scrollPos;
        $('body').css({
            position: 'relative',
            top: -scrollPos
        });
        $('html').css('overflow','hidden');
    }
})
.blur(function(){
    if (is_iOS()){
        $('body, html').removeAttr('style');
        $(document).scrollTop(savedScrollPos);
    }
});

それを壊す

最初に、HTMLの固定入力フィールドをページの上部に向ける必要があります(これは固定要素であるため、意味的には、上部近くに配置する必要があります)。

<!DOCTYPE HTML>

<html>

    <head>
      <title>Untitled</title>
    </head>

    <body>
        <form class="fixed-element">
            <input class="thing-causing-the-issue" type="text" />
        </form>

        <div class="everything-else">(content)</div>

    </body>

</html>

次に、現在のスクロール位置をグローバル変数に保存する必要があります。

//Always know the current scroll position
var scrollPos = $(document).scrollTop();
$(window).scroll(function(){
    scrollPos = $(document).scrollTop();
});

//need to be able to save current scroll pos while keeping actual scroll pos up to date
var savedScrollPos = scrollPos;

次に、修正が不要なものに影響を与えないようにiOSデバイスを検出する方法が必要です(https://stackoverflow.com/a/9039885/1611058から取得した関数)

//function for testing if it is an iOS device
function is_iOS() {
  var iDevices = [
    'iPad Simulator',
    'iPhone Simulator',
    'iPod Simulator',
    'iPad',
    'iPhone',
    'iPod'
  ];

  while (iDevices.length) {
    if (navigator.platform === iDevices.pop()){ return true; }
  }

  return false;
}

これで必要なものがすべて揃ったので、修正します:)

//when user touches the input
$('input[type=text]').on('touchstart', function(){

    //only fire code if it's an iOS device
    if (is_iOS()){

        //set savedScrollPos to the current scroll position
        savedScrollPos = scrollPos;

        //shift the body up a number of pixels equal to the current scroll position
        $('body').css({
            position: 'relative',
            top: -scrollPos
        });

        //Hide all content outside of the top of the visible area
        //this essentially chops off the body at the position you are scrolled to so the browser can't scroll up any higher
        $('html').css('overflow','hidden');
    }
})

//when the user is done and removes focus from the input field
.blur(function(){

    //checks if it is an iOS device
    if (is_iOS()){

        //Removes the custom styling from the body and html attribute
        $('body, html').removeAttr('style');

        //instantly scrolls the page back down to where you were when you clicked on input field
        $(document).scrollTop(savedScrollPos);
    }
});

+1。自明ではないDOM階層がある場合、これは受け入れられた回答よりもはるかに複雑な修正ではありません。これはより多くの賛成投票が必要です
Anson Kao

これをネイティブJSでも提供できますか?本当にありがとう!
mesqueeb 2017

@SamSaffron、この答えは本当にあなたのために働いていますか?ここにいくつかの例がありますか?それは私のために働いた?
Ganesh Putta 2018

@SamSaffron、この答えは本当にあなたの問題を解決しましたか、あなたはあなたのために働いたいくつかの例を送ることができます、同じことに取り組んでいますが、それは私のために働いた
Ganesh Putta 2018

@GaneshPutta最近のiOSの更新により、これが機能しなくなった可能性があります。これを2.5年前に投稿しました。それは仕事まだはずしかし、あなたは正確にすべての命令に続く場合:/
ダニエルTononを

4

必要な選択要素にイベントリスナーを追加し、問題の選択がフォーカスを取得したときに1ピクセルのオフセットでスクロールすることで、選択入力のこれを修正できました。

これは必ずしも良い解決策ではありませんが、ここで見た他の回答よりもはるかに単純で信頼性が高くなります。ブラウザは位置を再レンダリング/再計算するようです:修正済み。window.scrollBy()関数で提供されるオフセットに基づく属性。

document.querySelector(".someSelect select").on("focus", function() {window.scrollBy(0, 1)});

2

Mark Ryan Salleeが示唆したように、背景要素の高さとオーバーフローを動的に変更することが重要であることに気づきました。これにより、Safariにスクロールする必要がなくなります。

したがって、モーダルの開始アニメーションが終了したら、背景のスタイルを変更します。

$('body > #your-background-element').css({
  'overflow': 'hidden',
  'height': 0
});

モーダルを閉じたら、元に戻します。

$('body > #your-background-element').css({
  'overflow': 'auto',
  'height': 'auto'
});

他の答えはより単純な状況では有用ですが、私のDOMは複雑すぎ(SharePointに感謝)、絶対/固定位置スワップを使用できません。


1

きれいに?番号。

私は最近、スティッキーヘッダーの固定検索フィールドでこの問題を抱えていました。現時点でできる最善の方法は、常にスクロール位置を変数に保持し、選択時に固定要素の位置を上部で固定するのではなく絶対位置にすることです。ドキュメントのスクロール位置に基づく位置。

ただし、これは非常に醜く、適切な場所に着陸する前に奇妙な前後のスクロールが発生しますが、これは私が得ることができる最も近いものです。

その他のソリューションでは、ブラウザのデフォルトのスクロールメカニズムをオーバーライドする必要があります。


1

これはiOS 10.3で修正されました!

ハックはもう必要ないはずです。


1
これが修正されたと指摘するリリースノートを指摘できますか?
bluepnume 2017

Appleは非常に秘密主義です、彼らは:)すべて私が得ていること、私はそれが今、適切に動作確認した私のバグレポートを閉じ
サムサフラン

私はまだiOS 11でこの問題を抱えています
zekia

0

この特定のバグには対処していませんが、オーバーフローが発生している可能性があります。テキスト領域が表示されているとき(またはデザインによってはアクティブなとき)に本文に表示されます。これは、ブラウザをスクロールするための「下」の場所に与えないという効果があるかもしれません。


1
そのハックを考えるほど早い段階でタッチスタートをトリガーするようにも思えない:(
Sam Saffron

0

可能な解決策は、入力フィールドを置き換えることです。

  • divのクリックイベントを監視する
  • 非表示の入力フィールドにフォーカスしてキーボードをレンダリングする
  • 非表示の入力フィールドの内容を偽の入力フィールドに複製する

function focus() {
  $('#hiddeninput').focus();
}

$(document.body).load(focus);

$('.fakeinput').bind("click",function() {
    focus();
});

$("#hiddeninput").bind("keyup blur", function (){
  $('.fakeinput .placeholder').html(this.value);
});
#hiddeninput {
  position:fixed;
  top:0;left:-100vw;
  opacity:0;
  height:0px;
  width:0;
}
#hiddeninput:focus{
  outline:none;
}
.fakeinput {
  width:80vw;
  margin:15px auto;
  height:38px;
  border:1px solid #000;
  color:#000;
  font-size:18px;
  padding:12px 15px 10px;
  display:block;
  overflow:hidden;
}
.placeholder {
  opacity:0.6;
  vertical-align:middle;
}
<input type="text" id="hiddeninput"></input>

<div class="fakeinput">
    <span class="placeholder">First Name</span>
</div> 


コードペン


0

私のDOMは複雑で、動的な無限スクロールページがあるため、これらのソリューションはどれもうまくいきませんでした。

背景:固定ヘッダーと、ユーザーがその下までスクロールするとその下にくる要素を使用しています。この要素には検索入力フィールドがあります。さらに、順方向および逆方向のスクロール中に動的ページが追加されました。

問題: iOSでは、ユーザーがfixed要素の入力をクリックするたびに、ブラウザーがページの一番上までスクロールしていました。これにより、望ましくない動作が発生するだけでなく、ページの上部に動的ページが追加されました。

期待される解決策:ユーザーがsticky要素の入力をクリックしても、iOSではスクロールしない(まったくスクロールしない)。

解決:

     /*Returns a function, that, as long as it continues to be invoked, will not
    be triggered. The function will be called after it stops being called for
    N milliseconds. If `immediate` is passed, trigger the function on the
    leading edge, instead of the trailing.*/
    function debounce(func, wait, immediate) {
        var timeout;
        return function () {
            var context = this, args = arguments;
            var later = function () {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };

     function is_iOS() {
        var iDevices = [
          'iPad Simulator',
          'iPhone Simulator',
          'iPod Simulator',
          'iPad',
          'iPhone',
          'iPod'
        ];
        while (iDevices.length) {
            if (navigator.platform === iDevices.pop()) { return true; }
        }
        return false;
    }

    $(document).on("scrollstop", debounce(function () {
        //console.log("Stopped scrolling!");
        if (is_iOS()) {
            var yScrollPos = $(document).scrollTop();
            if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px)
                $('#searchBarDiv').css('position', 'absolute');
                $('#searchBarDiv').css('top', yScrollPos + 50 + 'px'); //50 for fixed header
            }
            else {
                $('#searchBarDiv').css('position', 'inherit');
            }
        }
    },250,true));

    $(document).on("scrollstart", debounce(function () {
        //console.log("Started scrolling!");
        if (is_iOS()) {
            var yScrollPos = $(document).scrollTop();
            if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px)
                $('#searchBarDiv').css('position', 'fixed');
                $('#searchBarDiv').css('width', '100%');
                $('#searchBarDiv').css('top', '50px'); //50 for fixed header
            }
        }
    },250,true));

要件: startsroll関数とstopscroll関数が機能するには、jQueryモバイルが必要です。

デバウンスは、粘着性の要素によって作成された遅延を滑らかにするために含まれています。

iOS10でテスト済み。


0

私は昨日、#bが表示されているときに#aの高さを最大表示高さ(私の場合は身長)に設定して、このようなものを飛び越えました

例:

    <script>
    document.querySelector('#b').addEventListener('focus', function () {
      document.querySelector('#a').style.height = document.body.clientHeight;
    })
    </script>

ps:最近の例では申し訳ありませんが、必要だったことに気づきました。


14
修正がどのように役立つかを明確にするコード例を含めてください
roo2

@EruPenkmanごめんなさい、コメントに気づきました。お役に立てば幸いです。
Onur Uyar 2017年

0

私は問題を抱えていました、以下のコード行が私のためにそれを解決しました-

html{

 overflow: scroll; 
-webkit-overflow-scrolling: touch;

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.